Skip to content

Commit 1abb06e

Browse files
authored
ci: Add github action for trusted publishing (#1)
1 parent 76008d9 commit 1abb06e

4 files changed

Lines changed: 246 additions & 0 deletions

File tree

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
name: Publish package
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
branch:
7+
description: Branch to release from
8+
required: true
9+
default: main
10+
type: string
11+
12+
concurrency:
13+
group: publish-package-${{ github.event.inputs.branch || 'main' }}
14+
cancel-in-progress: false
15+
16+
jobs:
17+
publish:
18+
runs-on: ubuntu-24.04
19+
timeout-minutes: 20
20+
permissions:
21+
contents: write
22+
id-token: write
23+
24+
steps:
25+
- name: Check out source
26+
uses: actions/checkout@v4
27+
with:
28+
fetch-depth: 0
29+
ref: ${{ github.event.inputs.branch }}
30+
31+
- name: Set up Node.js
32+
uses: actions/setup-node@v4
33+
with:
34+
node-version: 22
35+
registry-url: https://registry.npmjs.org
36+
37+
- name: Set up Bun
38+
uses: oven-sh/setup-bun@v2
39+
with:
40+
bun-version: latest
41+
42+
- name: Determine release metadata
43+
id: metadata
44+
run: |
45+
set -euo pipefail
46+
47+
VERSION=$(node -p "require('./package.json').version")
48+
PACKAGE_NAME=$(node -p "require('./package.json').name")
49+
TAG="v${VERSION}"
50+
51+
if git ls-remote --exit-code --tags origin "refs/tags/${TAG}" >/dev/null 2>&1; then
52+
echo "Tag ${TAG} already exists on origin" >&2
53+
exit 1
54+
fi
55+
56+
if npm view "${PACKAGE_NAME}@${VERSION}" version --registry=https://registry.npmjs.org >/dev/null 2>&1; then
57+
echo "${PACKAGE_NAME}@${VERSION} is already published on npm" >&2
58+
exit 1
59+
fi
60+
61+
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
62+
echo "package_name=${PACKAGE_NAME}" >> "$GITHUB_OUTPUT"
63+
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
64+
echo "commit_sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
65+
66+
- name: Install dependencies
67+
run: bun install --frozen-lockfile
68+
69+
- name: Validate package
70+
run: |
71+
set -euo pipefail
72+
bun run check
73+
bun run typecheck
74+
bun run test
75+
bun run build
76+
npm pack --dry-run
77+
78+
- name: Publish to npm with provenance
79+
run: npm publish --provenance --access public
80+
81+
- name: Create and push git tag
82+
env:
83+
TAG: ${{ steps.metadata.outputs.tag }}
84+
COMMIT_SHA: ${{ steps.metadata.outputs.commit_sha }}
85+
run: |
86+
set -euo pipefail
87+
88+
git config user.name "github-actions[bot]"
89+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
90+
git tag "${TAG}" "${COMMIT_SHA}"
91+
git push origin "${TAG}"
92+
93+
- name: Create GitHub release
94+
env:
95+
GH_TOKEN: ${{ github.token }}
96+
TAG: ${{ steps.metadata.outputs.tag }}
97+
PACKAGE_NAME: ${{ steps.metadata.outputs.package_name }}
98+
run: |
99+
set -euo pipefail
100+
gh release create "${TAG}" \
101+
--title "${PACKAGE_NAME} ${TAG}" \
102+
--generate-notes
103+
104+
- name: Summarize release
105+
env:
106+
PACKAGE_NAME: ${{ steps.metadata.outputs.package_name }}
107+
VERSION: ${{ steps.metadata.outputs.version }}
108+
TAG: ${{ steps.metadata.outputs.tag }}
109+
run: |
110+
{
111+
echo "## Package published"
112+
echo
113+
echo "- Package: \`${PACKAGE_NAME}\`"
114+
echo "- Version: \`${VERSION}\`"
115+
echo "- Git tag: \`${TAG}\`"
116+
echo "- npm provenance: enabled"
117+
} >> "$GITHUB_STEP_SUMMARY"

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,3 +110,13 @@ Session (task span)
110110
│ └── ...
111111
└── metrics: total_turns, total_tool_calls
112112
```
113+
114+
## Releasing
115+
116+
This package is published from GitHub Actions via `.github/workflows/publish-package.yaml` using npm trusted publishing with OIDC and npm provenance (`npm publish --provenance`).
117+
118+
To cut a release:
119+
120+
1. Bump `package.json` to the version you want to ship.
121+
2. Run the **Publish package** workflow from the branch you want to release.
122+
3. The workflow validates the package, publishes to npm, pushes the matching `v<version>` git tag, and creates a GitHub release.

docs/PUBLISHING.md

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
# Publishing
2+
3+
This package is published to npm from GitHub Actions using **npm trusted publishing** with **GitHub OIDC** and **provenance attestations**.
4+
5+
Workflow file:
6+
7+
- `.github/workflows/publish-package.yaml`
8+
9+
## Why this setup
10+
11+
This avoids storing a long-lived `NPM_TOKEN` in GitHub secrets.
12+
13+
Instead:
14+
15+
- GitHub Actions requests a short-lived OIDC identity token
16+
- npm verifies that token against the configured trusted publisher
17+
- `npm publish --provenance` attaches a provenance attestation for the published package
18+
19+
The workflow has these permissions:
20+
21+
- `contents: write` - to create and push the git tag
22+
- `id-token: write` - required for npm trusted publishing via OIDC
23+
24+
## One-time npm setup
25+
26+
In npm, configure **trusted publishing** for this package:
27+
28+
- **Package:** `@braintrust/trace-opencode`
29+
- **Repository:** `braintrustdata/braintrust-opencode-plugin`
30+
- **Workflow:** `publish-package.yaml`
31+
32+
This workflow no longer requires a GitHub Actions environment.
33+
34+
## Release flow
35+
36+
Releases are started manually from GitHub Actions using the **Publish package** workflow.
37+
38+
### 1. Bump the version
39+
40+
Update the version in `package.json` to the version you want to publish.
41+
42+
### 2. Run the workflow
43+
44+
From the GitHub Actions UI:
45+
46+
- choose **Publish package**
47+
- choose the branch to publish from (default: `main`)
48+
- run the workflow
49+
50+
## What the workflow does
51+
52+
The workflow performs these steps:
53+
54+
1. Checks out the selected branch
55+
2. Sets up Node.js and Bun
56+
3. Reads `name` and `version` from `package.json`
57+
4. Verifies that:
58+
- git tag `v<version>` does not already exist on `origin`
59+
- `@braintrust/trace-opencode@<version>` is not already published on npm
60+
5. Installs dependencies with `bun install --frozen-lockfile`
61+
6. Validates the package by running:
62+
- `bun run check`
63+
- `bun run typecheck`
64+
- `bun run test`
65+
- `bun run build`
66+
- `npm pack --dry-run`
67+
7. Publishes to npm with:
68+
69+
```bash
70+
npm publish --provenance --access public
71+
```
72+
73+
8. Creates and pushes the matching git tag:
74+
75+
```text
76+
v<version>
77+
```
78+
79+
9. Creates a GitHub release for that tag
80+
81+
## Notes
82+
83+
- The package is public, so `package.json` includes:
84+
85+
```json
86+
{
87+
"publishConfig": {
88+
"access": "public"
89+
}
90+
}
91+
```
92+
93+
- The publish step relies on npm trusted publishing. No `NPM_TOKEN` secret should be needed.
94+
- If the publish succeeds, npm should show provenance information for the release.
95+
96+
## Failure modes
97+
98+
The workflow will fail early if:
99+
100+
- the git tag for that version already exists
101+
- that exact version is already published on npm
102+
- lint, typecheck, tests, build, or `npm pack --dry-run` fails
103+
- npm trusted publishing is not configured correctly for the package/repo/workflow
104+
105+
## Troubleshooting
106+
107+
If npm rejects the publish, verify:
108+
109+
1. trusted publishing is enabled for `@braintrust/trace-opencode`
110+
2. the repository matches exactly:
111+
- `braintrustdata/braintrust-opencode-plugin`
112+
3. the workflow filename matches exactly:
113+
- `publish-package.yaml`
114+
4. the GitHub Actions job has `id-token: write`
115+
116+
If needed, update the trusted publishing settings in npm and rerun the workflow.

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@
3434
"type": "git",
3535
"url": "https://github.com/braintrustdata/braintrust-opencode-plugin"
3636
},
37+
"publishConfig": {
38+
"access": "public"
39+
},
3740
"files": [
3841
"dist",
3942
"README.md"

0 commit comments

Comments
 (0)