Skip to content
Merged
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
9 changes: 7 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,13 @@ jobs:
build-command: pnpm run build
test-command: pnpm run test
# `build/` is generated, so the only way to know the published tarball
# isn't empty is to pack one.
extra-command: npm pack --dry-run
# isn't empty is to pack one. changelog:check renders a synthetic history
# of every commit type through the real changelog preset and fails if a
# type renders against policy or a breaking change goes missing.
#
# One `extra-command`, so they're chained. `&&` short-circuits, which is
# what we want: either failing fails the job.
extra-command: npm pack --dry-run && pnpm run changelog:check
# Nothing here uses turbo.
turbo-cache: false

Expand Down
31 changes: 25 additions & 6 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ jobs:
- run: pnpm run build

# Publishes to npm, pushes the annotated tag and creates the GitHub
# release. The version bump commit stays local, see .release-it.cjs.
# release. The version bump commit stays local, see .release-it.mjs.
- name: Release
id: release
env:
Expand All @@ -79,9 +79,25 @@ jobs:
args="$args --dry-run"
fi

before=$(node -p "require('./package.json').version")
pnpm run release $args

echo "version=$(node -p "require('./package.json').version")" >> "$GITHUB_OUTPUT"
after=$(node -p "require('./package.json').version")

echo "version=$after" >> "$GITHUB_OUTPUT"

# Now that the changelog types carry `effect`, only the `bump` ones
# can raise a version, so a cycle of nothing but docs/chore/build/
# refactor commits recommends nothing. release-it prints "No new
# version to release", exits 0 and leaves the tree alone. Without
# this flag the next step would push an empty branch, fail to open a
# PR against it and report that npm and the tag are out when neither
# is. Re-run with the `increment` input if a release was the point.
if [ "$before" = "$after" ]; then
echo "released=false" >> "$GITHUB_OUTPUT"
echo "::notice::No new version to release. Nothing was published or tagged. Dispatch again with an explicit \`increment\` to force one."
else
echo "released=true" >> "$GITHUB_OUTPUT"
fi

# master requires status checks and a fresh commit has none, so the bump
# has to arrive as a PR. Merge commit, not squash: the tag points at the
Expand All @@ -99,7 +115,7 @@ jobs:
# npm, the tag and the GitHub release are already out at that point, so
# only the bump on master is waiting.
- name: Land the version bump on master
if: ${{ !inputs.dry-run }}
if: ${{ !inputs.dry-run && steps.release.outputs.released == 'true' }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VERSION: ${{ steps.release.outputs.version }}
Expand Down Expand Up @@ -130,7 +146,10 @@ jobs:
echo "opened $url"

# The merge commit lands on master too, so it needs a conventional
# subject. `.release-it.cjs` filters `chore(release)` out of the
# changelog, so neither this nor the bump commit shows up next time.
# subject. `--merge` rather than `--squash` is what keeps both this
# and the bump commit out of the next changelog: conventional-changelog
# skips merge commits, and a merge is what this produces. There used
# to be a `commitFilter` in the release-it config claiming that job,
# but the plugin never read the key.
gh pr merge "$number" --auto --merge --delete-branch \
--subject "chore(release): v$VERSION (#$number)" --body ""
38 changes: 20 additions & 18 deletions .release-it.cjs → .release-it.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,15 @@
* release workflow can't push the version bump straight to it. release-it
* commits and tags locally, pushes only the tag (tags aren't covered by a
* branch ruleset), and the workflow lands the bump commit through a PR.
*
* ESM rather than `.release-it.cjs` so the type list can live in
* tools/changelog-preset.mjs and be shared with tools/changelog-check.mjs.
* conventional-changelog-conventionalcommits is ESM-only, so a CJS config
* could only reach it through a dynamic import.
*/
module.exports = {
import { TYPES } from "./tools/changelog-preset.mjs";

const config = {
git: {
requireBranch: "master",
commitMessage: "chore(release): v${version}",
Expand Down Expand Up @@ -39,26 +46,21 @@ module.exports = {
"@release-it/conventional-changelog": {
infile: "CHANGELOG.md",
header: "# Changelog",
// Each release leaves a `chore(release)` bump commit and the merge commit
// that lands it. Neither is worth a changelog line.
commitFilter: (commit) =>
!(commit.header ?? "").startsWith("chore(release)"),
// There used to be a `commitFilter` here, meant to keep the
// `chore(release)` bump commits out of the notes. The plugin never read
// it: it destructures `preset`, `context`, `gitRawCommitsOpts`,
// `parserOpts`, `writerOpts` and `whatBump`, and `commitFilter` appears
// nowhere in its source. Nothing was lost by it doing nothing, because
// the bump commits reach master as merge commits (both v1.0.12's and
// v1.0.13's have two parents) and conventional-changelog skips those
// anyway. Removing the key so the config stops claiming a behaviour it
// never had.
preset: {
name: "conventionalcommits",
types: [
{ type: "feat", section: "Features" },
{ type: "fix", section: "Bug Fixes" },
{ type: "perf", section: "Performance" },
{ type: "refactor", section: "Code Refactoring" },
{ type: "docs", section: "Documentation" },
{ type: "build", section: "Build System" },
{ type: "ci", section: "Continuous Integration" },
{ type: "chore", section: "Chores" },
{ type: "revert", section: "Reverts" },
{ type: "test", hidden: true },
{ type: "style", hidden: true },
],
types: TYPES,
},
},
},
};

export default config;
8 changes: 7 additions & 1 deletion oxlint.config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,17 @@ import base from "magic-oxlint-config/base";
// with nothing local to drift from the preset.
export default extendConfig(base, {
overrides: [
{
// tools/ holds the changelog preset and its control script. Printing to
// the terminal is what the control is for, the CI job reads its output.
files: ["tools/**"],
rules: { "no-console": "off" },
},
{
// `${version}` and friends in this file are release-it's own template
// syntax, interpolated by release-it at release time. They are supposed
// to reach it uninterpolated, so a real template literal would be the bug.
files: [".release-it.cjs"],
files: [".release-it.mjs"],
rules: { "no-template-curly-in-string": "off" },
},
],
Expand Down
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
},
"scripts": {
"build": "tsc --project tsconfig.build.json",
"changelog:check": "node tools/changelog-check.mjs",
"clean": "expo-module clean",
"lint": "oxlint --report-unused-disable-directives --deny-warnings",
"lint:fix": "oxlint --report-unused-disable-directives --deny-warnings --fix",
Expand All @@ -40,6 +41,9 @@
"@release-it/conventional-changelog": "^12.0.0",
"@types/jest": "^29.5.14",
"@types/node": "^24.13.3",
"conventional-changelog": "^8.1.0",
"conventional-changelog-conventionalcommits": "^10.2.1",
"conventional-recommended-bump": "^12.1.0",
"expo": ">=49.0.0",
"expo-module-scripts": "^3.0.3",
"jest": "^29",
Expand Down
9 changes: 9 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading