From 339357c23ba90df56531b2a0736d0ffd2828b52b Mon Sep 17 00:00:00 2001 From: Kevin van Zonneveld Date: Mon, 21 Sep 2026 21:54:30 +0200 Subject: [PATCH] Emit each Changesets release tag only once --- CONTRIBUTING.md | 9 ++-- .../2026-09-21-viewer-alpha-release.md | 47 +++++++++++++++++-- scripts/publish-release.test.ts | 11 ++++- scripts/publish-release.ts | 5 +- 4 files changed, 62 insertions(+), 10 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f4ba3199..18914a20 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -117,8 +117,9 @@ Changelog guidance: Manual fallback (maintainers only): - On the generated version PR's merged commit: `corepack yarn release:publish`. -- This publishes Viewer with its explicit `alpha` tag, then uses `changeset publish` for the - remaining packages and `changeset tag` for release discovery. A failed registry lookup stops the +- This publishes Viewer with its explicit `alpha` tag, then uses `changeset publish --no-git-tag` + for the remaining packages and one `changeset tag` pass for release discovery. Duplicate tag + announcements make the release action try to create the same GitHub release twice. A failed registry lookup stops the release; retries do not republish an existing version. Notes: @@ -127,7 +128,9 @@ Notes: - Scoped packages publish to `latest`, except `@transloadit/viewer`, which is an alpha and uses `alpha`. Its `publishConfig.tag` alone is insufficient because Changesets passes `--tag latest`. - Mark the Viewer GitHub release as a prerelease and verify the npm tags after publishing. On first - publication, npm may also assign `latest`; remove that Viewer tag if present. + publication, npm also assigned `latest` and rejected its removal with HTTP 400 (also reported in + [npm/cli#8490](https://github.com/npm/cli/issues/8490)). Do not claim alpha-only registry tagging: + keep the package description, README and GitHub release explicit about alpha stability. - Viewer prepack uses incremental TypeScript builds, like Utils. Do not recursively clean project references there: Changesets can pack dependent packages concurrently. - If this was a pre-release, remember to reset the [npm `latest` tag](https://www.npmjs.com/package/transloadit?activeTab=versions) to the previous version (replace `x.y.z` with previous version): diff --git a/docs/prompts/2026-09-21-viewer-alpha-release.md b/docs/prompts/2026-09-21-viewer-alpha-release.md index d715b867..b5b2737f 100644 --- a/docs/prompts/2026-09-21-viewer-alpha-release.md +++ b/docs/prompts/2026-09-21-viewer-alpha-release.md @@ -24,10 +24,11 @@ at least 4.9.0 before publishing. The old private workspace protocol must not es - [x] Reproduce private-package/registry-dependency metadata failure with a regression test. - [x] Verify package checks and the packed Next browser fixture (Chromium/WebKit, both cache modes, and development diagnostics). Run the versioned release dry run in PR CI. -- [ ] Reconcile council review; open and finish the alpha preparation PR. -- [ ] Inspect generated #502 versions, dependency floors, changelogs and exact-head CI. -- [ ] Merge #502, watch trusted publication and verify every expected registry version. -- [ ] Verify Viewer alpha tag and GitHub prerelease; prevent an accidental stable `latest` claim. +- [x] Reconcile council review; open and finish the alpha preparation PR (#503). +- [x] Inspect generated #502 versions, dependency floors, changelogs and exact-head CI. +- [x] Merge #502, watch trusted publication and verify every expected registry version. +- [x] Verify Viewer alpha tag and GitHub prerelease; document npm's retained `latest` tag without + claiming stable API support. - [ ] Prove a registry-installed consumer, not just a workspace-linked or vendored package. For a new npm package, trusted-publisher bootstrap may need a maintainer action. Follow the @@ -51,6 +52,44 @@ Local `verify:full`, `check`, the 30 image fixture contract tests and the seven The packed fixture passed both production modes (58 browser cases each), the secretless public-only builds, and ten development cases. Evidence: `/tmp/viewer-alpha-20260921.gU9s3H/`. +## Publication receipts and follow-up + +#503 merged as `78de89e`; #502 merged as `a4db0b2` with green PR and main CI. The bot's force push +was prohibited, so #502 was regenerated with Changesets and updated through normal commits without +weakening repository rules. Its merged tree matches the tested head `fe77d59`. + +Viewer 0.0.1 required a maintainer MFA bootstrap because trusted publishing could not create the +new package. The registry accepted it at 19:34 UTC; its package index caught up around 19:39 UTC. +Release run `35643913063`, attempt 3, published the other six packages through trusted publishing: +Utils 4.9.0, Node and legacy CLI 4.13.0, Types and Zod 4.4.1, MCP 0.3.32. All seven versions are +independently available from the registry; the six CI-published versions have provenance. +Viewer's GitHub release is explicitly an alpha/prerelease. npm auto-created `latest` alongside +`alpha`; removing that Viewer-only tag with a fresh MFA code returned HTTP 400, matching +[npm/cli#8490](https://github.com/npm/cli/issues/8490). Do not keep requesting codes for this refusal +or claim that `latest` was removed. Alpha stability remains explicit in the package, README and +GitHub release. Configure its trusted publisher for future releases rather than leaving manual +publishing as the permanent process. + +The packages and all seven GitHub releases were created, but the workflow then failed: both +`changeset publish` and `changeset tag` emitted the stable package tags. With this workflow's +GitHub-API commit mode, the action parsed both announcements and tried to create those releases +twice. Do not delete valid releases or publish new versions to hide that failure. + +- [x] Reproduce the missing `--no-git-tag` guard with a failing publisher test. +- [x] Disable publication's tag pass and retain one final tag pass, including Viewer. +- [x] Cover failure before tagging so an incomplete publish does not announce unready versions. +- [ ] Review and verify this narrow release-orchestration fix; land it and confirm main's release + workflow succeeds without republishing any existing version. + +The follow-up council reported no findings; its Claude leg hit the monthly spending limit again. +The eight publisher tests and `yarn check` pass. No package version or published bytes change in +this orchestration fix. + +Content PR #6047 is the registry-installed consumer and records the later backend/Console/CLI and +Storage dogfood gates. The API2 x64 main build at `b553c67b84` is green and uploaded to R2: +`s3://build-artifacts-transloadit/main/api2/api2-gha-ci-35641331297.tar.gz`. ARM64 is still running at +this checkpoint. Deployment remains Kevin's responsibility. + ## Before API2 is deployed Storage Built-ins, native catalog APIs and device login need the matching backend rollout. The diff --git a/scripts/publish-release.test.ts b/scripts/publish-release.test.ts index 75d62673..4fff9169 100644 --- a/scripts/publish-release.test.ts +++ b/scripts/publish-release.test.ts @@ -19,7 +19,9 @@ test('the release publishes Viewer to alpha and leaves stable packages to Change expect(run.mock.calls.map(([command, args]) => [command, args])).toEqual([ ['npm', ['view', `${viewer.name}@${viewer.version}`, 'version', '--json']], ['npm', ['publish', './packages/img', '--access', 'public', '--tag', 'alpha']], - ['corepack', ['yarn', 'changeset', 'publish']], + // Only the following tag command may announce tags to changesets/action; duplicate + // announcements make it try to create each stable GitHub release twice. + ['corepack', ['yarn', 'changeset', 'publish', '--no-git-tag']], ['corepack', ['yarn', 'changeset', 'tag']], ]) }) @@ -50,6 +52,13 @@ test('a failed Viewer publish stops before the stable release and tags', async ( expect(run).toHaveBeenCalledTimes(2) }) +test('a failed stable release does not announce tags for unpublished packages', async () => { + run.mockResolvedValueOnce({ exitCode: 0, stdout: JSON.stringify(viewer.version) }) + run.mockRejectedValueOnce(new Error('stable publish refused')) + await expect(import('./publish-release.ts')).rejects.toThrow('stable publish refused') + expect(run).toHaveBeenCalledTimes(2) +}) + test('Viewer prepack never recursively deletes artifacts used by another publisher', () => { expect(viewer.scripts.build).not.toContain('--clean') }) diff --git a/scripts/publish-release.ts b/scripts/publish-release.ts index 1e1be399..98e09a17 100644 --- a/scripts/publish-release.ts +++ b/scripts/publish-release.ts @@ -29,8 +29,9 @@ async function main(): Promise { throw new Error('Viewer registry lookup failed; refusing to treat a network/auth error as E404') } - await execa('corepack', ['yarn', 'changeset', 'publish'], options) - // Include the alpha in Changesets' normal tag output so its action creates the release too. + // Emit each tag only once: changesets/action turns every announcement into a GitHub release. + // The separate tag pass also includes Viewer, which was already published with its alpha tag. + await execa('corepack', ['yarn', 'changeset', 'publish', '--no-git-tag'], options) await execa('corepack', ['yarn', 'changeset', 'tag'], options) }