Skip to content

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

Description

@cderv

The update-prerelease-reference workflow on quarto-web can pick a stale quarto-cli prerelease version and regenerate the reference docs from it, because the version resolution has no check that the version it found is actually the newest.

What happened

On 2026-08-17, between roughly 13:19 and 16:26 UTC, the GitHub Releases API served inconsistent paginated results to the update-downloads cron job (runs every 15 min). The "latest prerelease" version it resolved bounced across consecutive runs:

13:19  1.11.1  (correct)
13:47  1.2.113
14:28  0.2.434 (a "Daily" build)
14:43  1.9.10  <- this run opened quarto-dev/quarto-web#2146
14:57  1.5.49
15:26  1.11.1  (back to correct, stable since)

The 14:43 run landed on v1.9.10 (published 2025-11-06, ten months stale) and, since it found a valid release with assets, went ahead and cloned quarto-cli at that tag and opened a PR regenerating 63 reference files against it. A later run the same day, back on the correct v1.11.1, confirmed the prerelease branch was already current:

No changes detected - reference pages are already up to date for v1.11.1

I closed quarto-dev/quarto-web#2146 without merging, since it would have reverted the reference docs to the v1.9.10 state.

Where this comes from

The version comes from getPrerelease(), which pages through listReleases and takes the first release flagged prerelease: true, with no comparison against the previously resolved version or the release's semver position:

source

const getPrerelease = async () => {
  // List the releases
  var pagenumber = 1;
  var matchedRelease = undefined;
  while(true) {
    console.log("page " + pagenumber + " of prereleases");
    var releases = await octokit.rest.repos.listReleases({
      owner,
      repo,
      per_page: 25,
      page: pagenumber
    });  

    for (const release of releases.data) {
      if (release.prerelease) {
        matchedRelease = release;
        break;
      }
    }

    if (matchedRelease) {
      break;
    } else {
      pagenumber = pagenumber + 1;
    }
  }
  return matchedRelease;    
}

The only validation downstream is that the matched release has assets, which an old prerelease still has, so it passes silently.

Suggestion

We could add a sanity check before accepting the resolved prerelease, for example rejecting a candidate whose semver is lower than the currently committed docs/download/_prerelease.json version, or than the latest stable release. That would have caught all six bad values in the table above without needing the API to behave.

Related: quarto-dev/quarto-web#2146

Metadata

Metadata

Assignees

No one assigned

    Labels

    documentationDoc improvements & quarto-web

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions