mirror of
https://github.com/wwiinnddyy/LanMountainDesktop.git
synced 2026-06-20 23:54:26 +08:00
Replace UI DispatcherTimer polling with a StudySnapshotRenderGate across multiple widgets to queue and apply only the latest analytics snapshot; components updated include StudyDeductionReasonsWidget, StudyEnvironmentWidget, StudyInterruptDensityWidget, StudyNoiseCurveWidget. Add StudySnapshotRenderGate implementation to coordinate rendering and monitoring leases and update subscription/lease lifecycle handling (subscribe/unsubscribe, Acquire/Dispose leases, Clear/Dispose gate). Rewrite chart controls (StudyNoiseCurveChartControl and StudyNoiseDistributionScatterChartControl) to use stable logical-time origins, split series into static vs dynamic tails, add geometry/sample caching, stable jitter/coordinate mapping helpers, and expose internal helpers & counts for testing. Add unit tests (StudyComponentRenderingTests) covering the render gate and chart behaviors (layer counts, logical X mapping, stable jitter, cache rebuild). These changes improve rendering correctness and performance by avoiding redundant renders and enabling deterministic chart layout.
280 lines
10 KiB
YAML
280 lines
10 KiB
YAML
name: DDSS
|
|
|
|
on:
|
|
workflow_run:
|
|
workflows:
|
|
- PLONDS
|
|
types:
|
|
- completed
|
|
workflow_dispatch:
|
|
inputs:
|
|
tag:
|
|
description: 'Release tag'
|
|
required: true
|
|
type: string
|
|
|
|
env:
|
|
DOTNET_VERSION: '10.0.x'
|
|
|
|
jobs:
|
|
publish:
|
|
if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' }}
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: write
|
|
actions: read
|
|
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
submodules: recursive
|
|
|
|
- name: Resolve release tag
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
|
|
RAW_TAG="${{ github.event.inputs.tag }}"
|
|
if [[ "$RAW_TAG" == v* ]]; then
|
|
TAG="$RAW_TAG"
|
|
else
|
|
TAG="v$RAW_TAG"
|
|
fi
|
|
else
|
|
gh run download "${{ github.event.workflow_run.id }}" -n plonds-run-metadata -D plonds-run-metadata
|
|
TAG="$(tr -d '\r\n' < plonds-run-metadata/tag.txt)"
|
|
fi
|
|
|
|
echo "RELEASE_TAG=${TAG}" >> "$GITHUB_ENV"
|
|
PUBLIC_BASE="${{ vars.S3_PUBLIC_BASE_URL }}"
|
|
if [[ -z "$PUBLIC_BASE" ]]; then
|
|
PUBLIC_BASE="https://cn-nb1.rains3.com/lmdesktop/lanmountain/update"
|
|
fi
|
|
PUBLIC_BASE="${PUBLIC_BASE%/}"
|
|
echo "S3_PUBLIC_BASE_URL=${PUBLIC_BASE}" >> "$GITHUB_ENV"
|
|
echo "S3_BASE_URL=${PUBLIC_BASE}/releases/${TAG}/assets" >> "$GITHUB_ENV"
|
|
|
|
- name: Setup .NET
|
|
uses: actions/setup-dotnet@v4
|
|
with:
|
|
dotnet-version: ${{ env.DOTNET_VERSION }}
|
|
dotnet-quality: preview
|
|
|
|
- name: Prepare signing key
|
|
env:
|
|
UPDATE_PRIVATE_KEY_PEM: ${{ secrets.UPDATE_PRIVATE_KEY_PEM }}
|
|
PLONDS_SIGNING_KEY: ${{ secrets.PLONDS_SIGNING_KEY }}
|
|
PDC_SIGNING_KEY: ${{ secrets.PDC_SIGNING_KEY }}
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
KEY="${PLONDS_SIGNING_KEY:-}"
|
|
if [[ -z "$KEY" ]]; then KEY="${UPDATE_PRIVATE_KEY_PEM:-}"; fi
|
|
if [[ -z "$KEY" ]]; then KEY="${PDC_SIGNING_KEY:-}"; fi
|
|
if [[ -z "$KEY" ]]; then
|
|
echo "No signing key is configured."
|
|
exit 1
|
|
fi
|
|
printf '%s' "$KEY" > update-private-key.pem
|
|
echo "UPDATE_PRIVATE_KEY_PATH=$PWD/update-private-key.pem" >> "$GITHUB_ENV"
|
|
|
|
- name: Build PLONDS tool
|
|
run: dotnet build PenguinLogisticsOnlineNetworkDistributionSystem/src/Plonds.Tool/Plonds.Tool.csproj -c Release
|
|
|
|
- name: Download release assets
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
mkdir -p release-assets
|
|
gh release download "$RELEASE_TAG" -D release-assets
|
|
find release-assets -maxdepth 1 -type f | sort
|
|
|
|
- name: Prepare PLONDS static output
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
rm -rf plonds-static
|
|
mkdir -p plonds-static
|
|
if [[ "${{ github.event_name }}" == "workflow_run" ]]; then
|
|
gh run download "${{ github.event.workflow_run.id }}" -n plonds-static -D plonds-static || true
|
|
fi
|
|
if [[ ! -d plonds-static/repo/sha256 && -f release-assets/plonds-static.zip ]]; then
|
|
unzip -q release-assets/plonds-static.zip -d plonds-static
|
|
fi
|
|
if [[ ! -d plonds-static/repo/sha256 || ! -d plonds-static/meta/channels || ! -d plonds-static/manifests ]]; then
|
|
echo "PLONDS static output is missing. Run the PLONDS workflow for this release first."
|
|
exit 1
|
|
fi
|
|
|
|
- name: Upload release assets to Rainyun S3
|
|
env:
|
|
AWS_ACCESS_KEY_ID: ${{ secrets.S3_ACCESS_KEY }}
|
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.S3_SECRET_KEY }}
|
|
AWS_DEFAULT_REGION: ${{ vars.S3_REGION }}
|
|
AWS_REGION: ${{ vars.S3_REGION }}
|
|
S3_ENDPOINT: ${{ vars.S3_ENDPOINT }}
|
|
S3_BUCKET: ${{ vars.S3_BUCKET }}
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
aws --version
|
|
for file in release-assets/*; do
|
|
[[ -f "$file" ]] || continue
|
|
name="$(basename "$file")"
|
|
if [[ "$name" == "ddss.json" || "$name" == "ddss.json.sig" ]]; then
|
|
continue
|
|
fi
|
|
key="lanmountain/update/releases/${RELEASE_TAG}/assets/${name}"
|
|
sha256="$(sha256sum "$file" | awk '{print $1}')"
|
|
existing_sha="$(aws --endpoint-url "$S3_ENDPOINT" --region "$AWS_REGION" s3api head-object --bucket "$S3_BUCKET" --key "$key" --query 'Metadata.sha256' --output text 2>/dev/null || true)"
|
|
if [[ "$existing_sha" == "$sha256" ]]; then
|
|
echo "Skip existing asset: $name"
|
|
continue
|
|
fi
|
|
aws --endpoint-url "$S3_ENDPOINT" --region "$AWS_REGION" s3api put-object \
|
|
--bucket "$S3_BUCKET" \
|
|
--key "$key" \
|
|
--body "$file" \
|
|
--metadata "sha256=$sha256"
|
|
done
|
|
|
|
- name: Upload PLONDS static output to Rainyun S3
|
|
env:
|
|
AWS_ACCESS_KEY_ID: ${{ secrets.S3_ACCESS_KEY }}
|
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.S3_SECRET_KEY }}
|
|
AWS_DEFAULT_REGION: ${{ vars.S3_REGION }}
|
|
AWS_REGION: ${{ vars.S3_REGION }}
|
|
S3_ENDPOINT: ${{ vars.S3_ENDPOINT }}
|
|
S3_BUCKET: ${{ vars.S3_BUCKET }}
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
aws --endpoint-url "$S3_ENDPOINT" --region "$AWS_REGION" s3 sync \
|
|
plonds-static/ \
|
|
"s3://$S3_BUCKET/lanmountain/update/" \
|
|
--only-show-errors
|
|
|
|
- name: Mirror installers to Rainyun S3
|
|
env:
|
|
AWS_ACCESS_KEY_ID: ${{ secrets.S3_ACCESS_KEY }}
|
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.S3_SECRET_KEY }}
|
|
AWS_DEFAULT_REGION: ${{ vars.S3_REGION }}
|
|
AWS_REGION: ${{ vars.S3_REGION }}
|
|
S3_ENDPOINT: ${{ vars.S3_ENDPOINT }}
|
|
S3_BUCKET: ${{ vars.S3_BUCKET }}
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
version="${RELEASE_TAG#v}"
|
|
for file in release-assets/*; do
|
|
[[ -f "$file" ]] || continue
|
|
name="$(basename "$file")"
|
|
platform=""
|
|
case "$name" in
|
|
*.exe)
|
|
if [[ "$name" == *x86* ]]; then platform="windows-x86"; else platform="windows-x64"; fi
|
|
;;
|
|
*.deb)
|
|
platform="linux-x64"
|
|
;;
|
|
*.dmg)
|
|
if [[ "$name" == *arm64* ]]; then platform="macos-arm64"; else platform="macos-x64"; fi
|
|
;;
|
|
esac
|
|
[[ -n "$platform" ]] || continue
|
|
key="lanmountain/update/installers/${platform}/${version}/${name}"
|
|
sha256="$(sha256sum "$file" | awk '{print $1}')"
|
|
aws --endpoint-url "$S3_ENDPOINT" --region "$AWS_REGION" s3api put-object \
|
|
--bucket "$S3_BUCKET" \
|
|
--key "$key" \
|
|
--body "$file" \
|
|
--metadata "sha256=$sha256"
|
|
done
|
|
|
|
- name: Build DDSS manifest
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
mkdir -p ddss-output
|
|
dotnet run --project PenguinLogisticsOnlineNetworkDistributionSystem/src/Plonds.Tool/Plonds.Tool.csproj --configuration Release -- \
|
|
build-ddss \
|
|
--release-tag "$RELEASE_TAG" \
|
|
--assets-dir release-assets \
|
|
--output-dir ddss-output \
|
|
--private-key "$UPDATE_PRIVATE_KEY_PATH" \
|
|
--repository "${{ github.repository }}" \
|
|
--s3-base-url "$S3_BASE_URL"
|
|
|
|
- name: Upload DDSS manifest to release
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
gh release upload "$RELEASE_TAG" ddss-output/ddss.json ddss-output/ddss.json.sig --clobber
|
|
|
|
- name: Upload DDSS manifest to Rainyun S3
|
|
env:
|
|
AWS_ACCESS_KEY_ID: ${{ secrets.S3_ACCESS_KEY }}
|
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.S3_SECRET_KEY }}
|
|
AWS_DEFAULT_REGION: ${{ vars.S3_REGION }}
|
|
AWS_REGION: ${{ vars.S3_REGION }}
|
|
S3_ENDPOINT: ${{ vars.S3_ENDPOINT }}
|
|
S3_BUCKET: ${{ vars.S3_BUCKET }}
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
for file in ddss-output/ddss.json ddss-output/ddss.json.sig; do
|
|
name="$(basename "$file")"
|
|
key="lanmountain/update/releases/${RELEASE_TAG}/assets/${name}"
|
|
sha256="$(sha256sum "$file" | awk '{print $1}')"
|
|
aws --endpoint-url "$S3_ENDPOINT" --region "$AWS_REGION" s3api put-object \
|
|
--bucket "$S3_BUCKET" \
|
|
--key "$key" \
|
|
--body "$file" \
|
|
--metadata "sha256=$sha256"
|
|
done
|
|
|
|
- name: Verify Rainyun S3 PLONDS output
|
|
env:
|
|
AWS_ACCESS_KEY_ID: ${{ secrets.S3_ACCESS_KEY }}
|
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.S3_SECRET_KEY }}
|
|
AWS_DEFAULT_REGION: ${{ vars.S3_REGION }}
|
|
AWS_REGION: ${{ vars.S3_REGION }}
|
|
S3_ENDPOINT: ${{ vars.S3_ENDPOINT }}
|
|
S3_BUCKET: ${{ vars.S3_BUCKET }}
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
mapfile -t required < <(
|
|
{
|
|
find plonds-static/meta/channels -path '*/latest.json' -type f | sort | head -n 1
|
|
find plonds-static/meta/distributions -name '*.json' -type f | sort | head -n 1
|
|
find plonds-static/manifests -name 'plonds-filemap.json' -type f | sort | head -n 1
|
|
find plonds-static/manifests -name 'plonds-filemap.json.sig' -type f | sort | head -n 1
|
|
find plonds-static/repo/sha256 -type f | sort | head -n 1
|
|
} | sed '/^$/d'
|
|
)
|
|
|
|
if [[ "${#required[@]}" -lt 5 ]]; then
|
|
echo "Not enough PLONDS static files to verify."
|
|
exit 1
|
|
fi
|
|
|
|
for path in "${required[@]}"; do
|
|
rel="${path#plonds-static/}"
|
|
key="lanmountain/update/${rel}"
|
|
aws --endpoint-url "$S3_ENDPOINT" --region "$AWS_REGION" s3api head-object \
|
|
--bucket "$S3_BUCKET" \
|
|
--key "$key" >/dev/null
|
|
curl -fsSI "$S3_PUBLIC_BASE_URL/$rel" >/dev/null
|
|
done
|