diff --git a/RELEASING.md b/RELEASING.md new file mode 100644 index 00000000..b2b497a6 --- /dev/null +++ b/RELEASING.md @@ -0,0 +1,112 @@ +# Releasing + +Packages are published by `.github/workflows/publish.yml` (`workflow_dispatch`, +inputs `releaseType` = `release` | `canary`, and `version`). It runs +`lerna publish` under **fixed versioning** — every package shares one version +(`lerna.json`) — and authenticates to npm via **OIDC trusted publishing**, so no +npm token is involved. Requires npm ≥ 11.5.1 (the workflow installs it). + +**Always canary first.** A full release moves the `latest` tag and tags/commits +git — there's no clean undo. Publish a canary, run the [smoke test](#smoke-test) +against it, and only cut the full release once it's green. + +## Canary + +For testing unreleased changes before a full release. Dispatch the workflow from +any branch (usually the feature branch you want to test): + +``` +releaseType = canary +version = +``` + +Runs `lerna publish --canary --force-publish`. Publishes `X.Y.Z-alpha.` +to the `canary` dist-tag. No git commit or tag is created — canaries are +disposable, so cut as many as you need. + +**Test it.** Scaffold a fresh project and point it at the canary, then run the +[smoke test](#smoke-test): + +``` +npm create @revideo@canary -- --default +cd my-revideo-project +npx npm-check-updates '/@revideo/' --target newest --install # or hand-edit deps to @canary +``` + +Note the exact canary version from the workflow log (e.g. `0.11.1-alpha.1187`) +and confirm the installed packages match it before testing — a stale cache can +otherwise mask the change you're verifying. + +## Full release + +`main` is protected (PR-only, `enforce_admins`, squash-only merges), so lerna +cannot push its version commit directly to `main`. Release from a dedicated +branch instead and merge back via PR. + +1. **Examples submodule.** `packages/create/examples` is a git submodule + (`midrender/examples`) whose example `package.json`s pin exact `@revideo/*` + versions. `@revideo/create` ships this directory in its npm tarball + (`files: ["index.js", "examples"]`), so scaffolded projects install whatever + these files pin. `lerna` does **not** touch the submodule (separate repo), so + bump it by hand or `npm create` installs the old versions. + + In `packages/create/examples`, branch off the previous release branch and + bump every pinned `@revideo/*` dep to `X.Y.Z`: + + ``` + git checkout -b release-X.Y.Z origin/release- + # every "0.10.4" in these files is an @revideo dep — safe to replace wholesale + perl -pi -e 's/""/"X.Y.Z"/g' $(git ls-files '*package.json') + git commit -am "chore: bump @revideo deps to X.Y.Z" && git push -u origin release-X.Y.Z + ``` + + Note the resulting commit SHA — the superproject pointer is updated to it in + the next step. + +2. **Prep commit** — branch `release-X.Y.Z` off `main`, one commit: + + - submodule pointer `packages/create/examples` → the new examples commit + - `packages/cli/src/index.ts` → `const VERSION = 'X.Y.Z'` + + (`lerna` bumps every `package.json` + the lockfile itself; only these two + hand-maintained spots are left.) + +3. **Publish** — dispatch the workflow **from `release-X.Y.Z`**: + + ``` + releaseType = release + version = X.Y.Z + ``` + + Runs `lerna publish --force-publish --exact X.Y.Z`: versions all packages, + commits `ci(release): X.Y.Z`, tags `vX.Y.Z`, pushes to the release branch, + and publishes to the `latest` dist-tag with provenance. + +4. **Merge** — squash-merge `release-X.Y.Z` → `main`. The squash folds the prep + and `ci(release)` commits into one; `vX.Y.Z` still points at the published + commit on the release branch. + +Then run the [smoke test](#smoke-test) once more against `@latest` to confirm +the published release. + +## Smoke test + +Run against a canary before releasing, and against `@latest` after. Scaffold a +fresh project (`@canary` or `@latest` as appropriate) and check all three paths +end to end: + +``` +cd my-revideo-project +npm install # installs cleanly, deps resolve to the version under test +npm run render # → output/video.mp4 (non-empty, a few seconds long) +npm start # editor dev server boots +``` + +- **Install** — `npm ls @revideo/core` reports the expected version, no + peer/resolution errors. +- **Render** — `npm run render` exits 0 and writes a playable + `output/video.mp4`. This is the headless path (Puppeteer + Vite + ffmpeg); a + hang here means editor-only code leaked into the render bundle. +- **Editor** — `npm start` serves the editor (`http://localhost:9000`, HTTP 200) + and the per-scene plugins resolve (`/@id/@revideo/2d/editor` → HTTP 200). The + footer should show the version under test. diff --git a/package-lock.json b/package-lock.json index ad5749da..46cbdf42 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4172,6 +4172,7 @@ "hasInstallScript": true, "license": "MIT", "optional": true, + "peer": true, "dependencies": { "detect-libc": "^2.0.3", "is-glob": "^4.0.3", @@ -4213,6 +4214,7 @@ "os": [ "android" ], + "peer": true, "engines": { "node": ">= 10.0.0" }, @@ -4233,6 +4235,7 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": ">= 10.0.0" }, @@ -4253,6 +4256,7 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": ">= 10.0.0" }, @@ -4273,6 +4277,7 @@ "os": [ "freebsd" ], + "peer": true, "engines": { "node": ">= 10.0.0" }, @@ -4293,6 +4298,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 10.0.0" }, @@ -4313,6 +4319,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 10.0.0" }, @@ -4333,6 +4340,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 10.0.0" }, @@ -4353,6 +4361,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 10.0.0" }, @@ -4373,6 +4382,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 10.0.0" }, @@ -4393,6 +4403,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 10.0.0" }, @@ -4413,6 +4424,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">= 10.0.0" }, @@ -4433,6 +4445,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">= 10.0.0" }, @@ -4453,6 +4466,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">= 10.0.0" }, @@ -12589,6 +12603,37 @@ "url": "https://ko-fi.com/dangreen" } }, + "node_modules/git-raw-commits/node_modules/conventional-commits-filter": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-5.0.0.tgz", + "integrity": "sha512-tQMagCOC59EVgNZcC5zl7XqO30Wki9i9J3acbUvkaosCT6JX3EeFwJD7Qqp4MCikRnzS18WXV3BLIQ66ytu6+Q==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/git-raw-commits/node_modules/conventional-commits-parser": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-6.4.0.tgz", + "integrity": "sha512-tvRg7FIBNlyPzjdG8wWRlPHQJJHI7DylhtRGeU9Lq+JuoPh5BKpPRX83ZdLrvXuOSu5Eo/e7SzOQhU4Hd2Miuw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@simple-libs/stream-utils": "^1.2.0", + "meow": "^13.0.0" + }, + "bin": { + "conventional-commits-parser": "dist/cli/index.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/git-raw-commits/node_modules/meow": { "version": "13.2.0", "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", @@ -17722,7 +17767,8 @@ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", "license": "MIT", - "optional": true + "optional": true, + "peer": true }, "node_modules/node-gyp": { "version": "12.4.0",