Skip to content
Closed
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
63 changes: 38 additions & 25 deletions RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
Ship is **not done** until:

- npm is published
- GitHub Release has the Bun tarball asset
- GitHub Release has the macOS arm64 Bun tarball
- GitHub Release has the Linux x64 + arm64 Bun tarballs
- GitHub Release has the Chrome extension zip
- GitHub Release has the Firefox extension zip
- Homebrew tap is bumped + `brew install` verifies
- Homebrew tap is bumped + `brew install` verifies (macOS + Linux)

## Version sources (keep in sync)

Expand All @@ -32,9 +33,12 @@ Ship is **not done** until:
- `pnpm -s check`
- `pnpm -s build`

3. Build Bun artifact (prints sha256 + creates tarball)
- `pnpm -s build:bun:test`
- Artifact: `dist-bun/summarize-macos-arm64-v<ver>.tar.gz`
3. Build Bun artifacts for all platforms (cross-compiles Linux from macOS; prints sha256s)
- `pnpm -s build:bun:all:test`
- Artifacts in `dist-bun/`:
- `summarize-macos-arm64-v<ver>.tar.gz` (built + smoke-tested natively)
- `summarize-linux-x64-v<ver>.tar.gz` (cross-compiled)
- `summarize-linux-arm64-v<ver>.tar.gz` (cross-compiled)

4. Build Chrome extension artifact
- `pnpm -C apps/chrome-extension build`
Expand Down Expand Up @@ -74,6 +78,8 @@ Ship is **not done** until:

gh release create "v${ver}" \
"dist-bun/summarize-macos-arm64-v${ver}.tar.gz" \
"dist-bun/summarize-linux-x64-v${ver}.tar.gz" \
"dist-bun/summarize-linux-arm64-v${ver}.tar.gz" \
"dist-chrome/summarize-chrome-extension-v${ver}.zip" \
"dist-firefox/summarize-firefox-extension-v${ver}.zip" \
--title "v${ver}" \
Expand All @@ -84,11 +90,17 @@ Ship is **not done** until:

8. Homebrew tap bump + verify
- Repo: `~/Projects/homebrew-tap`
- Update `Formula/summarize.rb`:
- `url` → GitHub Release asset URL
- `sha256` → from `pnpm build:bun:test`
- `version` + test expectation
- `git commit -am "chore: bump summarize to <ver>" && git push`
- Run the helper (downloads all platform tarballs, computes sha256s, rewrites the formula):
```bash
bash scripts/release.sh tap
```
- The formula uses `on_macos`/`on_linux` blocks covering arm64 + linux-x64 + linux-arm64.
- Commit and push:
```bash
ver="$(node -p 'require("./package.json").version')"
git -C ~/Projects/homebrew-tap commit -am "chore: bump summarize to v${ver}" && \
git -C ~/Projects/homebrew-tap push
```
- Verify:
```bash
brew uninstall summarize || true
Expand Down Expand Up @@ -128,35 +140,36 @@ Helper (npm-only): `scripts/release.sh` (phases: `gates|build|publish|smoke|tag|

Goal:

- Build a **macOS arm64** Bun binary named `summarize`
- Package as `dist-bun/summarize-macos-arm64-v<ver>.tar.gz`
- Upload tarball as a GitHub Release asset
- Point Homebrew formula at that asset + sha256
- Build platform-native Bun binaries named `summarize` for all supported platforms
- Package as `dist-bun/summarize-<platform>-v<ver>.tar.gz`
- Supported platforms: `macos-arm64`, `linux-x64`, `linux-arm64` (all cross-compiled from macOS in one step)
- Upload tarballs as GitHub Release assets
- Point Homebrew formula `on_macos`/`on_linux` blocks at the correct asset + sha256
- Formula should install the compiled `summarize` binary directly (no Bun wrapper script).

1. Build the Bun artifact
- `pnpm build:bun`
- This uses `bun build --compile --bytecode` and prints the tarball sha256.
1. Build all Bun artifacts
- `pnpm build:bun:all:test`
- Builds macOS arm64 natively (with smoke test) and cross-compiles both Linux targets.
- Prints sha256 for each tarball.

2. Smoke test locally (before uploading)
2. Smoke test the native binary (already done by `--test` above; optionally run a real summary)
- `dist-bun/summarize --version`
- `dist-bun/summarize --help`
- Optional: run one real file/link summary.

3. GitHub Release (when approved)
- Create a release for tag `v<ver>` with clean notes (no duplicated version header inside the notes body):
- Prefer `--title "v<ver>"` and `--notes-file …` (avoid pasting text with escaped `\\n`)
- Notes should start with sections like `### Changes`, not `## v<ver>` (the release already has a title)
- Upload `dist-bun/summarize-macos-arm64-v<ver>.tar.gz`
- Upload all three tarballs alongside the extension zips (see fast path step 7).
- Verify notes render correctly:
- `gh release view v<ver> --json body --jq .body` (should show real newlines, not literal `\\n`)

4. Homebrew tap update (when approved + after asset is live)
4. Homebrew tap update (when approved + after assets are live)
- Repo: `~/Projects/homebrew-tap`
- Add/update `Formula/summarize.rb`:
- `url` = GitHub Release asset URL
- `sha256` = from step (1)
- `version` = `<ver>`
- Run the helper (downloads all three tarballs, computes sha256s, rewrites the formula):
```bash
bash scripts/release.sh tap
```

5. Homebrew verification (after formula update)
```bash
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
"build": "pnpm clean && pnpm -C packages/core build && pnpm build:lib && pnpm build:cli",
"build:bun": "bun scripts/build-bun.js",
"build:bun:test": "bun scripts/build-bun.js --test",
"build:bun:all": "bun scripts/build-bun.js --all",
"build:bun:all:test": "bun scripts/build-bun.js --all --test",
"build:cli": "node scripts/build-cli.mjs",
"build:lib": "tsc -p tsconfig.build.json",
"check": "pnpm format:check && pnpm lint && pnpm test:coverage",
Expand Down
108 changes: 98 additions & 10 deletions scripts/build-bun.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,15 @@ function chmodX(path) {
run("chmod", ["+x", path]);
}

function sha256(filePath) {
// shasum on macOS/BSD, sha256sum on Linux
if (process.platform === "darwin") {
run("shasum", ["-a", "256", filePath]);
} else {
run("sha256sum", [filePath]);
}
}

function buildOne({ target, outName, version, gitSha }) {
const outPath = join(distDir, outName);
console.log(`\n🔨 Building ${outName} (target=${target}, bytecode)…`);
Expand Down Expand Up @@ -102,22 +111,54 @@ function buildOne({ target, outName, version, gitSha }) {
return outPath;
}

function buildMacosArm64({ version }) {
function buildPlatform({ bunTarget, tarName, version }) {
const gitSha = readGitSha();
const outPath = buildOne({ target: "bun-darwin-arm64", outName: "summarize", version, gitSha });
const outPath = buildOne({ target: bunTarget, outName: "summarize", version, gitSha });
chmodX(outPath);

const tarName = `summarize-macos-arm64-v${version}.tar.gz`;
const tarPath = join(distDir, tarName);
console.log("\n📦 Packaging tarball…");
run("tar", ["-czf", tarPath, "-C", distDir, "summarize"]);

console.log("\n🔐 sha256:");
run("shasum", ["-a", "256", tarPath]);
sha256(tarPath);

return { binary: outPath, tarPath };
}

function buildMacosArm64({ version }) {
return buildPlatform({
bunTarget: "bun-darwin-arm64",
tarName: `summarize-macos-arm64-v${version}.tar.gz`,
version,
});
}

function buildLinuxX64({ version }) {
return buildPlatform({
bunTarget: "bun-linux-x64",
tarName: `summarize-linux-x64-v${version}.tar.gz`,
version,
});
}

function buildLinuxArm64({ version }) {
return buildPlatform({
bunTarget: "bun-linux-arm64",
tarName: `summarize-linux-arm64-v${version}.tar.gz`,
version,
});
}

// Returns the platform string matching the current host.
function detectNativePlatform() {
const { platform, arch } = process;
if (platform === "darwin" && arch === "arm64") return "macos-arm64";
if (platform === "linux" && arch === "x64") return "linux-x64";
if (platform === "linux" && arch === "arm64") return "linux-arm64";
return null;
}

async function runE2E(binary) {
if (!globalThis.Bun?.serve) {
throw new Error("Bun runtime missing; run with bun.");
Expand Down Expand Up @@ -171,18 +212,65 @@ async function main() {
console.log("========================");

const version = readPackageVersion();
const args = process.argv.slice(2);
const runTests = args.includes("--test");
const buildAll = args.includes("--all");

// --platform <name> overrides auto-detection; --all builds every platform.
const platformFlagIdx = args.indexOf("--platform");
const explicitPlatform = platformFlagIdx !== -1 ? args[platformFlagIdx + 1] : null;

const nativePlatform = detectNativePlatform();
const targetPlatform = buildAll ? "all" : (explicitPlatform ?? nativePlatform);

if (!targetPlatform) {
throw new Error(
`Unsupported host ${process.platform}/${process.arch}. Use --platform <macos-arm64|linux-x64|linux-arm64> or --all.`,
);
}

if (!existsSync(distDir)) {
mkdirSync(distDir, { recursive: true });
}

const { binary } = buildMacosArm64({ version });
const platforms = targetPlatform === "all"
? ["macos-arm64", "linux-x64", "linux-arm64"]
: [targetPlatform];

if (process.argv.includes("--test")) {
console.log("\n🧪 Smoke…");
run(binary, ["--version"]);
run(binary, ["--help"]);
await runE2E(binary);
// When building all platforms, put the native one last so that
// dist-bun/summarize ends up as the locally runnable binary for smoke tests.
if (nativePlatform && platforms.length > 1) {
const idx = platforms.indexOf(nativePlatform);
if (idx > -1) {
platforms.splice(idx, 1);
platforms.push(nativePlatform);
}
}

const builders = {
"macos-arm64": buildMacosArm64,
"linux-x64": buildLinuxX64,
"linux-arm64": buildLinuxArm64,
};

let nativeBinary = null;
for (const plat of platforms) {
const build = builders[plat];
if (!build) throw new Error(`Unknown platform: ${plat}`);
const { binary } = build({ version });
if (plat === nativePlatform) nativeBinary = binary;
}

if (runTests) {
const testBinary = nativeBinary ?? (platforms.length === 1 ? join(distDir, "summarize") : null);
if (!testBinary) {
console.warn("⚠️ Skipping tests: no native platform binary built (cross-compile only).");
} else {
console.log("\n🧪 Smoke…");
run(testBinary, ["--version"]);
run(testBinary, ["--help"]);
await runE2E(testBinary);
}
}

console.log(`\n✨ Done. dist: ${distDir}`);
Expand Down
Loading