mirror of
https://github.com/nexu-io/open-design.git
synced 2026-09-20 06:15:06 +08:00
a07dc736fd
* perf(ci): make prerelease mac Intel opt-in build_mac_intel is the prerelease critical path: ~33 min on the macos-15-intel fleet against ~21 min for mac arm64 and Windows, which stretches every release-branch push to ~56 min end to end. Gate it behind a new `enable_mac_x64` input that defaults to false, the same shape build_linux already uses, and let publish tolerate a skipped Intel job. Push-triggered prereleases now finish in ~21 min; a manual dispatch can still turn Intel back on. The stable promotion gate is unchanged and still hard-requires platforms.macIntel in the promoted prerelease metadata, so an Intel-less prerelease fails promotion loudly rather than shipping unvalidated. * perf(ci): stop the prerelease pipeline waiting on work it does not need Run 34102146769 took 56m, 42m of which survived turning macOS Intel off. Four things in that 42m were wall clock nobody was buying anything with. Tests no longer sit in front of packaging. functional_e2e, e2e_vitest, daemon_unit_tests and verify take ~11 min; packaging takes ~21 min, and the build jobs needed all four before they could start. They now need only metadata and run alongside the tests. Nothing about the safety of a release changes: `publish` is what writes metadata.json, and it already required all four gates to be `success` — a red suite still cannot ship. What it costs is CI minutes, since a failing gate now stops builds that have already run. The Feishu card stops cloning the repository to read one `git log`. That checkout was `fetch-depth: 0` against a 2.2 GiB pack and took 4m09s to produce a `PREV..CUR` commit list. The list now comes from the compare API, reproducing `git log --no-merges --pretty=format:'%s (%h)'` line for line — verified against 35 commits of real history, where the only difference was a leading space that feishu.ts trims anyway. Every fallback the old script had (no baseline, unreachable commit) still produces an empty changelog rather than a failed notification. Packaged smoke and Apple notarization become opt-in, like Intel already is. Smoke is advisory — `continue-on-error`, so it has never blocked publication — and costs ~2.5 min on the Windows critical path. Notarization is a round-trip to Apple that no prerelease consumer needs. Signing is NOT optional and stays on: prepare-stable.ts requires platforms.mac.signed == true, so an unsigned prerelease could never be promoted. `mac_sign_mode: no` is offered for parity with beta and now correctly reports signed=false rather than lying in the metadata. Windows drops setup-node's pnpm store cache and every job checks out shallow. Measured across four prerelease runs the store cache missed 3 times out of 4, costing 100s/66s/43s to save with no install speedup, and on its one hit traded a 32s restore for a 36s faster install — while competing for the 10 GB cache budget against tools-pack caches worth ~11 min each. Nothing in this workflow reads git history. * perf(ci): let a prerelease ship on the build's schedule, not the tests' prerelease is the internal channel. Its job is to put a package in a colleague's hands so a human can test it; automated CI is a second, parallel opinion about the same commit. Wiring the two in series meant a single flaky suite cost the team a whole day's package — the expensive failure, and the one this pipeline kept producing. The four test jobs (functional_e2e, e2e_vitest, daemon_unit_tests, verify) now gate nothing. They are absent from `build_*` and from `publish`. They still run on every prerelease and their results leave the workflow as outputs — functional_e2e_result and friends, plus a tests_all_green / failed_test_jobs rollup — so the notification layer can raise the alarm without holding the package hostage. Stable is untouched: its promotion gate reads the prerelease metadata and runs its own checks. The packaged smoke suites move for the same reason. They were already `continue-on-error`, so a smoke failure never failed the job — but they sat between the build and the R2 upload, which meant the package waited on them and a hung smoke could still eat the job. Every smoke now runs AFTER "Publish <platform> platform" and "Upload <platform> publish manifest", and every step after that manifest upload is continue-on-error, so nothing post-delivery can fail a build job and retroactively block publish. The results travel through a `test_signals` job rather than the `jobs.<job_id>.result` expression GitHub documents for workflow_call outputs, because actionlint has rejected that property since 2023 (rhysd/actionlint#343) and ci.yml runs actionlint at a pinned version. One consequence to hand to the notification work: a failed advisory job now makes the overall workflow conclusion red even though the package shipped, so `needs.build.result` is no longer the right thing to colour a card by. `version_metadata_url` is the authoritative "did a package ship?" signal and is documented as such. * perf(ci): move prerelease validation out of the pipeline it validates release-prerelease.yml holds one repository-wide concurrency group with cancel-in-progress: false, so any job that outlives `publish` keeps that group held and stops the NEXT prerelease from starting. Making the test jobs advisory did not fix that: they still held the group for 45 minutes after the package had shipped. The pipeline is now metadata -> build_* -> publish plus two dispatcher jobs that nothing depends on. The four code suites, the packaged mac/Windows smoke, and the release card each run in a workflow of their own, keyed on the origin run. The packaged smoke no longer installs electron-builder's output directory on the machine that produced it. It reads the published version metadata, downloads the DMG / setup.exe out of R2, verifies its sha256 sidecar, and writes it to the single path `tools-pack <platform> install` reads - which is why the specs need no changes. That is both a better test and what lets the smoke live on a different runner at all. The release card becomes progressive. A Feishu application bot can PATCH a message it sent, so one card is created when the first platform lands in R2 and edited in place as the rest of the platforms, the code tests, and the smoke report in. A PATCH replaces the whole card, so exactly one job may write it: the watcher owns the message and polls the three runs. The card is withheld until a package exists, because a "building..." card posted at dispatch time becomes a permanent lie when a build dies - with total build failure as the one exception, where silence would hide an incident. macOS Intel goes back to on-by-default: stable ships Intel and stable's gate is a validated prerelease artifact, so an Intel-less prerelease cannot be promoted at all. The switch stays, forwarded as `github.event_name != 'workflow_dispatch' || inputs.enable_mac_x64` - `inputs.enable_mac_x64 || true` would be unturnoffable, because a dispatch with the box unchecked also yields true. Also fixes the signal batch 2 left wrong: a reusable workflow reports failure if any job inside it failed, including work that runs after publication, so `needs.build.result` painted the download card red while its own buttons worked. `version_metadata_url` is non-empty only when publish wrote metadata, and now drives both the header colour and the smoke annotations. Two topology tests were already red on this branch - batch 2 changed the workflow without updating them. Both now assert the new invariant. The existing webhook card stays until the new one has carried a couple of real releases; a gap in release notification is worse than a duplicate.
155 lines
5.7 KiB
YAML
155 lines
5.7 KiB
YAML
name: release-prerelease-tests
|
|
|
|
# The code test suites for one prerelease build, run OUTSIDE the pipeline that
|
|
# produced it.
|
|
#
|
|
# These four jobs used to live in release-prerelease.yml. They never gated
|
|
# delivery there — build and publish deliberately ignore them — but they still
|
|
# held the workflow open, and release-prerelease.yml's concurrency group is one
|
|
# repository-wide string with cancel-in-progress: false. So a 45-minute test
|
|
# suite did not delay one prerelease, it delayed the NEXT one from starting.
|
|
# Moving them here removes that coupling entirely: this workflow has a
|
|
# concurrency group scoped to the origin run, so two prereleases can be
|
|
# validated at once and neither can queue a build.
|
|
#
|
|
# Nothing dispatches this on a schedule or a push. release-prerelease.yml's
|
|
# `dispatch_validation` job POSTs it the moment the build commit is resolved,
|
|
# via .github/scripts/release/dispatch-validation.sh. Results reach a human
|
|
# through the progressive Feishu card (release-prerelease-card.yml), which
|
|
# watches this run's jobs; they are not workflow outputs, because nothing in the
|
|
# release path is allowed to consume them as a gate.
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
commit:
|
|
description: "Exact commit SHA to test. This is the commit release-prerelease actually built, not a branch head."
|
|
required: true
|
|
type: string
|
|
version:
|
|
description: "Prerelease version these suites are validating, e.g. 0.21.1-prerelease.3. Display only."
|
|
required: false
|
|
type: string
|
|
default: ""
|
|
origin_run_id:
|
|
description: "Run id of the release-prerelease pipeline that dispatched this. Carried in the run name so the Feishu card and a human can walk between the two."
|
|
required: false
|
|
type: string
|
|
default: ""
|
|
|
|
run-name: prerelease tests · ${{ inputs.version || inputs.commit }} · origin-run ${{ inputs.origin_run_id }}
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
concurrency:
|
|
# Scoped to the origin run, never repository-wide: this workflow must not be
|
|
# able to queue behind — or ahead of — anything. Two prereleases in flight get
|
|
# two independent test runs.
|
|
group: release-prerelease-tests-${{ inputs.origin_run_id || inputs.commit }}
|
|
cancel-in-progress: false
|
|
|
|
jobs:
|
|
functional_e2e:
|
|
name: P0 Functional E2E
|
|
uses: ./.github/workflows/ui-extended-main.yml
|
|
with:
|
|
ref: ${{ inputs.commit }}
|
|
suite: p0
|
|
e2e_vitest:
|
|
name: E2E Vitest
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 20
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v6.0.2
|
|
with:
|
|
ref: ${{ inputs.commit }}
|
|
- name: Setup pnpm
|
|
uses: pnpm/action-setup@v5
|
|
with:
|
|
version: 10.33.2
|
|
- name: Setup Node.js
|
|
uses: actions/setup-node@v6
|
|
with:
|
|
node-version: 24
|
|
- name: Install dependencies
|
|
run: pnpm install --frozen-lockfile
|
|
- name: Setup Playwright
|
|
run: pnpm -C e2e exec playwright install --with-deps chromium
|
|
# Ubuntu 24.04 restricts unprivileged user namespaces through AppArmor.
|
|
# The daemon launches the installed Chromium directly instead of through
|
|
# Playwright. Restore user namespaces on this ephemeral runner so the real
|
|
# Chromium sandbox remains enabled rather than disabling that protection.
|
|
- name: Allow Chromium user namespaces on GitHub Ubuntu
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
if sysctl kernel.apparmor_restrict_unprivileged_userns >/dev/null 2>&1; then
|
|
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
|
|
fi
|
|
- name: Prebuild workspace type declarations
|
|
run: |
|
|
pnpm --filter @open-design/daemon build
|
|
pnpm --filter @open-design/desktop build
|
|
pnpm --filter @open-design/web build:sidecar
|
|
- name: Run E2E Vitest
|
|
run: pnpm --filter @open-design/e2e test
|
|
daemon_unit_tests:
|
|
name: Daemon tests (${{ matrix.shard }}/4)
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 15
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
shard: [1, 2, 3, 4]
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v6.0.2
|
|
with:
|
|
ref: ${{ inputs.commit }}
|
|
- name: Setup pnpm
|
|
uses: pnpm/action-setup@v5
|
|
with:
|
|
version: 10.33.2
|
|
- name: Setup Node.js
|
|
uses: actions/setup-node@v6
|
|
with:
|
|
node-version: 24
|
|
- name: Install dependencies
|
|
run: pnpm install --frozen-lockfile
|
|
- name: Run daemon test shard
|
|
env:
|
|
OD_WATCHER_TEST_DEBUG: "1"
|
|
run: pnpm --filter @open-design/daemon test --shard=${{ matrix.shard }}/4
|
|
verify:
|
|
name: Verify build (typecheck + tests)
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v6.0.2
|
|
with:
|
|
ref: ${{ inputs.commit }}
|
|
# Shallow: nothing here reads git history. `pnpm guard` uses
|
|
# `git ls-files` (the index, not history), and a full clone of this
|
|
# repository is a 2.2 GiB pack.
|
|
fetch-depth: 1
|
|
- name: Setup pnpm
|
|
uses: pnpm/action-setup@v5
|
|
with:
|
|
version: 10.33.2
|
|
- name: Setup Node.js
|
|
uses: actions/setup-node@v6
|
|
with:
|
|
node-version: 24
|
|
- name: Install dependencies
|
|
run: pnpm install --frozen-lockfile
|
|
- name: Build daemon and desktop (typecheck dependencies)
|
|
run: |
|
|
pnpm --filter @open-design/daemon build
|
|
pnpm --filter @open-design/desktop build
|
|
- name: Typecheck workspaces
|
|
run: pnpm -r --workspace-concurrency=4 --if-present run typecheck
|
|
- name: Check repository layout policies
|
|
run: pnpm guard
|