diff --git a/apps/cli/package.json b/apps/cli/package.json index 0ccc720..2a3b80a 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -1,6 +1,6 @@ { "name": "@c0upons/cli", - "version": "0.1.0", + "version": "1.2.0", "description": "c0upons CLI — search, submit, and manage coupons from the terminal", "type": "module", "bin": { diff --git a/apps/cli/src/index.ts b/apps/cli/src/index.ts index d45e655..3f24a31 100644 --- a/apps/cli/src/index.ts +++ b/apps/cli/src/index.ts @@ -1,9 +1,18 @@ #!/usr/bin/env node +import { readFileSync } from 'node:fs'; import { Command } from 'commander'; import chalk from 'chalk'; import ora from 'ora'; import 'dotenv/config'; +// Read the version rather than repeating it. A literal here is a second source +// of truth that nothing checks, and it had already drifted: package.json said +// 0.1.0 while the shipped CLI was on 1.2.0. Resolved relative to this module, +// so it works from src/ under tsx and from dist/ after a build. +const { version } = JSON.parse( + readFileSync(new URL('../package.json', import.meta.url), 'utf8') +) as { version: string }; + const BASE_URL = process.env.C0UPONS_API_URL ?? 'https://c0upons.com'; const program = new Command(); @@ -11,7 +20,7 @@ const program = new Command(); program .name('c0upons') .description('Search, submit, and manage coupon codes from the terminal') - .version('0.1.0'); + .version(version); program .command('search ') diff --git a/apps/web/package.json b/apps/web/package.json index d47e739..bb7f68b 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,6 +1,6 @@ { "name": "@c0upons/web", - "version": "0.1.0", + "version": "1.2.0", "private": true, "type": "module", "scripts": { diff --git a/package.json b/package.json index e67a302..5ad4991 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "c0upons", - "version": "0.1.0", + "version": "1.2.0", "private": true, "type": "module", "packageManager": "pnpm@9.15.0", @@ -19,7 +19,7 @@ "lint": "turbo run lint", "typecheck": "turbo run typecheck", "clean": "turbo run clean && rm -rf node_modules .turbo", - "test": "turbo run test", + "test": "node --test test/*.test.mjs && turbo run test", "setup:hooks": "git config core.hooksPath .githooks && echo 'Git hooks configured'" }, "devDependencies": { diff --git a/test/versions.test.mjs b/test/versions.test.mjs new file mode 100644 index 0000000..a686317 --- /dev/null +++ b/test/versions.test.mjs @@ -0,0 +1,48 @@ +// The repo carries its version in more places than anyone can hold in their +// head, and they had already drifted apart: the root and both workspaces said +// 0.1.0, the shipped CLI said 1.2.0, and the git tag agreed with the CLI. The +// shell script is the one that users actually see (`c0upons version`), so a +// silent disagreement means the version someone reports in a bug is not the +// version of the tree it came from. +// +// Nothing derives these automatically — the shell script is downloaded +// standalone, so its version has to be a literal — which is exactly why it +// needs a check instead of a convention. +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { readFileSync } from 'node:fs'; + +const read = (p) => JSON.parse(readFileSync(new URL(p, import.meta.url), 'utf8')); + +const root = read('../package.json'); +const web = read('../apps/web/package.json'); +const cli = read('../apps/cli/package.json'); + +/** The literal the shipped shell CLI reports as `c0upons version`. */ +function shippedCliVersion() { + const script = readFileSync(new URL('../apps/web/public/cli/c0upons', import.meta.url), 'utf8'); + const match = /^VERSION="([^"]+)"$/m.exec(script); + assert.ok(match, 'could not find a VERSION="…" line in apps/web/public/cli/c0upons'); + return match[1]; +} + +test('every workspace agrees with the root version', () => { + assert.equal(web.version, root.version, '@c0upons/web disagrees with the root package version'); + assert.equal(cli.version, root.version, '@c0upons/cli disagrees with the root package version'); +}); + +test('the shipped CLI reports the repo version', () => { + assert.equal( + shippedCliVersion(), + root.version, + 'apps/web/public/cli/c0upons VERSION= is out of step with package.json — bump both, since the ' + + 'script is served standalone and cannot read package.json at runtime' + ); +}); + +// The upgrade path replaces the binary from this URL, so a CLI that points +// somewhere else would strand every installed copy on its current version. +test('the shipped CLI upgrades from the canonical URL', () => { + const script = readFileSync(new URL('../apps/web/public/cli/c0upons', import.meta.url), 'utf8'); + assert.match(script, /https:\/\/c0upons\.com\/cli\/c0upons/); +});