diff --git a/.claude/commands/make-release/SKILL.md b/.claude/commands/make-release/SKILL.md new file mode 100644 index 00000000000..f6c5f71c5d5 --- /dev/null +++ b/.claude/commands/make-release/SKILL.md @@ -0,0 +1,72 @@ +--- +name: make-release +description: Drive a Quarto release end-to-end from the dev-docs release checklists. Invoke explicitly with /make-release — do not auto-invoke from ambient conversation, a release cut is high-stakes. Handles three release types — a routine dev prerelease off main, the first stable release of a new major.minor cycle, or a patch release on an existing stable branch — by asking which one applies (or reading it from an unambiguous request), then follows the matching checklist, verifying real git/gh state at each step and pausing for explicit human confirmation before every irreversible or externally-visible action. +--- + +# Make Release + +Command-only helper that drives an existing release checklist. **Do not auto-invoke** — only run when the user explicitly asks (e.g. `/make-release`). This skill does not restate the release steps; the checklists below are the source of truth. Your job is to read the right one and drive it with real-state verification and confirmation gates. + +## 1. Detect which checklist applies (verify, don't trust self-report) + +There are three release types, each with its own checklist. Branch-existence alone doesn't distinguish all three — a routine prerelease and a first-stable-of-cycle release can both start with no `v1.x` branch yet — so first establish intent, then verify with git where that disambiguates: + +- **Routine dev prerelease off `main`** (no version bump beyond the prerelease counter, no new branch) → drive + `dev-docs/checklist-make-a-new-quarto-prerelease.md` +- **First stable release of a new major.minor cycle** (cuts a new `v1.x` branch) → drive + `dev-docs/checklist-make-a-new-quarto-release.md` +- **Patch release on an already-cut stable branch** (e.g. another `v1.9.x`) → drive + `dev-docs/checklist-make-a-new-stable-quarto-release.md` + +If the user's request doesn't already make the type unambiguous, ask. Once it's a stable release (first or patch), confirm which with real state, don't trust self-report: + +```bash +git ls-remote --heads origin v1.x # substitute the target major.minor, e.g. v1.10 +``` + +Branch absent → first stable of the cycle; branch present → patch release. + +Out of scope: backporting an individual merged PR to a stable branch is a different task, not a release cut — see `dev-docs/checklist-backport-a-pr.md` directly, this skill doesn't drive it. + +**Always confirm before driving anything** — even when the type seems obvious from the request. State it plainly, e.g. "I will follow this checklist: `dev-docs/checklist-make-a-new-quarto-release.md`", and wait for the user's go-ahead before starting step 1. This is the one point where a wrong detection would send the whole run down the wrong checklist, so confirm every time, not just when ambiguous. + +## 2. Drive the checklist top to bottom, verifying real state, narrating as you go + +Read the matching checklist and work it in order. Before each step (not just the hard-confirm gates in section 3), state in one line what you're about to do and why, so the user can stop you early if something looks wrong — don't silently chain multiple steps together without narrating between them. + +At each step, confirm the real state with `git`/`gh` rather than trusting the checklist text or the user's word that something happened — e.g. that a branch/tag exists, a workflow run actually succeeded (`gh run view `), a release's flags are set, a version field is what it should be. A step isn't done until real state confirms it. + +## 3. Hard-confirm gates (stop and get explicit human OK before running) + +Never fire these on your own — state exactly what you're about to do, to which repo, and wait for an explicit go: + +- Pushing the new stable branch (`git push origin v1.x`) +- Pushing version-bump / release-checklist commits to `main` +- Any `workflow_dispatch` carrying a publish flag (e.g. `publish-release=true`, chocolatey publish checkbox, Cloudsmith `dry-run=false`) +- Editing a live GitHub release's flags (pre-release / "Set as latest release") +- Pushing the release tag +- Production package-registry publishes: pypi (`Production Release`), chocolatey, Cloudsmith manual publish + +Dry-runs and read-only verification don't need a gate — run them freely. + +## 4. Cross-repo flow + +Three repos are involved, with real ordering dependencies: + +- `quarto-dev/quarto-cli` — the release build and tag +- `quarto-dev/quarto-web` — quarto.org site, downloads, highlights, announcement +- `quarto-dev/quarto-cli-pypi` — pypi publish + +Ordering matters: e.g. quarto-cli's release build must fully publish before quarto-web's `update-downloads.yml` will pick it up, and chocolatey waits on the quarto.org download page updating. State which repo each step targets. Use `gh --repo / ...` so verification works even without a local clone of every repo. + +**No local quarto-web clone:** if a step needs local content edits to quarto-web (e.g. the `docs/prerelease//` highlights `.qmd` files, or `_quarto.yml` edits) and no local quarto-web clone is present, stop and ask the user for the clone path or to make the edit themselves. Do not attempt the edit through the GitHub API and do not silently skip the step. + +**Release announcement:** the announcement post is published on the Posit Open Source site (opensource.posit.co), not via a quarto-web blog PR (blog migrated June 2026). Point the user there; do not name any internal publishing tool. + +## 5. When the checklist doesn't match reality + +If a step's described state doesn't match what you find (stale path, wrong ordering, already done, renamed workflow), surface the discrepancy and get a human decision. Don't silently work around it, and don't edit the checklist mid-release — suggest a follow-up doc-fix PR instead. + +## 6. Non-blocking gaps found along the way + +If you spot a non-blocking gap during the release (e.g. a CI automation that only covers one branch, a doc-archival gap), don't try to fix it inline. Tell the user to track it in whatever task tracker they/their project use, and continue the release. diff --git a/.claude/rules/changelog.md b/.claude/rules/changelog.md index e94b85e7ae8..2784b3e90da 100644 --- a/.claude/rules/changelog.md +++ b/.claude/rules/changelog.md @@ -107,7 +107,9 @@ The stable changelog has a dual top-level structure that does NOT exist on main: **Backport entries always go under `# v{next} backports > ## In this release`.** Never under `## Regression fixes` of the `# v{this} changes` section — that section is frozen and tracks the original v1.x release. -**Lifecycle:** When the next v1.x.y patch ships, entries under `## In this release` get demoted to `## In previous releases` as part of release prep. Don't pre-empt this — leave them under `In this release` until release time. +**Where the scaffold comes from:** the `# v{next} backports` heading (with empty `## In this release` / `## In previous releases`) and the `# v{this} changes` wrapper are seeded when the stable branch is cut — see the branch-creation step in `dev-docs/checklist-make-a-new-quarto-release.md`. If a stable branch is missing the `# v{next} backports` heading, it was cut without it: add the scaffold before filing the entry, rather than falling back to `## Regression fixes`. + +**Lifecycle:** When the next v1.x.y patch ships, entries under `## In this release` get demoted to `## In previous releases` as part of release prep (the changelog step in `dev-docs/checklist-make-a-new-stable-quarto-release.md`). Don't pre-empt this — leave them under `In this release` until release time. ### Workflow diff --git a/dev-docs/checklist-backport-a-pr.md b/dev-docs/checklist-backport-a-pr.md index 685ca968e15..be00ac1141b 100644 --- a/dev-docs/checklist-backport-a-pr.md +++ b/dev-docs/checklist-backport-a-pr.md @@ -5,7 +5,8 @@ We backport development PRs to the stable branch using the following steps: - Cherry-pick the commits from the development branch: - `git cherry-pick ` for every commit identified above. - Resolve conflicts as needed. -- Revert the changelog file changes if it was included in the original commits. -- Move the new changelog entries to the top of old changelog, under "New in this release". +- Revert the changelog file changes if they were included in the original commits (the dev-branch `changelog-1.(x+1).md` does not exist on the stable branch, so the cherry-pick shows a modify/delete conflict — drop that hunk). +- Add the new changelog entries to `news/changelog-1.x.md`, under `# v1.(x+1) backports` > `## In this release` at the top of the file. There are change categories in the development release but not in the backport release, so just add them chronologically to the section. + If that scaffold heading is missing, the stable branch was cut without it — seed it per the branch-creation step in `checklist-make-a-new-quarto-release.md` rather than filing the entry under the frozen `## Regression fixes` (that section is the previous version's own release fixes). - Run the test suite GHA workflow on the stable branch manually. diff --git a/dev-docs/checklist-make-a-new-quarto-release.md b/dev-docs/checklist-make-a-new-quarto-release.md index d228e695ba1..6765f6c1e61 100644 --- a/dev-docs/checklist-make-a-new-quarto-release.md +++ b/dev-docs/checklist-make-a-new-quarto-release.md @@ -1,28 +1,37 @@ +> A Claude Code skill can drive this checklist interactively: run `/make-release` (see `.claude/commands/make-release/`). It reads this file, verifies real `git`/`gh` state at each step, and pauses for confirmation before irreversible actions. This checklist stays the source of truth — the skill just follows it. + - [ ] ensure your git repo is up to date with `main` +- [ ] ensure CI is green for `main` +- [ ] ensure `main` is coherent with the last prerelease build (usually the last prerelease tag is at `main`) +- [ ] Verify `changelog-1.x.md` is in good shape. - [ ] create a branch `v1.x`, where x is the version being released - `git checkout -b v1.4` - `git push origin v1.4` + - [ ] on this new branch, set `CITATION.cff` `version` (quote it — e.g. `"1.4"`; unquoted `1.10`-style values parse as a YAML float and lose the trailing zero) and `date-released` (today), then commit and push. Set once per branch here; the `CITATION.cff` step on `main` below is a cherry-pick of this commit, not a re-edit. + - [ ] on this new branch, seed the backports scaffold in `news/changelog-1.x.md`: wrap the existing body under a `# v1.x changes` heading and prepend a `# v1.(x+1) backports` heading holding empty `## In this release` and `## In previous releases` subsections (e.g. cutting `v1.4` with `main` moving to `1.5`, prepend `# v1.5 backports`). Commit and push. Backported PRs land under `## In this release` (`checklist-backport-a-pr.md`); the monthly patch release demotes them to `## In previous releases` (`checklist-make-a-new-stable-quarto-release.md`). Without this scaffold later backports have nowhere correct to go and get mis-filed under the frozen `## Regression fixes`. - [ ] mark the current release as the new release in the `main` branch - [ ] switch your repo back to `main`: `git checkout main` - - [ ] edit QUARTO_VERSION line in `/configuration` to be the new version (e.g. `1.5`) + - [ ] edit the QUARTO_VERSION line in `/configuration` to the new version (e.g. `1.5`) + - [ ] commit with message '[release checklist] QUARTO_VERSION -> x.x' - [ ] push the changes to the `main` branch - [ ] kick off a v1.5 build in GHA: https://github.com/quarto-dev/quarto-cli/actions/workflows/create-release.yml - - [ ] ensure the build completes successfully + Command: `gh workflow run create-release.yml --repo quarto-dev/quarto-cli --ref main -f publish-release=true -f pre-release=true` + - [ ] ensure the build completes successfully (e.g. `gh run view `) - [ ] mark v1.4 release as stable - [ ] go to https://github.com/quarto-dev/quarto-cli/releases - - [ ] find the latest v1.4 release and edit, (eg https://github.com/quarto-dev/quarto-cli/releases/edit/v1.4.549) - - [ ] at the bottom of the page, there will be two checkboxes, "Set as pre-release" and "Set as latest release": - - [ ] "Set as pre-release" should be unchecked, and - - [ ] "Set as latest release" should be checked. -- [ ] once the v1.5 build completes, edit the quarto.org website configuration on https://github.com/quarto-dev/quarto-web to reflect the new version - - this means flipping the profile group configuration in `_quarto.yml` from `[rc,prerelease]` to `[prerelease,rc]` + - [ ] find the latest v1.4 release and edit it (e.g. https://github.com/quarto-dev/quarto-cli/releases/edit/v1.4.549) + - [ ] at the bottom of the page, set the two checkboxes: + - [ ] "Set as pre-release" unchecked + - [ ] "Set as latest release" checked +- [ ] once the v1.5 build completes, edit the quarto.org website config on https://github.com/quarto-dev/quarto-web for the new version + - flip the profile group in `_quarto.yml` from `[rc,prerelease]` to `[prerelease,rc]` + - only if this cycle had a release-candidate (RC) phase — skip if you went straight from prerelease to stable, since the flip-back to `[rc,prerelease]` never happened either - [ ] push the changes to the `main` branch - [ ] quarto-dev/quarto-web changes - - - wait for the downloads file to be automatically updated by the GitHub Action on https://github.com/quarto-dev/quarto-web - - [ ] wait for https://github.com/quarto-dev/quarto-web/actions/workflows/update-downloads.yml to run (it runs every 15 minutes, or you can manually trigger it) - - This workflow run rendered the website: https://github.com/quarto-dev/quarto-web/actions/runs/12016407762 - - This workflow run did not: https://github.com/quarto-dev/quarto-web/actions/runs/12016128695 + - wait for the downloads file to be updated automatically by the GitHub Action on https://github.com/quarto-dev/quarto-web + - [ ] wait for https://github.com/quarto-dev/quarto-web/actions/workflows/update-downloads.yml to run (every 15 minutes, or trigger it manually) + - This run rendered the website: https://github.com/quarto-dev/quarto-web/actions/runs/12016407762 + - This run did not: https://github.com/quarto-dev/quarto-web/actions/runs/12016128695 - [ ] Ensure the run just triggered looks like the _former_ - [ ] Ensure the header in https://quarto.org has the new version number in its 'generator' meta tag - [ ] Ensure the download links on https://quarto.org/docs/get-started/ and https://quarto.org/docs/download/ point to the stable and prerelease versions respectively @@ -31,55 +40,57 @@ - [ ] create `docs/prerelease/1.5/{_highlights, index}.qmd` files based on the ones from the previous release - [ ] change `docs/prerelease/_highlights-prerelease.qmd` so its include points to the new version-specific `_highlights.qmd` file (here, 1.5) - [ ] change `docs/prerelease/_highlights-release.qmd` so its include points to the new version-specific `_highlights.qmd` file (here, 1.4) - - [ ] add the stable version to the older downloads list by editing /docs/download/\_download-older.yml - - [ ] run `quarto run tools/release-notes.R` to generate the release notes and bump `version` in `_quarto.yml` (to released version) and `_quarto-prerelease-docs.yml` (to next prerelease) + - [ ] run `quarto run _tools/release-notes.R` — generates the release notes and bumps `version` in `_quarto.yml` (released version) and `_quarto-prerelease-docs.yml` (next prerelease); it also adds the stable version to `/docs/download/_download-older.yml` automatically (no separate manual edit) - [ ] push the changes to `prerelease` branch, ensure they build correctly - [ ] Merge the `prerelease` branch into `main`, push to `main` + - [ ] before merging, run `git log --cherry-pick --right-only prerelease...main` to catch drifted commits (direct pushes and failed auto-backport PRs both silently miss `prerelease`); cherry-pick genuine fixes, ignore intentional cross-version divergence + - [ ] a direct push (default) skips `port-to-prerelease.yml`; merging via PR instead lets CI verify the build first — label the PR `no-sync-prerelease` so the backport action doesn't cherry-pick prerelease's history back onto itself + - [ ] on a conflict in `docs/cli/cli-info.json`, neither merge side is correct — regenerate it from a quarto build at the stable tag (the only automation, `update-prerelease-reference.yml`, produces the prerelease/dev version, not stable) + - [ ] watch for freeze conflicts — see quarto-web `.claude/rules/quarto-web-workflow.md` - [ ] ensure the build completes successfully - [ ] verify `_quarto.yml` `version` on `main` reflects the released version (e.g. `'1.4'`) — needed for `prerelease` shortcodes to resolve blog links and pre-release callouts - [ ] Merge `main` into `prerelease`, push to `prerelease` - [ ] ensure the build completes successfully - - [ ] Create new tag on `main` with stable release version number (here, `v1.4`) to mark when the new main site version went live + - [ ] Create a new tag on `main` with the stable release version (here, `v1.4`) to mark when the new main site version went live - [ ] `git tag -a v1.4 -m "v1.4"` - [ ] `git push origin v1.4` - - [ ] Verify version numbers were updated by `tools/release-notes.R` + - [ ] Verify version numbers were updated by `_tools/release-notes.R` - [ ] `_quarto.yml` `version` should be the released version (e.g. `'1.4'`) - [ ] `_quarto-prerelease-docs.yml` `version` should be the next prerelease (e.g. `'1.5'`) - - [ ] publish the release blog post that should exist in https://github.com/quarto-dev/quarto-web/tree/main/docs/blog/posts - - [ ] Create a branch off of `main` (to trigger our PR automation to make the corresponding change to `prerelease`). - - [ ] Removing the `draft: true` line in the metadata - - [ ] Change the date to match the release date. + - [ ] publish the release announcement post — since the June 2026 blog migration this is no longer a quarto-web PR: the post goes on the Posit Open Source site (opensource.posit.co), not `quarto-web/docs/blog/posts` (that directory now just redirects there) -- [ ] Update https://github.com/quarto-dev/quarto-cli/blob/main/CITATION.cff -- [ ] Packaging and package managers, etc - - [ ] chocolatey (Only once quarto.org download page is updated with the new release) +- [ ] Update https://github.com/quarto-dev/quarto-cli/blob/main/CITATION.cff (cherry-pick the commit made on the `v1.x` branch at branch-cut, above) +- [ ] Packaging and package managers, etc. + - [ ] chocolatey (only once the quarto.org download page shows the new release) - https://github.com/quarto-dev/quarto-release-bundles/actions/workflows/build-and-publish-choco.yaml - - [ ] In "Build Choco package & Publish" workflow page, click "Run Workflow" + - [ ] In the "Build Choco package & Publish" workflow page, click "Run Workflow" - Check the "Whether to publish or not the package on chocolatey" checkbox - Wait for @cderv to receive email confirmation, no action needed - [ ] pypi - Go to the [quarto-cli-pypi repo](https://github.com/quarto-dev/quarto-cli-pypi) - - [ ] Update `version.txt` to be the version you'd like to publish and commit - - Go to actions + - [ ] Update `version.txt` to the version you'd like to publish and commit + - Go to Actions - Select 'Publish Quarto PyPi' - [ ] Click "Run Workflow" - - **Publishing Test**: You may elect to publish to test.pypi first by _unchecking_ the `Production Release` option - - Once complete, trest using + - **Publishing Test**: you may publish to test.pypi first by _unchecking_ the `Production Release` option + - Once complete, test using ```bash python3 -m pip install --index-url https://test.pypi.org/ --extra-index-url https://pypi.org/ quarto-cli ``` - - You may have to run this command twice as the first time may report the package not found and cause cache invalidation. The next try should succeed. + - You may have to run this command twice — the first try may report the package not found and cause cache invalidation; the next try should succeed. - Published to: - - **Publishing Production**: You may elect to publish to production pypyi by checking the `Production Release` option + - **Publishing Production**: publish to production pypi by checking the `Production Release` option - Published to: - Take a sip of tea ☕, bask in the glory of automation. - - Others installers - - Cloudsmith: Automatically published by Build Installers workflow. No action needed. - - See [cloudsmith-publishing.md](cloudsmith-publishing.md) for manual republishing if needed. - - conda-forge: An automated PR will be created to update the package version in the feedstock - - This is community maintained, so no action is needed from us - except maybe helping if there are issues with the PR. We are assigned as reviewers to the PR. - - Winget: An automated PR will be created by a winget bot in . - - This is community maintained, so no action is needed from us. - - Scoop: This will be updated automatically in the manifests repo. No action is needed from us. Maintained as personal project by @cderv. - - Homebrew: This is automated through an homebrew bot to update the cask + - Other installers + - [ ] Cloudsmith: **not auto-published for a first stable release** — you must trigger it manually (a first stable is cut by relabeling an existing prerelease build, whose auto-publish job was skipped). Why + full context: [cloudsmith-publishing.md](cloudsmith-publishing.md#first-stable-release-of-a-cycle). Later patch releases on the stable branch auto-publish, so this is not needed there. + - [ ] Dry run: `gh workflow run publish-cloudsmith.yml --repo quarto-dev/quarto-cli -f version=v1.x.y -f dry-run=true` + - [ ] Verify it succeeded: `gh run list --repo quarto-dev/quarto-cli --workflow publish-cloudsmith.yml --limit 1 --json databaseId,status,conclusion` + - [ ] Production: `gh workflow run publish-cloudsmith.yml --repo quarto-dev/quarto-cli -f version=v1.x.y -f dry-run=false` + - conda-forge: an automated PR updates the package version in the feedstock + - Community maintained, so no action needed from us — except maybe helping if the PR has issues. We are assigned as reviewers. + - Winget: an automated PR is created by a winget bot in . + - Community maintained, so no action needed from us. + - Scoop: updated automatically in the manifests repo. No action needed from us. Maintained as a personal project by @cderv. + - Homebrew: automated through a homebrew bot to update the cask - Manifest: diff --git a/dev-docs/checklist-make-a-new-stable-quarto-release.md b/dev-docs/checklist-make-a-new-stable-quarto-release.md index 658b22c1d52..276483fcfef 100644 --- a/dev-docs/checklist-make-a-new-stable-quarto-release.md +++ b/dev-docs/checklist-make-a-new-stable-quarto-release.md @@ -1,3 +1,5 @@ +> A Claude Code skill can drive this checklist interactively: run `/make-release` (see `.claude/commands/make-release/`). It reads this file, verifies real `git`/`gh` state at each step, and pauses for confirmation before irreversible actions. This checklist stays the source of truth — the skill just follows it. + - [ ] ensure tests pass on stable branch - Actions -> Parallel Smokes Tests -> Run Workflow -> - Select the current stable branch in the "Use workflow from... v1.x" dropdown @@ -34,7 +36,7 @@ - Got to - In the "Build Choco package & Publish" workflow, click "Run Workflow" and be sure to check the "Whether to publish or not the package on chocolatey" checkbox - Wait for @cderv to receive email confirmation, no action needed. -- [ ] Update the stable changelog by moving entries to the "in previous release". Use "[release checklist]" in the commit message to make it easier to spot if a new release is needed next month. +- [ ] Update the stable changelog `news/changelog-1.x.md` by moving entries from `# v1.(x+1) backports` > `## In this release` down to `## In previous releases` — they've now shipped in this patch. Use "[release checklist]" in the commit message to make it easier to spot if a new release is needed next month. - Others installers - Cloudsmith: Automatically published by Build Installers workflow. No action needed. diff --git a/dev-docs/cloudsmith-publishing.md b/dev-docs/cloudsmith-publishing.md index 06a46b8fe37..4abb9ef1a3d 100644 --- a/dev-docs/cloudsmith-publishing.md +++ b/dev-docs/cloudsmith-publishing.md @@ -6,7 +6,11 @@ Publishes Linux packages (DEBs and RPMs) to Cloudsmith's `posit/open` repository ## Automatic Publishing -Called automatically by `create-release.yml` for stable releases (non-prereleases). +Called automatically by `create-release.yml`, but only when it runs with `pre-release=false` — the `call-cloudsmith-publish` job's condition is `inputs.publish-release && !(inputs.pre-release == true)`. + +### First stable release of a cycle + +The first stable release of a new major.minor (per `checklist-make-a-new-quarto-release.md`) is cut by relabeling an existing prerelease build as "Latest" on GitHub, not by re-running `create-release.yml` with `pre-release=false`. That build's `call-cloudsmith-publish` job was skipped, so Cloudsmith never gets it automatically — manually publish it (see below). Later patch releases on the stable branch (`checklist-make-a-new-stable-quarto-release.md`) dispatch `create-release.yml` with `pre-release=false` directly, so those publish automatically as expected. ## Manual Publishing