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
14 changes: 12 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ on:
branches:
- main

concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false
# A version-PR merge must not be replaced by a later feature merge while waiting to publish.
queue: max

jobs:
release:
name: Changesets release
Expand All @@ -23,13 +29,17 @@ jobs:
registry-url: https://registry.npmjs.org
- run: npm install -g npm@11.5.1
- run: corepack yarn install --immutable
- run: corepack yarn install --immutable
- run: corepack yarn tsc:utils
- run: corepack yarn tsc:zod
- run: corepack yarn tsc:node
- name: Prepare the version PR without force pushes
id: version
run: node scripts/version-release.ts
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- uses: changesets/action@a45c4d594aa4e2c509dc14a9f2b3b67ba3780d0d
if: steps.version.outputs.has_changesets == 'false'
with:
version: corepack yarn changeset:version:release
publish: corepack yarn release:publish
commitMode: github-api
env:
Expand Down
13 changes: 13 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,19 @@ Release flow:
4. Review and merge the version PR. CI publishes automatically via npm trusted publishing (OIDC).
5. Add [release notes](https://github.com/transloadit/node-sdk/releases) once the publish succeeds.

The version PR updater restores old generated files to their merge base, merges `main` into
`changeset-release/main`, then appends freshly generated versions. These are GitHub-signed commits,
not force pushes or branch recreation: the organization requires verified commits and blocks
non-fast-forward updates. Restoring only generated files prevents conflicts with new dependencies,
lockfile entries or changeset edits. Changesets still owns version calculation, changelogs and
publication. Unexpected source edits or concurrent branch changes stop the update; inspect them and
rerun the latest main release job. Do not merge an incomplete update. Approve any CI runs awaiting
approval on the bot-created PR before merging it. Do not bypass the required checks or branch rules.
Release runs use GitHub's `queue: max` so newer pushes do not replace a pending publication run.
The queue supports up to 100 pending runs; monitor a larger backlog rather than assuming unlimited
retention. A superseded run with changesets skips versioning; a version-PR merge with no pending
changesets still goes through the existing publisher, even if main has advanced.

Changelog guidance:

- Treat changesets as the changelog source. Write them as release notes (short, user-facing, and accurate).
Expand Down
49 changes: 49 additions & 0 deletions docs/prompts/2026-09-24-release-versioning.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Signed, append-only version PR updates

## Why

Release PR #509 already merged as `6b16ad1200e356bed22e00ee5dd11b6411db63f6`. Release run
`35887362708` published Node/legacy 4.14.0, Types/Zod 4.5.0, MCP 0.3.34 and Viewer 0.0.2.
Viewer remains on the alpha tag, with a GitHub prerelease. Do not republish these versions.

The old release branch is gone, but Changesets' current updater force-resets its version branch.
That conflicts with the organization's verified-signature and fast-forward requirements. This
change replaces branch preparation only; Changesets still calculates versions and publishes.

## Contract

- Generate from the exact current main run SHA, never from previously generated versions.
- Restore only generated deltas to the merge base with a signed append-only commit, merge main
through GitHub, then append the regenerated tree with an exact expected-head check.
- Preserve history as well as bytes: otherwise squash-merging can restore new consumed changesets.
Preserve executable/symlink source changes through the native merge, not GraphQL's file API.
- Refuse unexpected source edits, invalid file modes and concurrent edits. No force pushes,
destructive branch recreation, extra credentials or weakened rules.
- Serialize with `queue: max`, retaining up to 100 pending runs. Default single-pending concurrency
could discard a version-PR publication when the next feature merge arrives.
- Superseded feature runs skip safely. Runs with no pending changesets still publish, even after
main advances. Keep the existing trusted publisher and Viewer alpha policy.
- This updater deliberately supports the repository's normal Changesets mode, not `pre.json`.
Viewer uses an npm tag, not Changesets prerelease mode; unexpected generated files fail closed.

## Verification

- [x] Red-first tests for blob newlines, changeset/history consumption, generated conflicts,
modes, source edits, exact-head updates and stale runs; 24 focused tests pass.
- [x] Real Git object fixture reproduces the broken changeset and lockfile cases, then proves
the corrected squash tree preserves exact bytes, modes and consumed notes.
- [x] Temporary remote `release-proof` proved a verified two-parent merge under current rules
(`d50c260cbe6ff99393479f235add337c9d8d208d`); the owned diagnostic branch is removed.
- [x] Three council passes; valid findings reproduced and fixed. The final queue regression is
covered red-first using GitHub's documented multi-pending queue configuration.
- [x] Final `yarn check` and the explicit strict script typecheck pass.

Merge gate: exact-head PR CI must be green. A future version PR still requires the normal release
review; this workflow repair must not publish new runtime versions by itself.

Temporary, local-only evidence is in `/tmp/storage-canary-20260924.sr1781`. This is not a durable
artifact; use the PR's exact-head CI for the handoff. The separate production Storage canary and
Content/Convex gates are recorded in Content #6103's private living checklist, not this public repo.

https://github.com/transloadit/node-sdk/pull/509
https://github.com/transloadit/content/pull/6103
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"parity:transloadit": "node scripts/prepare-transloadit.ts && node scripts/fingerprint-pack.ts packages/transloadit --ignore-scripts --quiet --out /tmp/transloadit-after.json && node scripts/verify-fingerprint.ts --current /tmp/transloadit-after.json --diff",
"test:img:fixture": "node scripts/test-img-next-fixture.ts",
"test:sdk:edge": "node scripts/test-sdk-edge.ts",
"test:unit": "vitest run ./scripts/withProcess.test.ts ./scripts/img-next-fixture.test.ts ./scripts/knip.test.ts ./scripts/publish-release.test.ts ./scripts/sdk-edge.test.ts && yarn workspace @transloadit/utils test:unit && yarn workspace @transloadit/viewer test:unit && yarn workspace @transloadit/node test:unit && yarn workspace @transloadit/mcp-server test:unit && yarn workspace @transloadit/types test:unit && yarn workspace @transloadit/zod test:unit && yarn workspace @transloadit/notify-url-relay test:unit",
"test:unit": "vitest run ./scripts/withProcess.test.ts ./scripts/img-next-fixture.test.ts ./scripts/knip.test.ts ./scripts/publish-release.test.ts ./scripts/version-release.test.ts ./scripts/sdk-edge.test.ts && yarn workspace @transloadit/utils test:unit && yarn workspace @transloadit/viewer test:unit && yarn workspace @transloadit/node test:unit && yarn workspace @transloadit/mcp-server test:unit && yarn workspace @transloadit/types test:unit && yarn workspace @transloadit/zod test:unit && yarn workspace @transloadit/notify-url-relay test:unit",
"test:types": "yarn workspace @transloadit/zod test:types",
"test:e2e": "yarn workspace @transloadit/node test:e2e",
"test": "yarn workspace @transloadit/node test",
Expand Down
Loading
Loading