diff options
| author | matt korwel <[email protected]> | 2025-07-05 13:58:59 -0700 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-07-05 20:58:59 +0000 |
| commit | a7256f630c7c9335ccd7a41e97c9322c0a33ea67 (patch) | |
| tree | f1bdac3ec3d67171717de60032cf3dbbf24f437b /scripts | |
| parent | 4be32d1f73bc2d9e4d22a71eee7b142e9b0aa577 (diff) | |
Relase: Clean up and condensing (#3321)
Diffstat (limited to 'scripts')
| -rw-r--r-- | scripts/get-release-version.js | 89 | ||||
| -rw-r--r-- | scripts/tag-release.js | 58 | ||||
| -rw-r--r-- | scripts/tests/get-release-version.test.js | 108 | ||||
| -rw-r--r-- | scripts/tests/test-setup.ts | 12 | ||||
| -rw-r--r-- | scripts/tests/vitest.config.ts | 20 |
5 files changed, 229 insertions, 58 deletions
diff --git a/scripts/get-release-version.js b/scripts/get-release-version.js new file mode 100644 index 00000000..5aee50c4 --- /dev/null +++ b/scripts/get-release-version.js @@ -0,0 +1,89 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { execSync } from 'child_process'; +import fs from 'fs'; +import path from 'path'; + +function getPackageVersion() { + const packageJsonPath = path.resolve(process.cwd(), 'package.json'); + const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); + return packageJson.version; +} + +function getShortSha() { + return execSync('git rev-parse --short HEAD').toString().trim(); +} + +export function getNightlyTagName() { + const version = getPackageVersion(); + const now = new Date(); + const year = now.getUTCFullYear().toString().slice(-2); + const month = (now.getUTCMonth() + 1).toString().padStart(2, '0'); + const day = now.getUTCDate().toString().padStart(2, '0'); + const date = `${year}${month}${day}`; + + const sha = getShortSha(); + return `v${version}-nightly.${date}.${sha}`; +} + +export function getReleaseVersion() { + const isNightly = process.env.IS_NIGHTLY === 'true'; + const manualVersion = process.env.MANUAL_VERSION; + + let releaseTag; + + if (isNightly) { + console.error('Calculating next nightly version...'); + releaseTag = getNightlyTagName(); + } else if (manualVersion) { + console.error(`Using manual version: ${manualVersion}`); + releaseTag = manualVersion; + } else { + throw new Error( + 'Error: No version specified and this is not a nightly release.', + ); + } + + if (!releaseTag) { + throw new Error('Error: Version could not be determined.'); + } + + if (!releaseTag.startsWith('v')) { + console.error("Version is missing 'v' prefix. Prepending it."); + releaseTag = `v${releaseTag}`; + } + + if (releaseTag.includes('+')) { + throw new Error( + 'Error: Versions with build metadata (+) are not supported for releases. Please use a pre-release version (e.g., v1.2.3-alpha.4) instead.', + ); + } + + if (!releaseTag.match(/^v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?$/)) { + throw new Error( + 'Error: Version must be in the format vX.Y.Z or vX.Y.Z-prerelease', + ); + } + + const releaseVersion = releaseTag.substring(1); + let npmTag = 'latest'; + if (releaseVersion.includes('-')) { + npmTag = releaseVersion.split('-')[1].split('.')[0]; + } + + return { releaseTag, releaseVersion, npmTag }; +} + +if (process.argv[1] === new URL(import.meta.url).pathname) { + try { + const versions = getReleaseVersion(); + console.log(JSON.stringify(versions)); + } catch (error) { + console.error(error.message); + process.exit(1); + } +} diff --git a/scripts/tag-release.js b/scripts/tag-release.js deleted file mode 100644 index 40385264..00000000 --- a/scripts/tag-release.js +++ /dev/null @@ -1,58 +0,0 @@ -/** - * @license - * Copyright 2025 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import { execSync } from 'child_process'; -import { readFileSync } from 'fs'; -import path from 'path'; - -function getVersion() { - const packageJsonPath = path.resolve(process.cwd(), 'package.json'); - const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8')); - return packageJson.version; -} - -function getShortSha() { - return execSync('git rev-parse --short HEAD').toString().trim(); -} - -function getNightlyTagName() { - const version = getVersion(); - const now = new Date(); - const year = now.getUTCFullYear().toString().slice(-2); - const month = (now.getUTCMonth() + 1).toString().padStart(2, '0'); - const day = now.getUTCDate().toString().padStart(2, '0'); - const date = `${year}${month}${day}`; - - const sha = getShortSha(); - return `v${version}-nightly.${date}.${sha}`; -} - -function createAndPushTag(tagName, isSigned) { - const command = isSigned - ? `git tag -s -a ${tagName} -m ''` - : `git tag ${tagName}`; - - try { - console.log(`Executing: ${command}`); - execSync(command, { stdio: 'inherit' }); - console.log(`Successfully created tag: ${tagName}`); - - console.log(`Pushing tag to origin...`); - execSync(`git push origin ${tagName}`, { stdio: 'inherit' }); - console.log(`Successfully pushed tag: ${tagName}`); - } catch (error) { - console.error(`Failed to create or push tag: ${tagName}`); - console.error(error); - process.exit(1); - } -} - -const tagName = getNightlyTagName(); -// In GitHub Actions, the CI variable is set to true. -// We will create a signed commit if not in a CI environment. -const shouldSign = !process.env.CI; - -createAndPushTag(tagName, shouldSign); diff --git a/scripts/tests/get-release-version.test.js b/scripts/tests/get-release-version.test.js new file mode 100644 index 00000000..3b127644 --- /dev/null +++ b/scripts/tests/get-release-version.test.js @@ -0,0 +1,108 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest'; +import { getReleaseVersion } from '../get-release-version'; +import { execSync } from 'child_process'; +import * as fs from 'fs'; + +vi.mock('child_process', () => ({ + execSync: vi.fn(), +})); + +vi.mock('fs', async (importOriginal) => { + const mod = await importOriginal(); + return { + ...mod, + readFileSync: vi.fn(), + }; +}); + +describe('getReleaseVersion', () => { + const originalEnv = { ...process.env }; + + beforeEach(() => { + vi.resetModules(); + process.env = { ...originalEnv }; + vi.useFakeTimers(); + }); + + afterEach(() => { + process.env = originalEnv; + vi.clearAllMocks(); + vi.useRealTimers(); + }); + + it('should calculate nightly version when IS_NIGHTLY is true', () => { + process.env.IS_NIGHTLY = 'true'; + const knownDate = new Date('2025-07-20T10:00:00.000Z'); + vi.setSystemTime(knownDate); + vi.mocked(fs.readFileSync).mockReturnValue( + JSON.stringify({ version: '0.1.0' }), + ); + vi.mocked(execSync).mockReturnValue('abcdef'); + const { releaseTag, releaseVersion, npmTag } = getReleaseVersion(); + expect(releaseTag).toBe('v0.1.9-nightly.250720.abcdef'); + expect(releaseVersion).toBe('0.1.9-nightly.250720.abcdef'); + expect(npmTag).toBe('nightly'); + }); + + it('should use manual version when provided', () => { + process.env.MANUAL_VERSION = '1.2.3'; + const { releaseTag, releaseVersion, npmTag } = getReleaseVersion(); + expect(releaseTag).toBe('v1.2.3'); + expect(releaseVersion).toBe('1.2.3'); + expect(npmTag).toBe('latest'); + }); + + it('should prepend v to manual version if missing', () => { + process.env.MANUAL_VERSION = '1.2.3'; + const { releaseTag } = getReleaseVersion(); + expect(releaseTag).toBe('v1.2.3'); + }); + + it('should handle pre-release versions correctly', () => { + process.env.MANUAL_VERSION = 'v1.2.3-beta.1'; + const { releaseTag, releaseVersion, npmTag } = getReleaseVersion(); + expect(releaseTag).toBe('v1.2.3-beta.1'); + expect(releaseVersion).toBe('1.2.3-beta.1'); + expect(npmTag).toBe('beta'); + }); + + it('should throw an error for invalid version format', () => { + process.env.MANUAL_VERSION = '1.2'; + expect(() => getReleaseVersion()).toThrow( + 'Error: Version must be in the format vX.Y.Z or vX.Y.Z-prerelease', + ); + }); + + it('should throw an error if no version is provided for non-nightly release', () => { + expect(() => getReleaseVersion()).toThrow( + 'Error: No version specified and this is not a nightly release.', + ); + }); + + it('should throw an error for versions with build metadata', () => { + process.env.MANUAL_VERSION = 'v1.2.3+build456'; + expect(() => getReleaseVersion()).toThrow( + 'Error: Versions with build metadata (+) are not supported for releases.', + ); + }); +}); + +describe('get-release-version script', () => { + it('should print version JSON to stdout when executed directly', () => { + const expectedJson = { + releaseTag: 'v0.1.0-nightly.20250705', + releaseVersion: '0.1.0-nightly.20250705', + npmTag: 'nightly', + }; + execSync.mockReturnValue(JSON.stringify(expectedJson)); + + const result = execSync('node scripts/get-release-version.js').toString(); + expect(JSON.parse(result)).toEqual(expectedJson); + }); +}); diff --git a/scripts/tests/test-setup.ts b/scripts/tests/test-setup.ts new file mode 100644 index 00000000..d4c4b465 --- /dev/null +++ b/scripts/tests/test-setup.ts @@ -0,0 +1,12 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { vi } from 'vitest'; + +vi.mock('fs', () => ({ + ...vi.importActual('fs'), + appendFileSync: vi.fn(), +})); diff --git a/scripts/tests/vitest.config.ts b/scripts/tests/vitest.config.ts new file mode 100644 index 00000000..a8e67005 --- /dev/null +++ b/scripts/tests/vitest.config.ts @@ -0,0 +1,20 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + environment: 'node', + include: ['scripts/tests/**/*.test.js'], + setupFiles: ['scripts/tests/test-setup.ts'], + coverage: { + provider: 'v8', + reporter: ['text', 'lcov'], + }, + }, +}); |
