Skip to content

Fix stale quarto-cli release being accepted by release-info - #2170

Open
cderv wants to merge 4 commits into
mainfrom
fix/release-info-prerelease-version-check
Open

Fix stale quarto-cli release being accepted by release-info#2170
cderv wants to merge 4 commits into
mainfrom
fix/release-info-prerelease-version-check

Conversation

@cderv

@cderv cderv commented Aug 28, 2026

Copy link
Copy Markdown
Member

On 2026-08-17 the GitHub Releases API served inconsistent paginated results to
the Update Downloads cron job, and the resolved latest prerelease bounced
across four stale versions over about 100 minutes:

13:19  1.11.1  (correct)
13:47  1.2.113
14:28  0.2.434
14:43  1.9.10
14:57  1.5.49
15:26  1.11.1  (back to correct, stable since)

Each bad value was committed to main, synced to prerelease, and published,
so the downloads page and the /download/prerelease/* redirects advertised
assets from an old release. The 14:43 run also opened #2146, regenerating 63
reference pages against a ten-month-old v1.9.10; it was closed unmerged.

Root Cause

getPrerelease() in .github/workflows/actions/release-info/index.js returns
the first release flagged prerelease from page 1 of listReleases, relying
entirely on the API returning newest-first. The only downstream validation is
that the release has assets, which a ten-month-old release still has. All four
bad values are genuine old quarto-cli releases, so nothing in the payload marks
the response as wrong.

Fix

A glitched page cannot be told apart from a good one by inspection, so the
check compares against state the API does not control: the version already
committed in the JSON file the run would overwrite. A resolved version older
than the committed one throws, core.setFailed fails the step, and
git-auto-commit-action never runs, so nothing is committed, synced,
published, or handed to the reference-page workflow. Each check sits before
processRelease(), so a rejected run downloads no assets and writes nothing.

Comparison is numeric per segment, since both 1.9.10 and 1.10.2 sort wrong
as strings.

Comparing against the latest stable release was rejected instead: the stable
and prerelease lines cross at every release cycle. Today v1.10.18 is stable
while v1.11.1 is the prerelease, and once the 1.11 line goes stable the latest
stable will exceed the latest prerelease until 1.12.0 ships, so valid data
would be rejected.

The paging loop was also bounded. It was while(true) with no page cap and no
empty-page exit, so a repository with no prerelease at all would loop until the
job timed out.

Test Plan

  • npm test from .github/workflows/actions/release-info/ passes both suites

  • Each version the API served on 08-17 is rejected against the committed baseline:

    node -e "
    const { assertNotOlder } = require('./.github/workflows/actions/release-info/version-guard');
    const previous = require('./docs/download/_prerelease.json').version;
    for (const bad of ['1.2.113','0.2.434','1.9.10','1.5.49']) {
      try { assertNotOlder('v'+bad, previous, 'x'); console.log('NOT CAUGHT', bad); }
      catch { console.log('caught', bad); }
    }"
    
  • The current correct version is still accepted, so ordinary runs are unaffected

  • zizmor .github/ reports no findings, so audit-workflows.yml stays green

  • After merge, the next scheduled Update Downloads run completes normally

The original failure cannot be reproduced on demand, since it depends on the
API returning inconsistent pages. The equivalent end-to-end check inverts it:
raise the version field in a local copy of docs/download/_prerelease.json
above the real latest prerelease, run the action against the live API, and
confirm the step fails with the guard message and writes nothing. Driving that
through a workflow_dispatch of Update Downloads needs care, because that
workflow commits and pushes once the action succeeds.

Fixes quarto-dev/quarto-cli#14823

cderv added 4 commits August 28, 2026 18:04
zizmor flags actions/setup-node as a high-severity cache-poisoning risk
because it enables caching by default, and audit-workflows.yml runs zizmor
over .github on every PR. The repo audits clean without this file, so the
step would have introduced the only finding in the tree.

The pin bought very little: these tests use nothing beyond node:assert and
other built-in modules, so the runner image Node is sufficient.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Prerelease reference-page workflow can pick a stale quarto-cli version on GitHub API paging glitches

1 participant