From 9a7c306468adecec1590975dfefe131002272f8c Mon Sep 17 00:00:00 2001 From: Tane Morgan <464864+tanem@users.noreply.github.com> Date: Wed, 29 Jul 2026 05:36:40 +1200 Subject: [PATCH] fix: exit 0 when there is nothing to release The weekly release workflow was failing with a stack trace whenever no PRs had merged since the last tag, which is a normal outcome, not an error. release() now resolves 'nothing-to-release' instead of throwing, so the CLI prints a message and exits 0 in that case while still failing loudly for genuine validation errors (unlabelled or multi-labelled PRs). Co-Authored-By: Claude Opus 5 --- README.md | 2 +- src/cli.ts | 5 ++++- src/release.ts | 6 ++++-- test/release.test.ts | 4 ++-- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index cb311ea9..79074a51 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ import { changelog } from 'tanem-scripts'; ### release() -Returns a `Promise` that will be resolved once the release script completes. If an error occurs during execution, the `Promise` is rejected with an `Error` object. +Returns a `Promise` that resolves with `'released'` once the release script completes, or `'nothing-to-release'` if no pull requests have merged since the last published tag. If an error occurs during execution, the `Promise` is rejected with an `Error` object. **Example** diff --git a/src/cli.ts b/src/cli.ts index 53e086f8..9c8e26d1 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -71,7 +71,10 @@ program .description('publishes a package to npm') .action(async () => { try { - await release(); + const result = await release(); + if (result === 'nothing-to-release') { + process.stdout.write('Nothing to release\n'); + } } catch (error) { console.error(error); process.exit(1); diff --git a/src/release.ts b/src/release.ts index ef6bed63..12eee00a 100755 --- a/src/release.ts +++ b/src/release.ts @@ -12,7 +12,7 @@ import { get as getData } from './data'; const execaOptions: execa.Options = { stdio: 'inherit' }; -const release = async (): Promise => { +const release = async (): Promise<'released' | 'nothing-to-release'> => { const { pulls, tags } = await getData(); const latestTag = tags[tags.length - 1]; @@ -24,7 +24,7 @@ const release = async (): Promise => { : pulls; if (pullsToRelease.length === 0) { - throw new Error('Nothing to release'); + return 'nothing-to-release'; } const labelsToRelease = [ @@ -101,6 +101,8 @@ const release = async (): Promise => { ['publish', '--provenance', '--access', 'public'], execaOptions, ); + + return 'released'; }; export default release; diff --git a/test/release.test.ts b/test/release.test.ts index 78b6d0e0..ec5ce71c 100644 --- a/test/release.test.ts +++ b/test/release.test.ts @@ -75,7 +75,7 @@ describe('release validation', () => { ); }); - test('throws if nothing to release', async () => { + test("returns 'nothing-to-release' if there is nothing to release", async () => { const mockDataNoNewPulls = { ...mockDataWithLabels, tags: [ @@ -88,6 +88,6 @@ describe('release validation', () => { jest .spyOn(data, 'get') .mockResolvedValue(mockDataNoNewPulls as unknown as data.Data); - await expect(release()).rejects.toThrow('Nothing to release'); + await expect(release()).resolves.toBe('nothing-to-release'); }); });