Skip to content
Open
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
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ jobs:
node-version: 22
cache: yarn

- name: Test snapshot publisher
run: python3 -m unittest discover -s scripts -p 'test_*.py'

- name: Install
run: yarn install --frozen-lockfile

Expand Down
109 changes: 0 additions & 109 deletions .github/workflows/pages.yml

This file was deleted.

133 changes: 58 additions & 75 deletions .github/workflows/publish.yaml
Original file line number Diff line number Diff line change
@@ -1,92 +1,75 @@
name: Publish

on:
push:
branches:
- master
- develop
workflow_run:
workflows: [Snapshot package]
types: [completed]
workflow_call:
inputs:
release-ref:
required: true
type: string

# Required for npm Trusted Publishing (OIDC). `id-token: write` lets the job mint the
# short-lived OIDC token npm exchanges for publish rights, and enables provenance attestations.
# No NPM_TOKEN secret is used anymore.
permissions:
contents: read
id-token: write

jobs:
publish:
snapshot:
if: >-
github.event_name == 'workflow_run' &&
github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.event == 'pull_request'
permissions:
actions: read
contents: read
id-token: write
runs-on: ubuntu-latest
steps:
# Use the trusted default-branch scripts, never execute code from the PR here.
- uses: actions/checkout@v6

# NOTE deliberately no `registry-url`. That input makes setup-node write an `.npmrc` containing
# //registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}
# and yarn 1 resolves env placeholders when it *parses* config, so it throws
# "Failed to replace env in config: ${NODE_AUTH_TOKEN}" before installing anything — and
# `NODE_AUTH_TOKEN` no longer exists, because publishing moved to Trusted Publishing (OIDC).
# npm defaults to registry.npmjs.org anyway, and mints its own short-lived credential.
with:
persist-credentials: false
- uses: actions/setup-node@v7
with:
node-version: 22
- run: npm install -g npm@11
- name: Download tested PR package
uses: actions/download-artifact@v7
with:
name: snapshot-package
path: ${{ runner.temp }}/snapshot-artifact
github-token: ${{ github.token }}
run-id: ${{ github.event.workflow_run.id }}
- name: Publish next npm snapshot
env:
SNAPSHOT_ARTIFACT: ${{ runner.temp }}/snapshot-artifact
SNAPSHOT_RUN_ID: ${{ github.event.workflow_run.id }}
SNAPSHOT_RUN_ATTEMPT: ${{ github.event.workflow_run.run_attempt }}
NPM_CONFIG_USERCONFIG: ${{ runner.temp }}/snapshot.npmrc
run: python3 scripts/publish-snapshot.py

# Trusted Publishing requires npm >= 11.5.1, which is newer than the npm bundled with Node 22.
- name: Upgrade npm
run: npm install -g npm@latest

- name: Install
run: yarn install --frozen-lockfile

- name: Build
run: yarn build

- name: Test
run: yarn test --ci

- name: Resolve context
id: ctx
shell: bash
release:
if: github.event_name == 'workflow_dispatch'
permissions:
contents: read
id-token: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
ref: ${{ inputs.release-ref }}
persist-credentials: false
# No registry-url: Yarn 1 cannot resolve an unset NODE_AUTH_TOKEN.
- uses: actions/setup-node@v7
with:
node-version: 22
cache: yarn
- run: npm install -g npm@11
- run: yarn install --frozen-lockfile
- run: yarn lint && yarn typecheck && yarn build && yarn test --ci
- name: Publish version to npm
run: |
VERSION="$(node -p "require('./package.json').version")"
echo "branch=${GITHUB_REF##*/}" >> "$GITHUB_OUTPUT"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "short_sha=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"

# Skip (don't fail) if this exact version is already on npm — avoids a red build when a
# master push didn't bump the version. (develop publishes a unique snapshot, so it's exempt.)
- name: Check if version already published
id: published
if: steps.ctx.outputs.branch == 'master'
shell: bash
run: |
NAME="$(node -p "require('./package.json').name")"
if npm view "$NAME@${{ steps.ctx.outputs.version }}" version >/dev/null 2>&1; then
echo "Version $NAME@${{ steps.ctx.outputs.version }} already published — skipping publish."
echo "exists=true" >> "$GITHUB_OUTPUT"
else
echo "exists=false" >> "$GITHUB_OUTPUT"
fi

# master -> always "latest", whatever the version looks like.
#
# master is the release line: whatever is on it is what `npm install @observertc/observer-js`
# should give you. The dist-tag is therefore a property of the *branch*, not of the version
# string — a prerelease version on master is still the current release.
#
# Note npm permits a prerelease version under "latest"; it is a convention that stops it, not a
# rule. The consequence is deliberate and worth being clear about: while `package.json` reads
# e.g. 1.0.0-beta.20, a plain `npm install` resolves to that beta. Publish prereleases you do
# *not* want installed by default from a branch other than master.
#
# Auth is via OIDC (trusted publisher); provenance is attached automatically.
- name: Publish
if: steps.ctx.outputs.branch == 'master' && steps.published.outputs.exists == 'false'
run: npm publish --access public --ignore-scripts --tag latest

# develop -> publish a per-commit release candidate under a custom dist-tag.
- name: Make snapshot version
if: steps.ctx.outputs.branch != 'master'
run: npm version prerelease --preid=${{ steps.ctx.outputs.short_sha }} --git-tag-version false

- name: Publish (release candidate)
if: steps.ctx.outputs.branch != 'master'
run: npm publish --access public --ignore-scripts --tag "$(echo "develop-${{ steps.ctx.outputs.version }}-rc" | sed 's/[.]//g')"
TAG=latest
if [[ "$VERSION" == *-* ]]; then TAG=beta; fi
npm publish --access public --ignore-scripts --tag "$TAG"
81 changes: 81 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
name: Release

on:
workflow_dispatch:
inputs:
version:
description: 'Exact version to publish, without v (for example 1.3.0)'
required: true
type: string

permissions:
contents: read

concurrency:
group: release
cancel-in-progress: false

jobs:
prepare:
if: github.ref == 'refs/heads/master'
permissions:
contents: write
runs-on: ubuntu-latest
outputs:
tag: ${{ steps.prepare.outputs.tag }}
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: actions/setup-node@v7
with:
node-version: 22
cache: yarn
- run: yarn install --frozen-lockfile
- name: Finalize version and changelog
id: prepare
env:
RELEASE_VERSION: ${{ inputs.version }}
run: |
node scripts/prepare-release.cjs
echo "tag=v$RELEASE_VERSION" >> "$GITHUB_OUTPUT"
- run: yarn lint && yarn typecheck && yarn build && yarn test --ci
- name: Commit and tag release
env:
RELEASE_VERSION: ${{ inputs.version }}
run: |
git config user.name 'github-actions[bot]'
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
git add package.json CHANGELOG.md
git commit -m "chore: release $RELEASE_VERSION"
git tag -a "v$RELEASE_VERSION" -m "Release $RELEASE_VERSION"
git push --atomic origin HEAD:master "v$RELEASE_VERSION"

publish:
needs: prepare
permissions:
contents: read
id-token: write
uses: ./.github/workflows/publish.yaml
with:
release-ref: ${{ needs.prepare.outputs.tag }}

release:
needs: [prepare, publish]
permissions:
contents: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
ref: ${{ needs.prepare.outputs.tag }}
- name: Extract release notes
run: node scripts/prepare-release.cjs --notes
- name: Create GitHub release
env:
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ needs.prepare.outputs.tag }}
run: |
FLAGS=()
if [[ "$RELEASE_TAG" == *-* ]]; then FLAGS+=(--prerelease); fi
gh release create "$RELEASE_TAG" --verify-tag --title "$RELEASE_TAG" --notes-file release-notes.md "${FLAGS[@]}"
32 changes: 32 additions & 0 deletions .github/workflows/snapshot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Snapshot package

on:
pull_request:
branches: [master]

permissions:
contents: read

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: actions/setup-node@v7
with:
node-version: 22
cache: yarn
- run: yarn install --frozen-lockfile
- run: yarn lint && yarn typecheck && yarn build && yarn test --ci
- name: Pack tested snapshot
run: |
mkdir -p "$RUNNER_TEMP/snapshot-package"
npm pack --ignore-scripts --pack-destination "$RUNNER_TEMP/snapshot-package"
- uses: actions/upload-artifact@v7
with:
name: snapshot-package
path: ${{ runner.temp }}/snapshot-package/*.tgz
if-no-files-found: error
retention-days: 7
6 changes: 4 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,10 @@ yarn test:coverage # jest --coverage (enforces the coverage thresholds in j
`prepublishOnly` runs lint + typecheck; `prepare` runs the build.

CI (`.github/workflows/ci.yml`) runs lint + typecheck + **build** + `test:coverage` on every push/PR.
Publishing (`.github/workflows/publish.yaml`) uses npm **Trusted Publishing (OIDC) + provenance** on
pushes to `master` (prerelease versions publish under the `beta` dist-tag).
Snapshots (`.github/workflows/snapshot.yml`) build PRs targeting `master`;
`publish.yaml` publishes the tested artifact to npm as `<version>-rc.<number>` under `snapshot`. Manual `Release` on `master` takes an exact version, commits the finalized changelog
and package version, tags it, publishes through npm Trusted Publishing, and creates a GitHub release.
See `CONTRIBUTING.md` for setup and recovery.

---

Expand Down
Loading
Loading