The update worker queried the unscoped 'get-shit-done-redux' via
`npm view`, which returns E404 — so `latest` stayed null and
`update_available` could never become true. Now derives the name from
package.json (`require('../package.json').name`) so it always matches
the actual published scoped name (@opengsd/get-shit-done-redux).
Fixes #378.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
5
.changeset/378-update-check-scoped-name.md
Normal file
5
.changeset/378-update-check-scoped-name.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
type: Fixed
|
||||
pr: 378
|
||||
---
|
||||
Update check now polls the correct scoped package name (@opengsd/get-shit-done-redux) so update_available works.
|
||||
@@ -13,6 +13,9 @@ const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { execFileSync } = require('child_process');
|
||||
const { isSemverNewer } = require('../get-shit-done/bin/lib/semver-compare.cjs');
|
||||
// Derive the published package name from package.json so this survives
|
||||
// future renames and always matches the actual registry entry (#378).
|
||||
const PACKAGE_NAME = require('../package.json').name;
|
||||
|
||||
const cacheFile = process.env.GSD_CACHE_FILE;
|
||||
const projectVersionFile = process.env.GSD_PROJECT_VERSION_FILE;
|
||||
@@ -79,7 +82,7 @@ if (configDir) {
|
||||
|
||||
let latest = null;
|
||||
try {
|
||||
latest = execFileSync('npm', ['view', 'get-shit-done-redux', 'version'], {
|
||||
latest = execFileSync('npm', ['view', PACKAGE_NAME, 'version'], {
|
||||
encoding: 'utf8',
|
||||
timeout: 10000,
|
||||
windowsHide: true,
|
||||
|
||||
98
tests/bug-378-update-check-scoped-name.test.cjs
Normal file
98
tests/bug-378-update-check-scoped-name.test.cjs
Normal file
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* Regression test for #378: gsd-check-update-worker.js must query
|
||||
* the SCOPED package name (@opengsd/get-shit-done-redux) when calling
|
||||
* `npm view <name> version`.
|
||||
*
|
||||
* Background: the worker previously hardcoded the unscoped string
|
||||
* 'get-shit-done-redux', which returns E404 from the npm registry because
|
||||
* the published package is scoped. This caused `latest` to stay null and
|
||||
* `update_available` to be permanently false — users never saw update
|
||||
* notifications.
|
||||
*
|
||||
* The fix derives the name from package.json (most robust — survives
|
||||
* future renames). This test locks the contract in two ways:
|
||||
*
|
||||
* 1. Structural: the worker must NOT contain the bare unscoped literal
|
||||
* 'get-shit-done-redux' as a standalone npm view argument.
|
||||
* 2. Derived: the worker must read the package name from package.json
|
||||
* and the package.json name MUST be the scoped string
|
||||
* '@opengsd/get-shit-done-redux'.
|
||||
*
|
||||
* Source-grep policy: this test reads hook source via readFileSync.
|
||||
* The repo's lint-no-source-grep rule targets bin/lib/get-shit-done — hooks/
|
||||
* is out of scope. The shape we need to lock (which string is passed as the
|
||||
* npm view argument) only manifests at runtime against the live registry;
|
||||
* a structural assertion is the minimum-cost contract.
|
||||
*/
|
||||
|
||||
// allow-test-rule: structural assertion on hook npm-view argument; the
|
||||
// behavior being tested (correct package name → no E404) only manifests at
|
||||
// runtime against the live npm registry, which CI does not call.
|
||||
|
||||
'use strict';
|
||||
|
||||
const { test, describe } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const WORKER_PATH = path.join(__dirname, '..', 'hooks', 'gsd-check-update-worker.js');
|
||||
const PKG_PATH = path.join(__dirname, '..', 'package.json');
|
||||
|
||||
describe('bug #378: update-check worker uses scoped package name', () => {
|
||||
test('worker file exists', () => {
|
||||
assert.ok(fs.existsSync(WORKER_PATH), `worker not found at ${WORKER_PATH}`);
|
||||
});
|
||||
|
||||
test('package.json name is the scoped @opengsd/get-shit-done-redux', () => {
|
||||
const pkg = JSON.parse(fs.readFileSync(PKG_PATH, 'utf8'));
|
||||
assert.equal(
|
||||
pkg.name,
|
||||
'@opengsd/get-shit-done-redux',
|
||||
'package.json must declare the scoped name — this is what npm view must query',
|
||||
);
|
||||
});
|
||||
|
||||
test('worker does NOT hardcode the unscoped get-shit-done-redux as an npm view argument', () => {
|
||||
const src = fs.readFileSync(WORKER_PATH, 'utf8');
|
||||
|
||||
// Strip comments so doc-prose mentions don't trigger the check.
|
||||
const codeOnly = src
|
||||
.replace(/\/\*[\s\S]*?\*\//g, '')
|
||||
.replace(/(^|[^:])\/\/[^\n]*/g, '$1');
|
||||
|
||||
// The unscoped bare string as a string literal used in code.
|
||||
// A match here means the bug is present: npm view 'get-shit-done-redux'
|
||||
// → E404 → update_available permanently false.
|
||||
const unscopedLiteral = /['"]get-shit-done-redux['"]/;
|
||||
|
||||
assert.doesNotMatch(
|
||||
codeOnly,
|
||||
unscopedLiteral,
|
||||
[
|
||||
"Worker must not pass the unscoped 'get-shit-done-redux' to `npm view`.",
|
||||
'That name returns E404, leaving update_available permanently false.',
|
||||
'Use the scoped name from package.json: @opengsd/get-shit-done-redux.',
|
||||
].join(' '),
|
||||
);
|
||||
});
|
||||
|
||||
test('worker derives package name from package.json (require + .name)', () => {
|
||||
const src = fs.readFileSync(WORKER_PATH, 'utf8');
|
||||
|
||||
// Structural check: worker must load package.json and read .name from it.
|
||||
// This is the robust form — survives future renames without code edits.
|
||||
const requiresPkgJson = /require\s*\(\s*['"][^'"]*package\.json['"]\s*\)\.name/;
|
||||
|
||||
assert.match(
|
||||
src,
|
||||
requiresPkgJson,
|
||||
[
|
||||
'Worker must derive the npm view package name via',
|
||||
"`require('../package.json').name` (or similar).",
|
||||
'Hardcoding the scoped literal is less robust: a future rename',
|
||||
'would silently break update checks again.',
|
||||
].join(' '),
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user