You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/prompt-context/building-a-release-pipeline.md
+27-27Lines changed: 27 additions & 27 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,26 +1,26 @@
1
1
# Automating Release Pipelines for Node, Deno, and Bun Projects
2
2
3
-
Successfully automating releases for diverse Javascript and Typescript projects requires a unified yet flexible approach. We need to **detect each project’s runtime and type**, then run the appropriate build and publish steps. Below, we outline how to identify the project type from config files, update configurations if needed, and implement a **GitHub Actions** workflow with accompanying scripts to handle library publishing, CLI distribution, and compiled binary releases for **Node.js**, **Deno**, and **Bun**.
3
+
Successfully automating releases for diverse Javascript and Typescript projects requires a unified yet flexible approach. We need to **detect each project's runtime and type**, then run the appropriate build and publish steps. Below, we outline how to identify the project type from config files, update configurations if needed, and implement a **GitHub Actions** workflow with accompanying scripts to handle library publishing, CLI distribution, and compiled binary releases for **Node.js**, **Deno**, and **Bun**.
4
4
5
5
## 1. Detecting Runtime and Release Type
6
6
7
7
Each project contains manifest files that signal its runtime and intended usage:
8
8
9
-
***Node.js projects**: Indicated by a `package.json`. We further check if it’s a CLI tool by looking for a `"bin"` field mapping command names to script files (e.g. `"bin": {"mytool": "src/cli.js"}`). Presence of `"bin"` means the package is a CLI tool (executables on install); absence suggests a library module.
9
+
***Node.js projects**: Indicated by a `package.json`. We further check if it's a CLI tool by looking for a `"bin"` field mapping command names to script files (e.g. `"bin": {"mytool": "src/cli.js"}`). Presence of `"bin"` means the package is a CLI tool (executables on install); absence suggests a library module.
10
10
***Bun projects**: Usually have a `bun.json`/`bunfig.toml` (in addition to or instead of `package.json`). Bun is largely Node-compatible, but a `bun.json` hints the project is optimized for Bun. If Bun config defines an executable entry (or the Node `"bin"` field is present), treat it as a CLI; otherwise a library.
11
-
***Deno projects**: Identified by a `deno.json`/`deno.jsonc`. Deno packages must have a `name`, `version`, and `exports` in this file for publishing. Deno doesn’t use a separate `"bin"` field; a Deno CLI vs library is determined by intent:
11
+
***Deno projects**: Identified by a `deno.json`/`deno.jsonc`. Deno packages must have a `name`, `version`, and `exports` in this file for publishing. Deno doesn't use a separate `"bin"` field; a Deno CLI vs library is determined by intent:
12
12
13
-
* If the code’s primary use is to be run (e.g. has a `main.ts` or similar that parses arguments or is referenced in documentation as a command), treat as a CLI tool.
14
-
* Otherwise, if it’s meant to be imported (e.g. provides functions/types), treat as a library.
15
-
***Compiled Binary**: Some CLI projects also require a **compiled binary** release (single-file executables) for convenience (no runtime needed). By default, we will produce binaries for CLI tools in Node and Bun (to run without Node/Bun installed), and optionally for Deno CLIs if needed for users without Deno. If it’s ambiguous (e.g. a Deno CLI might be fine with just `deno install`), we’d confirm with maintainers whether to provide a binary.
13
+
* If the code's primary use is to be run (e.g. has a `main.ts` or similar that parses arguments or is referenced in documentation as a command), treat as a CLI tool.
14
+
* Otherwise, if it's meant to be imported (e.g. provides functions/types), treat as a library.
15
+
***Compiled Binary**: Some CLI projects also require a **compiled binary** release (single-file executables) for convenience (no runtime needed). By default, we will produce binaries for CLI tools in Node and Bun (to run without Node/Bun installed), and optionally for Deno CLIs if needed for users without Deno. If it's ambiguous (e.g. a Deno CLI might be fine with just `deno install`), we'd confirm with maintainers whether to provide a binary.
16
16
17
17
**Note:** If multiple configs exist (e.g. both `package.json` and `deno.json`), the project may target both Node (NPM) and Deno (JSR). In such cases, we can publish to both registries. If the intended release registry is unclear, we should ask the user to specify their preference (NPM vs JSR, or both).
18
18
19
19
## 2. Configuration Updates for Release Readiness
20
20
21
-
Before writing the pipeline, ensure each project’s config is set up for smooth publishing:
21
+
Before writing the pipeline, ensure each project's config is set up for smooth publishing:
22
22
23
-
***Node (and Bun) packages**: The `package.json` should have correct `name` (unique in npm), `version` (matching the tag to be released), and if it’s a library, an appropriate `"main"` or `"exports"` field for module entry. For TypeScript, include `"types"` pointing to the type definitions. If it’s a CLI, add a `"bin"` field mapping the command name to the compiled output or entry script. Also, set `"files"` or `.npmignore` to include built artifacts (and exclude source if not needed) so that `npm publish` packages the right files. For example, a Node CLI might have:
23
+
***Node (and Bun) packages**: The `package.json` should have correct `name` (unique in npm), `version` (matching the tag to be released), and if it's a library, an appropriate `"main"` or `"exports"` field for module entry. For TypeScript, include `"types"` pointing to the type definitions. If it's a CLI, add a `"bin"` field mapping the command name to the compiled output or entry script. Also, set `"files"` or `.npmignore` to include built artifacts (and exclude source if not needed) so that `npm publish` packages the right files. For example, a Node CLI might have:
24
24
25
25
```json
26
26
{
@@ -46,7 +46,7 @@ Before writing the pipeline, ensure each project’s config is set up for smooth
46
46
47
47
(The `"entry"` above is not official; the important field is `"exports"` for JSR).
48
48
49
-
***Bun projects**: Bun uses `package.json` for publishing (via `bun publish` to NPM), so ensure that’s updated. If a `bunfig.toml` or `bun.json` exists, verify it doesn’t conflict with package.json for name/version. For binaries, Bun will automatically append “.exe” on Windows builds, so our naming conventions should account for that.
49
+
***Bun projects**: Bun uses `package.json` for publishing (via `bun publish` to NPM), so ensure that's updated. If a `bunfig.toml` or `bun.json` exists, verify it doesn't conflict with package.json for name/version. For binaries, Bun will automatically append ".exe" on Windows builds, so our naming conventions should account for that.
50
50
51
51
With configs in place, we can proceed to automation.
52
52
@@ -55,31 +55,31 @@ With configs in place, we can proceed to automation.
55
55
We create a single workflow `.github/workflows/release.yml` that triggers on pushing a version tag. It will detect the project type and run the appropriate jobs:
56
56
57
57
***Trigger**: Only on new tags that look like version (e.g. `v*`) pushed to the main or master branch.
58
-
***Environment Matrix**: We may use a job matrix for building binaries on multiple OS, or use cross-compilation tools. Here we’ll demonstrate cross-compiling where possible to keep a single job, but note that Node’s official SEA feature might require per-OS jobs. We’ll use **Vercel pkg** for Node to allow cross-builds from one job, since Node’s Single Executable Application is experimental and multi-step.
58
+
***Environment Matrix**: We may use a job matrix for building binaries on multiple OS, or use cross-compilation tools. Here we'll demonstrate cross-compiling where possible to keep a single job, but note that Node's official SEA feature might require per-OS jobs. We'll use **Vercel pkg** for Node to allow cross-builds from one job, since Node's Single Executable Application is experimental and multi-step.
59
59
***Steps**:
60
60
61
61
1.**Checkout code**.
62
62
2.**Set up languages** (Node, Deno, or Bun) depending on project:
63
63
64
64
* For Node: use `actions/setup-node@v3` to install appropriate Node version (and prepare npm auth).
65
65
* For Bun: use `oven-sh/setup-bun@v2`.
66
-
* For Deno: use `denoland/setup-deno@v1` to get Deno on runner.
66
+
* For Deno: use `denoland/setup-deno@v2` to get Deno on runner.
67
67
3.**Install Dependencies & Build**:
68
68
69
69
* If Node or Bun and TypeScript, run `npm ci`/`bun install`, then a build script (e.g. `npm run build`) to produce JS in `dist/`.
70
70
* If Deno, installation is not needed (Deno fetches deps on the fly), but we might run `deno check` or tests.
71
71
4.**Publish to Registry** (if library or CLI package):
72
72
73
-
* For Node/Bun: use `npm publish` (or `bun publish`). We configure `NPM_TOKEN` for auth. (Bun’s publish packs and pushes to npm just like npm publish).
74
-
* For Deno: run `deno publish --token=${{ secrets.JSR_TOKEN }}` to publish to JSR (assuming we’ve created the package on JSR beforehand).
73
+
* For Node/Bun: use `npm publish` (or `bun publish`). We configure `NPM_TOKEN` for auth. (Bun's publish packs and pushes to npm just like npm publish).
74
+
* For Deno: run `deno publish --token=${{ secrets.JSR_TOKEN }}` to publish to JSR (assuming we've created the package on JSR beforehand).
***Node CLI**: Use `npx pkg` to compile for Linux, Windows, macOS in one go. For example, `pkg -t node18-linux-x64,node18-win-x64,node18-macos-x64 .` will output executables for each target. (We choose a Node runtime target matching our project’s requirements, e.g., Node 18 or `latest` for latest LTS).
77
+
***Node CLI**: Use `npx pkg` to compile for Linux, Windows, macOS in one go. For example, `pkg -t node18-linux-x64,node18-win-x64,node18-macos-x64 .` will output executables for each target. (We choose a Node runtime target matching our project's requirements, e.g., Node 18 or `latest` for latest LTS).
78
78
***Deno CLI**: Use `deno compile`. We can cross-compile for all supported targets with `--target` flag (e.g., `deno compile -A --output mytool-linux --target x86_64-unknown-linux-gnu main.ts` and similarly for `*-windows-msvc` and `*-apple-darwin`). Deno will download the appropriate `denort` runtime for each target automatically.
79
79
***Bun CLI**: Use `bun build --compile` with `--target` for each platform. For example: `bun build --compile src/cli.ts --target=bun-linux-x64 --outfile mytool-linux` (and likewise `bun-windows-x64`, `bun-darwin-x64`, etc.). Bun supports cross-compiling via the `--target` option.
80
80
6.**Prepare Release Assets**:
81
81
82
-
* Rename the binaries with a clear convention: e.g. `mytool-linux`, `mytool-macos`, `mytool-windows.exe` (include `.exe` for Windows) for clarity. We might archive them (e.g. zip/tar) or attach raw; to keep it simple we’ll attach raw binaries and ensure the install script accounts for platform naming.
82
+
* Rename the binaries with a clear convention: e.g. `mytool-linux`, `mytool-macos`, `mytool-windows.exe` (include `.exe` for Windows) for clarity. We might archive them (e.g. zip/tar) or attach raw; to keep it simple we'll attach raw binaries and ensure the install script accounts for platform naming.
83
83
* Generate or update the **`install.sh`** script in the repository root. This script will let users install the latest release easily: it will detect OS and arch, fetch the appropriate binary from GitHub Releases, install it to `/usr/local/bin` (or another prefix), and print usage instructions. We write this script once (committed to the repo) and it always pulls the **latest** release by default. Optionally, allow specifying a version: if user passes an argument (version tag), the script uses that instead of latest.
84
84
7.**Create GitHub Release**:
85
85
@@ -114,7 +114,7 @@ jobs:
114
114
runs-on: ubuntu-latest
115
115
env:
116
116
NODE_VERSION: "18"# Node version for Node/Bun projects
117
-
DENO_VERSION: "1.x"# Deno version (if needed specific)
117
+
DENO_VERSION: "2.x"# Deno version (if needed specific)
118
118
steps:
119
119
- name: Checkout
120
120
uses: actions/checkout@v4
@@ -181,7 +181,7 @@ jobs:
181
181
182
182
- name: Setup Deno (if Deno)
183
183
if: env.RUNTIME == 'deno'
184
-
uses: denoland/setup-deno@v1
184
+
uses: denoland/setup-deno@v2
185
185
with:
186
186
deno-version: ${{ env.DENO_VERSION }}
187
187
@@ -307,7 +307,7 @@ fi
307
307
**Notes:**
308
308
309
309
* For **npm** publishing, we use `npm publish`. The `actions/setup-node` step in the workflow already created an `.npmrc` with the token, so `npm publish` will succeed. We include `--access public` for scoped packages.
310
-
* For **JSR (Deno)**, we use `deno publish`. If `JSR_TOKEN` is provided, we pass it (to avoid interactive login). Deno’s OIDC flow (tokenless from GitHub Actions using Sigstore) could be enabled by setting `permissions:id-token: write` and omitting the token, but using a token is straightforward.
310
+
* For **JSR (Deno)**, we use `deno publish`. If `JSR_TOKEN` is provided, we pass it (to avoid interactive login). Deno's OIDC flow (tokenless from GitHub Actions using Sigstore) could be enabled by setting `permissions:id-token: write` and omitting the token, but using a token is straightforward.
311
311
* For **Bun**, `bun publish` will pack and publish to the npm registry using the same credentials as npm (it respects `.npmrc` or environment).
312
312
313
313
### `bin/release.sh` – Building Binaries and Preparing Assets
@@ -411,14 +411,14 @@ fi
411
411
A few points about `bin/release.sh`:
412
412
413
413
* It finds a base `NAME` for the binaries, typically the package name without scope (e.g. `"@org/mytool"` -> `mytool`). This name will be used in the filenames.
414
-
***Node**: We install or use `pkg` to create binaries for all three platforms in one command. The script picks the entry point from package.json (`bin` or `main`). We rename outputs to a consistent scheme. (Using Node’s official SEA would involve a more complex process including `--experimental-sea-config` and `postject` injection – for simplicity and automation, we use `pkg` here.)
414
+
***Node**: We install or use `pkg` to create binaries for all three platforms in one command. The script picks the entry point from package.json (`bin` or `main`). We rename outputs to a consistent scheme. (Using Node's official SEA would involve a more complex process including `--experimental-sea-config` and `postject` injection – for simplicity and automation, we use `pkg` here.)
415
415
***Deno**: We compile 4 binaries: linux x64, macOS x64, macOS arm64, and Windows x64. This covers common platforms. (We could also build Linux arm64 if needed by adding `aarch64-unknown-linux-gnu`.)
416
-
***Bun**: We compile for Linux x64, Windows x64, and macOS x64. (Bun can also produce macOS arm64 if run on an ARM host or in cross-mode, but here we assume x64 build. We note that Bun’s `--target` has baseline vs modern variants; using default ensures broad compatibility.)
417
-
* The script creates an `install.sh` if one doesn’t exist. In practice, you’d create this script once manually with the correct `REPO` placeholder. We show it being generated for completeness. The script:
416
+
***Bun**: We compile for Linux x64, Windows x64, and macOS x64. (Bun can also produce macOS arm64 if run on an ARM host or in cross-mode, but here we assume x64 build. We note that Bun's `--target` has baseline vs modern variants; using default ensures broad compatibility.)
417
+
* The script creates an `install.sh` if one doesn't exist. In practice, you'd create this script once manually with the correct `REPO` placeholder. We show it being generated for completeness. The script:
418
418
419
-
* Fetches the latest release tag via GitHub’s API (unless a version argument is given).
419
+
* Fetches the latest release tag via GitHub's API (unless a version argument is given).
420
420
* Detects OS and sets the expected asset name (this simple version lumps all Linux under one and all macOS under one, assuming x64 works on ARM via Rosetta – one could refine to pick an ARM binary if available).
421
-
* Downloads the asset and installs it to `/usr/local/bin` (using `~/.local/bin` if the former isn’t accessible).
421
+
* Downloads the asset and installs it to `/usr/local/bin` (using `~/.local/bin` if the former isn't accessible).
422
422
* Makes the binary executable and prints a success message.
423
423
* For Windows, since this is a Bash script, it would typically be run in WSL or Git Bash. Windows users not using a UNIX shell can manually download the `.exe` or use a PowerShell equivalent script (not included here).
424
424
@@ -433,7 +433,7 @@ This will install the latest version of **mytool** on their system.
433
433
434
434
## 4. Summary of Benefits and Maintenance
435
435
436
-
By following this template, each project’s CI/CD pipeline will automatically:
436
+
By following this template, each project's CI/CD pipeline will automatically:
437
437
438
438
***Detect the runtime and project type** (Node library, Node CLI, Deno lib/CLI, Bun project, etc.) and run the correct build and release steps.
439
439
***Publish libraries** to the appropriate registry:
@@ -445,8 +445,8 @@ By following this template, each project’s CI/CD pipeline will automatically:
445
445
***npm/JSR** for package installation (e.g. `npm -g` or `deno add`), and/or
446
446
***GitHub Releases** with pre-built binaries for macOS, Linux, and Windows, plus an easy installer script (`curl | sh`).
447
447
***Trigger on tagged releases** ensuring that new versions are released only when you push a version tag (e.g. `v1.2.3`) to the main branch, aligning with Git flow.
448
-
***Isolate build logic** in version-controlled scripts (`/bin`) and configuration in `.github/workflows`, making it easy to update across many projects. There’s no magic – just standard tools as documented by Node, Deno, and Bun.
449
-
***Support all platforms**: We produced platform-specific binaries using each runtime’s official capability (Node’s packagers, Deno’s native compiler, Bun’s compiler) so users on Windows, Linux, or macOS are all covered. For example, Deno’s compiler can target all supported OSes from a single machine, and Bun’s `--target` can cross-compile executables as well. Node’s `pkg` can bundle for multiple platforms in one go.
450
-
***Minimal intervention**: once this is set up, maintainers only need to update version numbers and push tags. The workflow and scripts handle the rest automatically. If a project’s intent is unclear (e.g. a Deno project that could be both a library and an app), the detection logic can be refined or a one-time manual tweak can be made to the script for that repository.
448
+
***Isolate build logic** in version-controlled scripts (`/bin`) and configuration in `.github/workflows`, making it easy to update across many projects. There's no magic – just standard tools as documented by Node, Deno, and Bun.
449
+
***Support all platforms**: We produced platform-specific binaries using each runtime's official capability (Node's packagers, Deno's native compiler, Bun's compiler) so users on Windows, Linux, or macOS are all covered. For example, Deno's compiler can target all supported OSes from a single machine, and Bun's `--target` can cross-compile executables as well. Node's `pkg` can bundle for multiple platforms in one go.
450
+
***Minimal intervention**: once this is set up, maintainers only need to update version numbers and push tags. The workflow and scripts handle the rest automatically. If a project's intent is unclear (e.g. a Deno project that could be both a library and an app), the detection logic can be refined or a one-time manual tweak can be made to the script for that repository.
451
451
452
452
By leveraging official tools and documentation from each ecosystem – Node (npm, pkg, or SEA), Deno (JSR and `deno compile`), and Bun (bun build/publish) – this solution provides a **consistent release process** across all projects. It ensures that libraries reach the right package registries and that CLI applications are easily installable in any environment, all through automated GitHub Actions workflows.
0 commit comments