From 53d3db9db263cde6c8fe5748b47a02a98b03a8f3 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Thu, 5 Feb 2026 14:42:57 +0100 Subject: [PATCH 1/4] Restore publishing commitlies --- .github/workflows/publish-release.yml | 10 +++++----- scripts/release/get-version.js | 10 +++++++++- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index 8d42c1b102..c7b23e9cc1 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -1,11 +1,11 @@ name: Publish release to npm on: # For commitlies - # push: - # branches: - # - main - # paths: - # - packages/react-native-gesture-handler/** + push: + branches: + - main + paths: + - packages/react-native-gesture-handler/** # For manual releases workflow_dispatch: inputs: diff --git a/scripts/release/get-version.js b/scripts/release/get-version.js index 7e9b14a491..ba965b98ee 100644 --- a/scripts/release/get-version.js +++ b/scripts/release/get-version.js @@ -4,7 +4,15 @@ const { ReleaseType } = require('./parse-arguments'); function getVersion(releaseType, versionHint = null) { if (releaseType === ReleaseType.COMMITLY) { - const [major, minor] = getLatestVersion(); + let [major, minor] = getLatestVersion(); + + if (major === 2) { + // If the latest version is 2.x.x, we are still in the beta period for 3.x.x + // Override the values so the resulting version is 3.0.0 + // TODO: Remove this once we have a stable 3.x.x release + major = 3; + minor = -1; + } const currentSHA = execSync('git rev-parse HEAD').toString().trim(); const now = new Date(); From 9b15b1651989009bf3843ac7912028ba46da8b36 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Thu, 5 Feb 2026 14:46:26 +0100 Subject: [PATCH 2/4] Update test --- scripts/release/__tests__/get-version.test.js | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/scripts/release/__tests__/get-version.test.js b/scripts/release/__tests__/get-version.test.js index 38730382db..81d9de327e 100644 --- a/scripts/release/__tests__/get-version.test.js +++ b/scripts/release/__tests__/get-version.test.js @@ -39,23 +39,32 @@ describe('get-version', () => { // Commitly/nightly release tests describe('commitly releases', () => { test('returns nightly version with date and SHA', () => { - getLatestVersion.mockReturnValue([2, 22, 0, null]); + getLatestVersion.mockReturnValue([3, 0, 0, null]); execSync.mockReturnValue(Buffer.from('abc123def456789\n')); const result = getVersion(ReleaseType.COMMITLY); - expect(result).toBe('2.23.0-nightly-20260129-abc123def'); + expect(result).toBe('3.1.0-nightly-20260129-abc123def'); expect(getLatestVersion).toHaveBeenCalled(); expect(execSync).toHaveBeenCalledWith('git rev-parse HEAD'); }); test('increments minor version from latest', () => { - getLatestVersion.mockReturnValue([2, 25, 3, null]); + getLatestVersion.mockReturnValue([3, 0, 0, null]); + execSync.mockReturnValue(Buffer.from('fedcba987654321\n')); + + const result = getVersion(ReleaseType.COMMITLY); + + expect(result).toBe('3.1.0-nightly-20260129-fedcba987'); + }); + + test('overrides major version from latest', () => { + getLatestVersion.mockReturnValue([2, 22, 0, null]); execSync.mockReturnValue(Buffer.from('fedcba987654321\n')); const result = getVersion(ReleaseType.COMMITLY); - expect(result).toBe('2.26.0-nightly-20260129-fedcba987'); + expect(result).toBe('3.0.0-nightly-20260129-fedcba987'); }); test('uses first 9 characters of SHA', () => { From 85ec7e924a314924fdc9e67497e93ef3a5c21220 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Thu, 5 Feb 2026 14:55:06 +0100 Subject: [PATCH 3/4] Update test name --- scripts/release/__tests__/get-version.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/release/__tests__/get-version.test.js b/scripts/release/__tests__/get-version.test.js index 81d9de327e..f74c04a579 100644 --- a/scripts/release/__tests__/get-version.test.js +++ b/scripts/release/__tests__/get-version.test.js @@ -58,7 +58,7 @@ describe('get-version', () => { expect(result).toBe('3.1.0-nightly-20260129-fedcba987'); }); - test('overrides major version from latest', () => { + test('overrides major 2.x release to 3.0.0 nightly', () => { getLatestVersion.mockReturnValue([2, 22, 0, null]); execSync.mockReturnValue(Buffer.from('fedcba987654321\n')); From 847995d8847f3702732a078c3b0aca7e3a667fa8 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Fri, 6 Feb 2026 13:02:02 +0100 Subject: [PATCH 4/4] Switch to nightlies --- .../actions/publish-npm-package/action.yml | 8 ++-- .github/workflows/publish-release.yml | 16 ++++--- scripts/release/__tests__/get-version.test.js | 42 +++++++++++++++---- .../release/__tests__/parse-arguments.test.js | 32 +++++++------- scripts/release/get-version.js | 18 ++++++-- scripts/release/parse-arguments.js | 16 +++---- 6 files changed, 83 insertions(+), 49 deletions(-) diff --git a/.github/actions/publish-npm-package/action.yml b/.github/actions/publish-npm-package/action.yml index 44ced76168..21ea1775de 100644 --- a/.github/actions/publish-npm-package/action.yml +++ b/.github/actions/publish-npm-package/action.yml @@ -2,8 +2,8 @@ name: publish-npm-package description: Build and publish react-native-gesture-handler package to npm inputs: release-type: - description: Release type to be published (stable, commitly, beta, rc). - default: "commitly" + description: Release type to be published (stable, nightly, beta, rc). + default: "nightly" version: description: Specific version to publish (usually inferred from x.y-stable branch name). default: "" @@ -31,7 +31,7 @@ runs: id: set-package-version shell: bash run: | - VERSION=$(node ./scripts/release/set-package-version.js ${{ inputs.release-type == 'commitly' && '--commitly' || '' }} ${{ inputs.release-type == 'beta' && '--beta' || '' }} ${{ inputs.release-type == 'rc' && '--rc' || '' }} ${{ inputs.version != '' && format('--version ''{0}''', inputs.version) || '' }}) + VERSION=$(node ./scripts/release/set-package-version.js ${{ inputs.release-type == 'nightly' && '--nightly' || '' }} ${{ inputs.release-type == 'beta' && '--beta' || '' }} ${{ inputs.release-type == 'rc' && '--rc' || '' }} ${{ inputs.version != '' && format('--version ''{0}''', inputs.version) || '' }}) echo "Updated package version to $VERSION" echo "version=$VERSION" >> $GITHUB_OUTPUT @@ -39,7 +39,7 @@ runs: id: figure-out-npm-tag shell: bash run: | - if [ "${{ inputs.release-type == 'commitly' }}" = "true" ]; then + if [ "${{ inputs.release-type == 'nightly' }}" = "true" ]; then TAG_ARGUMENT="--tag nightly" else if [ "${{ inputs.release-type == 'beta' || inputs.release-type == 'rc' }}" = "true" ]; then diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index c7b23e9cc1..80c5fa131e 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -1,11 +1,8 @@ name: Publish release to npm on: - # For commitlies - push: - branches: - - main - paths: - - packages/react-native-gesture-handler/** + # For nightly releases + schedule: + - cron: '27 23 * * *' # at 23:27 every day # For manual releases workflow_dispatch: inputs: @@ -14,6 +11,7 @@ on: type: choice options: - stable + - nightly - beta - rc default: stable @@ -52,9 +50,9 @@ jobs: version: ${{ inputs.version }} dry-run: ${{ inputs.dry-run }} - - name: Publish automatic commitly release - if: ${{ github.event_name == 'push' }} + - name: Publish automatic nightly release + if: ${{ github.event_name == 'schedule' }} uses: ./.github/actions/publish-npm-package with: - release-type: 'commitly' + release-type: 'nightly' dry-run: false diff --git a/scripts/release/__tests__/get-version.test.js b/scripts/release/__tests__/get-version.test.js index f74c04a579..72b3f6ec9a 100644 --- a/scripts/release/__tests__/get-version.test.js +++ b/scripts/release/__tests__/get-version.test.js @@ -10,8 +10,13 @@ jest.mock('../version-utils', () => ({ parseVersion: jest.fn(), })); +jest.mock('../npm-utils', () => ({ + getPackageVersionByTag: jest.fn(), +})); + const { execSync } = require('child_process'); const { getStableBranchVersion, getLatestVersion, getNextPreReleaseVersion, getNextStableVersion, parseVersion } = require('../version-utils'); +const { getPackageVersionByTag } = require('../npm-utils'); const { getVersion } = require('../get-version'); const { ReleaseType } = require('../parse-arguments'); @@ -29,6 +34,9 @@ describe('get-version', () => { return new RealDate(...args); } }; + + // By default, simulate that the latest nightly was published with a different SHA + getPackageVersionByTag.mockReturnValue('3.0.0-nightly-20260128-previoussha'); }); afterEach(() => { @@ -36,13 +44,13 @@ describe('get-version', () => { }); describe('getVersion', () => { - // Commitly/nightly release tests - describe('commitly releases', () => { + // Nightly release tests + describe('nightly releases', () => { test('returns nightly version with date and SHA', () => { getLatestVersion.mockReturnValue([3, 0, 0, null]); execSync.mockReturnValue(Buffer.from('abc123def456789\n')); - const result = getVersion(ReleaseType.COMMITLY); + const result = getVersion(ReleaseType.NIGHTLY); expect(result).toBe('3.1.0-nightly-20260129-abc123def'); expect(getLatestVersion).toHaveBeenCalled(); @@ -53,7 +61,7 @@ describe('get-version', () => { getLatestVersion.mockReturnValue([3, 0, 0, null]); execSync.mockReturnValue(Buffer.from('fedcba987654321\n')); - const result = getVersion(ReleaseType.COMMITLY); + const result = getVersion(ReleaseType.NIGHTLY); expect(result).toBe('3.1.0-nightly-20260129-fedcba987'); }); @@ -62,7 +70,7 @@ describe('get-version', () => { getLatestVersion.mockReturnValue([2, 22, 0, null]); execSync.mockReturnValue(Buffer.from('fedcba987654321\n')); - const result = getVersion(ReleaseType.COMMITLY); + const result = getVersion(ReleaseType.NIGHTLY); expect(result).toBe('3.0.0-nightly-20260129-fedcba987'); }); @@ -71,7 +79,7 @@ describe('get-version', () => { getLatestVersion.mockReturnValue([2, 22, 0, null]); execSync.mockReturnValue(Buffer.from('123456789abcdef0\n')); - const result = getVersion(ReleaseType.COMMITLY); + const result = getVersion(ReleaseType.NIGHTLY); expect(result).toContain('-123456789'); expect(result).not.toContain('abcdef0'); @@ -89,11 +97,29 @@ describe('get-version', () => { getLatestVersion.mockReturnValue([2, 22, 0, null]); execSync.mockReturnValue(Buffer.from('abc123def\n')); - const result = getVersion(ReleaseType.COMMITLY); + const result = getVersion(ReleaseType.NIGHTLY); expect(result).toContain('-nightly-20261205-'); }); + test('throws when latest nightly SHA matches current SHA', () => { + global.Date = class extends RealDate { + constructor(...args) { + if (args.length === 0) { + return new RealDate('2026-12-05T12:00:00Z'); + } + return new RealDate(...args); + } + }; + getLatestVersion.mockReturnValue([2, 22, 0, null]); + execSync.mockReturnValue(Buffer.from('abc123def\n')); + getPackageVersionByTag.mockReturnValue('3.0.0-nightly-20261205-abc123def'); + + expect(() => getVersion(ReleaseType.NIGHTLY)).toThrow( + 'Latest nightly version 3.0.0-nightly-20261205-abc123def SHA abc123def is the same as current SHA abc123def' + ); + }); + test('pads single digit month and day', () => { global.Date = class extends RealDate { constructor(...args) { @@ -106,7 +132,7 @@ describe('get-version', () => { getLatestVersion.mockReturnValue([2, 22, 0, null]); execSync.mockReturnValue(Buffer.from('abc123def\n')); - const result = getVersion(ReleaseType.COMMITLY); + const result = getVersion(ReleaseType.NIGHTLY); expect(result).toContain('-nightly-20260307-'); }); diff --git a/scripts/release/__tests__/parse-arguments.test.js b/scripts/release/__tests__/parse-arguments.test.js index 7c956a3878..4529bf268d 100644 --- a/scripts/release/__tests__/parse-arguments.test.js +++ b/scripts/release/__tests__/parse-arguments.test.js @@ -26,10 +26,10 @@ describe('parse-arguments', () => { }); // Single flag tests - test('returns commitly release type with --commitly flag', () => { - process.argv = ['node', 'script.js', '--commitly']; + test('returns nightly release type with --nightly flag', () => { + process.argv = ['node', 'script.js', '--nightly']; const result = parseArguments(); - expect(result).toEqual({ releaseType: ReleaseType.COMMITLY, version: null }); + expect(result).toEqual({ releaseType: ReleaseType.NIGHTLY, version: null }); }); test('returns beta release type with --beta flag', () => { @@ -64,29 +64,29 @@ describe('parse-arguments', () => { }); // Mutual exclusivity tests - test('throws error when --commitly and --beta are both provided', () => { - process.argv = ['node', 'script.js', '--commitly', '--beta']; - expect(() => parseArguments()).toThrow('Release flags --commitly, --beta, and --rc are mutually exclusive'); + test('throws error when --nightly and --beta are both provided', () => { + process.argv = ['node', 'script.js', '--nightly', '--beta']; + expect(() => parseArguments()).toThrow('Release flags --nightly, --beta, and --rc are mutually exclusive'); }); - test('throws error when --commitly and --rc are both provided', () => { - process.argv = ['node', 'script.js', '--commitly', '--rc']; - expect(() => parseArguments()).toThrow('Release flags --commitly, --beta, and --rc are mutually exclusive'); + test('throws error when --nightly and --rc are both provided', () => { + process.argv = ['node', 'script.js', '--nightly', '--rc']; + expect(() => parseArguments()).toThrow('Release flags --nightly, --beta, and --rc are mutually exclusive'); }); test('throws error when --beta and --rc are both provided', () => { process.argv = ['node', 'script.js', '--beta', '--rc']; - expect(() => parseArguments()).toThrow('Release flags --commitly, --beta, and --rc are mutually exclusive'); + expect(() => parseArguments()).toThrow('Release flags --nightly, --beta, and --rc are mutually exclusive'); }); test('throws error when all three flags are provided', () => { - process.argv = ['node', 'script.js', '--commitly', '--beta', '--rc']; - expect(() => parseArguments()).toThrow('Release flags --commitly, --beta, and --rc are mutually exclusive'); + process.argv = ['node', 'script.js', '--nightly', '--beta', '--rc']; + expect(() => parseArguments()).toThrow('Release flags --nightly, --beta, and --rc are mutually exclusive'); }); - // Version not allowed for commitly - test('throws error when version provided for commitly release', () => { - process.argv = ['node', 'script.js', '--commitly', '--version', '2.22.0']; + // Version not allowed for nightly + test('throws error when version provided for nightly release', () => { + process.argv = ['node', 'script.js', '--nightly', '--version', '2.22.0']; expect(() => parseArguments()).toThrow(); }); @@ -147,7 +147,7 @@ describe('parse-arguments', () => { expect(ReleaseType.STABLE).toBe('stable'); expect(ReleaseType.BETA).toBe('beta'); expect(ReleaseType.RELEASE_CANDIDATE).toBe('rc'); - expect(ReleaseType.COMMITLY).toBe('commitly'); + expect(ReleaseType.NIGHTLY).toBe('nightly'); }); }); }); diff --git a/scripts/release/get-version.js b/scripts/release/get-version.js index ba965b98ee..7735c078d9 100644 --- a/scripts/release/get-version.js +++ b/scripts/release/get-version.js @@ -1,9 +1,10 @@ const { execSync } = require('child_process'); const { getStableBranchVersion, getLatestVersion, getNextPreReleaseVersion, getNextStableVersion, parseVersion } = require('./version-utils'); const { ReleaseType } = require('./parse-arguments'); +const { getPackageVersionByTag } = require('./npm-utils'); function getVersion(releaseType, versionHint = null) { - if (releaseType === ReleaseType.COMMITLY) { + if (releaseType === ReleaseType.NIGHTLY) { let [major, minor] = getLatestVersion(); if (major === 2) { @@ -14,15 +15,24 @@ function getVersion(releaseType, versionHint = null) { minor = -1; } - const currentSHA = execSync('git rev-parse HEAD').toString().trim(); + const currentSHA = execSync('git rev-parse HEAD').toString().trim().slice(0, 9); + + const latestNightlyVersion = getPackageVersionByTag('react-native-gesture-handler', 'nightly'); + const latestNightlySHA = latestNightlyVersion.split('-').pop(); + + // Don't publish the same commit twice + if (latestNightlySHA === currentSHA) { + throw new Error(`Latest nightly version ${latestNightlyVersion} SHA ${latestNightlySHA} is the same as current SHA ${currentSHA}`); + } + const now = new Date(); const year = now.getFullYear(); const month = String(now.getMonth() + 1).padStart(2, '0'); const day = String(now.getDate()).padStart(2, '0'); const currentDate = `${year}${month}${day}`; - const commitlyVersion = `${major}.${minor + 1}.${0}-nightly-${currentDate}-${currentSHA.slice(0, 9)}`; - return commitlyVersion; + const nightlyVersion = `${major}.${minor + 1}.${0}-nightly-${currentDate}-${currentSHA}`; + return nightlyVersion; } else if (releaseType === ReleaseType.BETA || releaseType === ReleaseType.RELEASE_CANDIDATE) { let versionToUse = versionHint; diff --git a/scripts/release/parse-arguments.js b/scripts/release/parse-arguments.js index 3e024c0faf..de8f9d8903 100644 --- a/scripts/release/parse-arguments.js +++ b/scripts/release/parse-arguments.js @@ -4,19 +4,19 @@ const ReleaseType = { STABLE: 'stable', BETA: 'beta', RELEASE_CANDIDATE: 'rc', - COMMITLY: 'commitly', + NIGHTLY: 'nightly', }; function parseArguments() { let version = null; - let isCommitly = false; + let isNightly = false; let isBeta = false; let isReleaseCandidate = false; for (let i = 2; i < process.argv.length; i++) { const arg = process.argv[i]; - if (arg === '--commitly') { - isCommitly = true; + if (arg === '--nightly') { + isNightly = true; } else if (arg === '--beta') { isBeta = true; } else if (arg === '--rc') { @@ -31,11 +31,11 @@ function parseArguments() { } } - assert([isCommitly, isBeta, isReleaseCandidate].filter(Boolean).length <= 1, 'Release flags --commitly, --beta, and --rc are mutually exclusive; specify at most one'); - assert(version === null || isBeta || isReleaseCandidate || !isCommitly, 'Version should not be provided for commitly releases'); + assert([isNightly, isBeta, isReleaseCandidate].filter(Boolean).length <= 1, 'Release flags --nightly, --beta, and --rc are mutually exclusive; specify at most one'); + assert(version === null || isBeta || isReleaseCandidate || !isNightly, 'Version should not be provided for nightly releases'); - const releaseType = isCommitly - ? ReleaseType.COMMITLY + const releaseType = isNightly + ? ReleaseType.NIGHTLY : isBeta ? ReleaseType.BETA : isReleaseCandidate