Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .github/workflows/create-release.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
name: Build Installers
# The `workflows: ["Build Installers"]` filter in test-smokes-built.yml matches the
# `name:` above, NOT this run-name. The [skip-auto-smoke] suffix is how that workflow
# reads this dispatch input, since the Runs API does not expose workflow_dispatch
# inputs on a completed run. Do not reword it casually -- see D7.1 in
# llm-docs/built-version-testing-architecture.md.
run-name: "Build Installers${{ inputs.skip-auto-smoke && ' [skip-auto-smoke]' || '' }}"
on:
schedule:
- cron: "0 4 * * *"
Expand Down Expand Up @@ -29,6 +35,11 @@ on:
required: false
type: boolean
default: false
skip-auto-smoke:
description: "Skip the built-version smoke suite that test-smokes-built.yml normally auto-triggers via workflow_run on this run's completion. Build still runs normally."
required: false
type: boolean
default: false

env:
NFPM_VERSION: "2.43.1"
Expand Down
32 changes: 22 additions & 10 deletions .github/workflows/test-smokes-built.yml
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,10 @@ jobs:
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RUN_ID_INPUT: ${{ github.event_name == 'workflow_run' && github.event.workflow_run.id || github.event.inputs.run-id }}
# create-release.yml encodes its skip-auto-smoke dispatch input in its run-name,
# which the completed workflow_run payload carries as display_title (D7.1).
# Deliberately false for a manual dispatch: an explicit run-id still gets tested.
SKIP_AUTO_SMOKE: ${{ github.event_name == 'workflow_run' && contains(github.event.workflow_run.display_title, '[skip-auto-smoke]') }}
run: |
run_id="$RUN_ID_INPUT"
if [ -z "$run_id" ]; then
Expand All @@ -230,16 +234,24 @@ jobs:
echo "::error::Could not resolve the head SHA for create-release run ${run_id}"
exit 1
fi
# Run only legs whose artifacts still exist.
names="$(gh api "repos/${GITHUB_REPOSITORY}/actions/runs/${run_id}/artifacts" \
--paginate --jq '.artifacts[] | select(.expired | not) | .name')"
has() { grep -Fxq "$1" <<<"$names" && echo true || echo false; }
has_linux="$(has 'Deb Zip')"
has_windows="$(has 'Windows Zip')"
has_mac="$(has 'Mac Zip')"
if [ "$has_linux" = false ] && [ "$has_windows" = false ] && [ "$has_mac" = false ]; then
echo "::error::run ${run_id} produced none of the expected artifacts (Deb Zip / Windows Zip / Mac Zip)"
exit 1
# A create-release dispatch that set skip-auto-smoke short-circuits every nightly leg.
if [ "$SKIP_AUTO_SMOKE" = true ]; then
echo "create-release run ${run_id} was dispatched with skip-auto-smoke; skipping all nightly legs"
has_linux=false
has_windows=false
has_mac=false
else
# Run only legs whose artifacts still exist.
names="$(gh api "repos/${GITHUB_REPOSITORY}/actions/runs/${run_id}/artifacts" \
--paginate --jq '.artifacts[] | select(.expired | not) | .name')"
has() { grep -Fxq "$1" <<<"$names" && echo true || echo false; }
has_linux="$(has 'Deb Zip')"
has_windows="$(has 'Windows Zip')"
has_mac="$(has 'Mac Zip')"
if [ "$has_linux" = false ] && [ "$has_windows" = false ] && [ "$has_mac" = false ]; then
echo "::error::run ${run_id} produced none of the expected artifacts (Deb Zip / Windows Zip / Mac Zip)"
exit 1
fi
fi
echo "Using create-release run ${run_id} at commit ${sha} (linux=${has_linux} windows=${has_windows} mac=${has_mac})"
echo "run-id=$run_id" >> "$GITHUB_OUTPUT"
Expand Down
24 changes: 21 additions & 3 deletions llm-docs/built-version-testing-architecture.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
main_commit: e5850df75
analyzed_date: 2026-09-10
main_commit: 76d3a1e5f
analyzed_date: 2026-09-18
key_files:
- tests/quarto-cmd.ts
- tests/test.ts
Expand Down Expand Up @@ -214,6 +214,8 @@ The release pipeline is not modified for testing purposes.
**Known weaknesses:** the trigger depends on the workflow display name (`workflows: ["Build Installers"]`), so renaming the workflow stops the trigger. GitHub does not report a missing trigger as a failure.
The trigger fires after every completed create-release run, including manual and partial builds. Each OS leg therefore checks that its artifact exists.

A dispatch can opt out per-run via `skip-auto-smoke` (see D7.1) when it does not need the downstream suite.

**Revisit when:** maintainers want a single nightly build-and-test status and are willing to couple the workflows.

### D2. Version marker: semver *build metadata* (`X.Y.Z+test.YYYYMMDD`)
Expand Down Expand Up @@ -253,6 +255,22 @@ For a branch build, dispatch create-release with `publish-release=false` and `sm
The configure job rejects this mode when publishing is enabled, and partial builds use a per-run concurrency group.
The daily path still uses the full build because the Mac zip provides macOS smoke coverage.

### D7.1 `skip-auto-smoke` opts a dispatch out of the auto-triggered suite

`create-release.yml`'s `workflow_dispatch` has a `skip-auto-smoke` boolean input (default `false`). The Runs API does not expose `workflow_dispatch` inputs on a completed run (verified live via `gh api repos/quarto-dev/quarto-cli/actions/runs/RUNID` — no `inputs` field), so `test-smokes-built.yml`'s `resolve-nightly` job cannot read the flag off `github.event.workflow_run` directly.

**Mechanism: the run title.** `create-release.yml` sets `run-name: "Build Installers${{ inputs.skip-auto-smoke && ' [skip-auto-smoke]' || '' }}"`. GitHub evaluates `run-name` from the dispatch inputs when the run is created, and the `completed` `workflow_run` payload carries the result as `github.event.workflow_run.display_title`. `resolve-nightly` reads it through a `SKIP_AUTO_SMOKE` env expression — no extra API call — and forces `has-linux`/`has-windows`/`has-mac` to `false`. Every nightly leg (smoke, Playwright, ff-matrix, all OSes) then no-ops through its existing `has-* == 'true'` gate, without an error. The build itself is unaffected: this only silences the downstream test fan-out, unlike `smoke-artifacts-only` (D7), which trims what gets built.

Use `inputs.skip-auto-smoke`, not `github.event.inputs.skip-auto-smoke` — the latter yields the string `"false"`, which is truthy. For `schedule` runs `inputs` is empty, so the expression renders the plain `Build Installers` title, byte-identical to the pre-change default.

**Why not a marker artifact.** Uploading an empty `skip-smoke` artifact also works and was implemented first, but it costs an upload step on every skipped build and leaves a stray artifact for the full retention period — which would also suppress a later *manual* `source=nightly` dispatch aimed at that run id, silently. The title is metadata-to-metadata and scoped to the event.

**Scope: only the automatic path.** `SKIP_AUTO_SMOKE` is gated on `github.event_name == 'workflow_run'`, so a manual `test-smokes-built.yml` dispatch with `source=nightly` and an explicit `run-id` still tests that build. An explicit request to test a run outranks the upstream dispatcher's "do not auto-test me".

**Why this does not break the `workflow_run` trigger.** The `workflows: ["Build Installers"]` filter matches the workflow's `name:`, not its `run-name`; the two are separate objects in the payload (`workflow.name` vs `workflow_run.name`). Verified against the reproduction repo for actions/runner#4141, where a workflow carrying a custom `run-name` still fires its `workflow_run` listener on both `requested` and `completed`. Related open bug worth knowing: actions/runner#4141 reports that on `completed`, `workflow_run.name` is *also* overwritten with the run-name. We key on `display_title`, which is the field GitHub documents for this purpose and which the proposed fix preserves. Note also that at `types: [requested]` the payload's `display_title` is the workflow name, not the run-name — harmless here because this trigger uses `types: [completed]`, and the check fails open (tests run) if that ever changes.

Independent of a separately-tracked `branches:[main]` filter on the same `workflow_run` trigger: that gate narrows by branch, this narrows by explicit per-dispatch choice, regardless of branch.

### D8. macOS runners: scheduled/built runs only, never per-commit

`test-smokes-parallel.yml` (per-commit) must stay fast, so it never passes `runners` and keeps the `ubuntu-latest`/`windows-latest` default.
Expand All @@ -270,7 +288,7 @@ The julia-engine subtree tests are temporarily dev-only. Their direct Quarto sub
Remaining built-mode gaps are preview and serve paths, publishing, installer behavior, Linux arm64, and Playwright visual snapshots.
Windows browser behavior and macOS feature-format coverage are also excluded as described in "Built-mode test legs".
Dev schedules remain because they test source behavior, while built schedules test packaged behavior.
All built legs currently run after every completed `create-release` run. If CI cost becomes excessive, gate heavy legs on scheduled runs.
All built legs currently run after every completed `create-release` run, unless the triggering dispatch set `skip-auto-smoke` (D7.1). If CI cost becomes excessive, gate heavy legs on scheduled runs.

### D10. Release mode only works for post-harness tags

Expand Down
Loading