005d7e0a3f
## What Adds [pkg.pr.new](https://pkg.pr.new) continuous preview releases. Every push to a branch builds the public `@trigger.dev/*` packages and publishes installable preview builds keyed by commit SHA — **without touching the npm registry**. pkg.pr.new drops install instructions on the associated PR: ``` npm i https://pkg.pr.new/@trigger.dev/sdk@<sha> ``` This lets reviewers and users try a branch (SDK, CLI, core, etc.) before anything is released, separate from the changesets release, the manual `--snapshot` prerelease, and the chat-prerelease flow. ## How `.github/workflows/preview-packages.yml` (push trigger) → install → generate Prisma → **stamp preview version** → build → `pkg-pr-new publish`. ### The version stamp (the important part) pkg.pr.new serves previews by SHA but does **not** rewrite the package.json `version` field. If a preview shipped as `4.5.0-rc.4`, a consumer who installed it would pin `4.5.0-rc.4` to the preview tarball in their lockfile/cache — and a later `npm i @trigger.dev/sdk@4.5.0-rc.4` from npm could resolve to the stale preview. This is a known, by-design gap in the tool (stackblitz-labs/pkg.pr.new#250, #390). `scripts/stamp-preview-version.mjs` runs **before the build** and rewrites every public package to a unique `0.0.0-preview-<sha>`. The `0.0.0-` prefix can never satisfy a real semver range, so the collision is structurally impossible (same convention React/Next canaries use). Running before the build also means `scripts/updateVersion.ts` bakes the preview version into the runtime `VERSION` constant, so previews are self-identifying (`trigger --version`, the `x-trigger-cli-version` header, the MCP server version) instead of all reporting the RC version. Sibling `workspace:` specifiers are relaxed to `workspace:*` so `pnpm pack` resolves them against the rewritten versions — `packages/python` pins peerDependencies as `workspace:^4.5.0-rc.4`, which would otherwise be unsatisfiable once the version changes. Non-public deps (`@trigger.dev/database`, `@internal/*`) are left untouched. All mutations happen on the ephemeral CI checkout; nothing is committed. ## GitHub App The pkg.pr.new GitHub App is **already installed** on `triggerdotdev/trigger.dev` (has been for a while), so no setup is needed. Confirmed live — this branch's pushes published all 10 public packages, e.g. ``` pnpm add https://pkg.pr.new/@trigger.dev/sdk@e4dfc59 ``` ## Fork limitation pkg.pr.new authenticates with a GitHub Actions OIDC token, which GitHub does not issue to `pull_request` workflows from forks. The `push` trigger therefore covers branches pushed to this repo (core team), not external fork PRs. Fork coverage would need a `workflow_run` two-stage setup; left out for now. ## Notes - Pinned `pkg-pr-new@0.0.75` (no Node engine constraint; Node 20 CI is fine). - pkg.pr.new [#525](https://github.com/stackblitz-labs/pkg.pr.new/pull/525) adds a built-in `--previewVersion` flag (still open). If it lands we can drop the version-rewrite half of the script, but we'd keep a pre-build stamp anyway so `updateVersion.ts` picks up the preview version (the flag rewrites at pack time, too late for the baked `VERSION`).
93 lines
3.6 KiB
JavaScript
93 lines
3.6 KiB
JavaScript
// Rewrites the version of every public packages/* package to a unique,
|
|
// preview-only semver (0.0.0-preview-<sha>) BEFORE the build runs.
|
|
//
|
|
// Why this exists:
|
|
// pkg.pr.new serves preview builds by commit SHA but does NOT change the
|
|
// package.json "version" field (as of 0.0.75). If a preview is published
|
|
// while package.json still says e.g. 4.5.0-rc.4, a consumer who installs the
|
|
// preview pins 4.5.0-rc.4 to the pkg.pr.new tarball in their lockfile/cache,
|
|
// and a later `npm i @trigger.dev/sdk@4.5.0-rc.4` from npm can resolve to the
|
|
// stale preview. See stackblitz-labs/pkg.pr.new#250 and #390.
|
|
//
|
|
// A 0.0.0- prefix can never satisfy a real semver range, so the collision
|
|
// becomes structurally impossible (the same convention React/Next canaries
|
|
// use).
|
|
//
|
|
// Running BEFORE the build also means scripts/updateVersion.ts bakes this
|
|
// same preview version into the runtime VERSION constant, so previews are
|
|
// self-identifying (trigger --version, the x-trigger-cli-version header, the
|
|
// MCP server version, etc.) rather than all reporting the RC version.
|
|
//
|
|
// Sibling workspace: specifiers are relaxed to workspace:* so `pnpm pack`
|
|
// resolves them against the rewritten versions without range-validation
|
|
// errors. packages/python pins peerDependencies as workspace:^4.5.0-rc.4,
|
|
// which would otherwise be unsatisfiable once the sibling version changes.
|
|
//
|
|
// Note: pkg.pr.new PR #525 adds a built-in --previewVersion flag. Once that
|
|
// ships we could drop the version-rewrite half here, but we still want a
|
|
// pre-build stamp so updateVersion.ts picks up the preview version (the flag
|
|
// rewrites at pack time, which is too late for the baked VERSION constant).
|
|
|
|
import { readdirSync, readFileSync, writeFileSync, existsSync } from "node:fs";
|
|
import { join } from "node:path";
|
|
import { execSync } from "node:child_process";
|
|
|
|
const PACKAGES_DIR = "packages";
|
|
const DEP_SECTIONS = [
|
|
"dependencies",
|
|
"devDependencies",
|
|
"peerDependencies",
|
|
"optionalDependencies",
|
|
];
|
|
|
|
function resolveSha() {
|
|
const sha = process.argv[2] || process.env.GITHUB_SHA;
|
|
if (sha) return sha;
|
|
try {
|
|
return execSync("git rev-parse HEAD", { encoding: "utf8" }).trim();
|
|
} catch {
|
|
throw new Error(
|
|
"Could not determine commit SHA (pass as the first argument or set GITHUB_SHA)"
|
|
);
|
|
}
|
|
}
|
|
|
|
const sha = resolveSha().slice(0, 7);
|
|
const previewVersion = `0.0.0-preview-${sha}`;
|
|
|
|
const dirs = readdirSync(PACKAGES_DIR, { withFileTypes: true }).filter((e) =>
|
|
e.isDirectory()
|
|
);
|
|
|
|
// First pass: collect every public package name so we know which workspace
|
|
// specifiers point at a sibling whose version we are about to change.
|
|
const publicNames = new Set();
|
|
const manifests = [];
|
|
for (const dir of dirs) {
|
|
const pkgPath = join(PACKAGES_DIR, dir.name, "package.json");
|
|
if (!existsSync(pkgPath)) continue;
|
|
const json = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
manifests.push({ pkgPath, json });
|
|
if (!json.private && json.name) publicNames.add(json.name);
|
|
}
|
|
|
|
// Second pass: stamp the version and relax sibling workspace specifiers.
|
|
let stamped = 0;
|
|
for (const { pkgPath, json } of manifests) {
|
|
if (json.private) continue;
|
|
json.version = previewVersion;
|
|
for (const section of DEP_SECTIONS) {
|
|
const deps = json[section];
|
|
if (!deps) continue;
|
|
for (const [name, spec] of Object.entries(deps)) {
|
|
if (publicNames.has(name) && String(spec).startsWith("workspace:")) {
|
|
deps[name] = "workspace:*";
|
|
}
|
|
}
|
|
}
|
|
writeFileSync(pkgPath, `${JSON.stringify(json, null, 2)}\n`);
|
|
stamped++;
|
|
}
|
|
|
|
console.log(`Stamped ${stamped} public package(s) to ${previewVersion}`);
|