-
Notifications
You must be signed in to change notification settings - Fork 30
feat: ship per-arch standalone binaries on each release #73
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
ja-818
wants to merge
2
commits into
stripe:main
Choose a base branch
from
gethouston:add-binary-releases
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+209
−0
Open
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| name: Release binaries | ||
|
|
||
| # Triggers when a GitHub Release is published (changesets/action does this on | ||
| # every npm publish). Builds standalone single-file binaries for every Stripe | ||
| # Link CLI target and attaches them, plus a manifest.json with sha256s, to | ||
| # the same release. Downstream consumers (Houston, OpenClaw, etc.) pin against | ||
| # the manifest URL so they can bundle link-cli without an npm runtime. | ||
| # | ||
| # workflow_dispatch path is provided for manual rebuilds against a past tag. | ||
|
|
||
| on: | ||
| release: | ||
| types: [published] | ||
| workflow_dispatch: | ||
| inputs: | ||
| release_tag: | ||
| description: Existing release tag to attach binaries to (e.g. @stripe/link-cli@0.4.2) | ||
| required: true | ||
|
|
||
| permissions: | ||
| contents: write | ||
|
|
||
| jobs: | ||
| build: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Resolve release tag | ||
| id: tag | ||
| run: | | ||
| TAG="${{ github.event.release.tag_name || github.event.inputs.release_tag }}" | ||
| # Strip the @stripe/link-cli@ prefix changesets uses, leaving "0.4.2" | ||
| VERSION="${TAG##*@}" | ||
| echo "tag=$TAG" >> "$GITHUB_OUTPUT" | ||
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | ||
|
|
||
| - uses: actions/checkout@v4 | ||
| with: | ||
| ref: ${{ steps.tag.outputs.tag }} | ||
|
|
||
| - uses: oven-sh/setup-bun@v2 | ||
| with: | ||
| bun-version: '1.3.10' | ||
|
|
||
| - uses: pnpm/action-setup@v4 | ||
|
|
||
| - uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: 20 | ||
| cache: pnpm | ||
|
|
||
| - run: pnpm install --frozen-lockfile | ||
|
|
||
| - run: pnpm turbo run build | ||
|
|
||
| - name: Build binaries | ||
| run: | | ||
| for target in darwin-arm64 darwin-x64 linux-x64 windows-x64; do | ||
| bun run scripts/build-binary.ts "$target" | ||
| done | ||
|
|
||
| - name: Generate manifest | ||
| run: bun run scripts/generate-manifest.ts "${{ steps.tag.outputs.version }}" | ||
|
|
||
| - name: Attach binaries to release | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| run: | | ||
| gh release upload "${{ steps.tag.outputs.tag }}" \ | ||
| dist-bin/link-cli-darwin-arm64 \ | ||
| dist-bin/link-cli-darwin-x64 \ | ||
| dist-bin/link-cli-linux-x64 \ | ||
| dist-bin/link-cli-windows-x64.exe \ | ||
| dist-bin/manifest.json \ | ||
| --clobber | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,6 @@ | ||
| node_modules/ | ||
| dist/ | ||
| dist-bin/ | ||
| *.log | ||
| *.tgz | ||
| .DS_Store | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| #!/usr/bin/env bun | ||
| /** | ||
| * Build a single-file standalone link-cli binary for the requested target. | ||
| * | ||
| * Usage: | ||
| * bun run scripts/build-binary.ts <target> | ||
| * | ||
| * Targets: darwin-arm64 | darwin-x64 | linux-x64 | windows-x64 | ||
| * | ||
| * Output is written to dist-bin/link-cli-<target>[.exe]. | ||
| * | ||
| * The bundle entrypoint is the same packages/cli/dist/cli.js that tsup emits, | ||
| * so this script must run AFTER `pnpm turbo run build`. | ||
| * | ||
| * Two of ink's transitive dependencies are stubbed at bundle time: | ||
| * - react-devtools-core: only used when DEV=true; ink uses a dynamic import, | ||
| * but tsup bundles the static reference inside ink/build/devtools.js, which | ||
| * then breaks compile if the optional dep is not installed. | ||
| * - update-notifier: marked external in tsup config; replaced with a noop | ||
| * so the standalone binary does not need the on-disk package present. | ||
| */ | ||
| import { mkdirSync } from 'node:fs'; | ||
| import type { BunPlugin } from 'bun'; | ||
|
|
||
| const TARGETS = { | ||
| 'darwin-arm64': 'bun-darwin-arm64', | ||
| 'darwin-x64': 'bun-darwin-x64', | ||
| 'linux-x64': 'bun-linux-x64', | ||
| 'windows-x64': 'bun-windows-x64', | ||
| } as const; | ||
|
|
||
| type Target = keyof typeof TARGETS; | ||
|
|
||
| const stubPlugin: BunPlugin = { | ||
| name: 'stub-optional-deps', | ||
| setup(build) { | ||
| build.onResolve( | ||
| { filter: /^(react-devtools-core|update-notifier)$/ }, | ||
| (args) => ({ path: args.path, namespace: 'stub' }), | ||
| ); | ||
| build.onLoad({ filter: /.*/, namespace: 'stub' }, (args) => { | ||
| if (args.path === 'react-devtools-core') { | ||
| return { | ||
| contents: 'export default { connectToDevTools() {} };', | ||
| loader: 'js', | ||
| }; | ||
| } | ||
| return { | ||
| contents: | ||
| 'const noop = () => ({ notify: () => {} }); export default noop;', | ||
| loader: 'js', | ||
| }; | ||
| }); | ||
| }, | ||
| }; | ||
|
|
||
| const target = (process.argv[2] ?? 'darwin-arm64') as Target; | ||
| const flag = TARGETS[target]; | ||
| if (!flag) { | ||
| console.error(`Unknown target: ${target}`); | ||
| console.error(`Valid targets: ${Object.keys(TARGETS).join(', ')}`); | ||
| process.exit(1); | ||
| } | ||
|
|
||
| mkdirSync('dist-bin', { recursive: true }); | ||
|
|
||
| const ext = target.startsWith('windows') ? '.exe' : ''; | ||
| const outfile = `./dist-bin/link-cli-${target}${ext}`; | ||
|
|
||
| console.log(`Building ${flag} -> ${outfile}`); | ||
|
|
||
| await Bun.build({ | ||
| entrypoints: ['./packages/cli/dist/cli.js'], | ||
| // @ts-expect-error compile is a Bun.build option but not in the public types yet | ||
| compile: { target: flag, outfile }, | ||
| plugins: [stubPlugin], | ||
| minify: true, | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| #!/usr/bin/env bun | ||
| /** | ||
| * Emit dist-bin/manifest.json with sha256 checksums + download URLs for every | ||
| * binary in dist-bin/. Consumed by release-binaries.yml after the binaries are | ||
| * built. Downstream consumers (Houston, etc.) pin against this manifest. | ||
| * | ||
| * Usage: | ||
| * bun run scripts/generate-manifest.ts <version> | ||
| */ | ||
| import { createHash } from 'node:crypto'; | ||
| import { readFileSync, readdirSync, writeFileSync } from 'node:fs'; | ||
| import { join } from 'node:path'; | ||
|
|
||
| const version = process.argv[2]; | ||
| if (!version) { | ||
| console.error('Usage: bun run scripts/generate-manifest.ts <version>'); | ||
| process.exit(1); | ||
| } | ||
|
|
||
| const baseUrl = `https://github.com/stripe/link-cli/releases/download/${version}`; | ||
| const distDir = 'dist-bin'; | ||
| const files = readdirSync(distDir).filter((f) => f.startsWith('link-cli-')); | ||
|
|
||
| const binaries: Record<string, { file: string; sha256: string; url: string }> = | ||
| {}; | ||
|
|
||
| for (const file of files) { | ||
| const target = file.replace(/^link-cli-/, '').replace(/\.exe$/, ''); | ||
| const buf = readFileSync(join(distDir, file)); | ||
| const sha256 = createHash('sha256').update(buf).digest('hex'); | ||
| binaries[target] = { file, sha256, url: `${baseUrl}/${file}` }; | ||
| } | ||
|
|
||
| const manifest = { | ||
| version, | ||
| generated_at: new Date().toISOString(), | ||
| binaries, | ||
| }; | ||
|
|
||
| writeFileSync( | ||
| join(distDir, 'manifest.json'), | ||
| `${JSON.stringify(manifest, null, 2)}\n`, | ||
| ); | ||
| console.log(JSON.stringify(manifest, null, 2)); |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
doesn't this script need two params?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch — second arg was an optional URL override but nothing ever passed it, so it was dead flexibility. Removed in 3ee1633: script now takes one required version arg, with the GitHub releases URL hardcoded inline.