Skip to content
Draft
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
77 changes: 77 additions & 0 deletions .github/workflows/release.yml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
name: release

on:
push:
branches: [develop]

# Serialized: no two runs publish at once. GitHub keeps only the newest queued run, so a
# rapid-fire merge's own run can be superseded — the survivor publishes the latest version.
concurrency:
group: release
cancel-in-progress: false

permissions:
contents: write
id-token: write

jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
# HEAD^ is what this merge is judged against.
fetch-depth: 2
- uses: actions/setup-node@v4
with:
node-version: 24
cache: npm
registry-url: https://registry.npmjs.org

- name: Decide whether this version needs publishing
id: decide
run: |
VERSION=$(node -p "require('./packages/cli/package.json').version")
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
PUBLISHED=$(npm view "@naturalcycles/diffity@$VERSION" version 2>/dev/null || true)
if [ "$PUBLISHED" != "$VERSION" ]; then
echo "publish=yes" >> "$GITHUB_OUTPUT"
echo "Version $VERSION is not on npm — publishing."
exit 0
fi
echo "publish=no" >> "$GITHUB_OUTPUT"
# Already published. A merge that changes what ships must have re-bumped — this is
# the parallel-branch gap: two branches bump to the same number, and the second to
# land would otherwise ship nothing, silently. Everything the artifact is built from
# counts; tests do not.
CHANGED=$(git diff --name-only HEAD^ HEAD -- \
':(glob)packages/**' ':(glob)scripts/**' 'skills' \
'package.json' 'package-lock.json' 'tsconfig.json' \
':(exclude,glob)packages/*/tests/**' ':(exclude)scripts/lib/utils.test.ts')
if [ -n "$CHANGED" ]; then
echo "::error::$VERSION is already on npm, but this merge changed shipped code. Re-bump the version (npx tsx scripts/release.ts patch --no-git) and merge again. If a previous run of this same commit already published and only tagging failed, instead create the tag by hand: git tag v$VERSION && git push origin v$VERSION && gh release create v$VERSION --generate-notes"
echo "$CHANGED"
exit 1
fi
echo "Version $VERSION already published and nothing shipped changed — nothing to do."

- if: steps.decide.outputs.publish == 'yes'
run: npm ci
- if: steps.decide.outputs.publish == 'yes'
run: npm run build
- if: steps.decide.outputs.publish == 'yes'
run: npm test
- if: steps.decide.outputs.publish == 'yes'
run: npm publish -w @naturalcycles/diffity --provenance --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- if: steps.decide.outputs.publish == 'yes'
name: Tag and release
env:
GH_TOKEN: ${{ github.token }}
VERSION: ${{ steps.decide.outputs.version }}
run: |
# Idempotent, so a re-run that already got partway does not fail on its own leftovers.
git rev-parse -q --verify "refs/tags/v$VERSION" >/dev/null || git tag "v$VERSION"
git push origin "v$VERSION"
gh release view "v$VERSION" >/dev/null 2>&1 || gh release create "v$VERSION" --generate-notes --title "v$VERSION"
27 changes: 27 additions & 0 deletions .github/workflows/test.yml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
name: test

on:
pull_request:
push:
branches: [develop]

permissions:
contents: read

jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
# The engines floor and the version releases build with.
node: [22, 24]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: npm
- run: npm ci
# Typecheck resolves workspace imports through built dists, so the build comes first.
- run: npm run build
- run: npm test
13 changes: 9 additions & 4 deletions AGENTS.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,8 +18,13 @@ npx tsx scripts/release.ts patch --no-git # or: minor
`develop` takes squash merges the commit is rewritten on the way in and the tag is left pointing at
a commit that never lands.

There are no npm publish scripts: publishing `@naturalcycles/diffity` is a deliberate, separate
step (issue #60 gives it to CI on merges to develop).
There are no npm publish scripts: every merge to develop whose version is not yet on npm is
published by `.github/workflows/release.yml`, with a `v<version>` tag and a GitHub release. The
workflow needs the `NPM_TOKEN` secret (publish rights on the `@naturalcycles` scope).

A pull request that changes nothing a user could observe — a test, a comment, a rename — does not
need a bump. Everything else does.
Bump in every pull request that touches anything the artifact is built from — `packages/`,
`scripts/`, `skills/`, or the root `package.json`/`package-lock.json`/`tsconfig.json`; tests are
exempt. The release workflow enforces exactly that set: a merge to develop whose version is already
on npm and whose diff touched it fails, which is what catches two parallel branches bumping to the
same number — the second to land would otherwise ship nothing, silently. A comment-only source
change trips the same wire; bump anyway, a patch number costs nothing.
Loading