Skip to content

Commit 2fac42e

Browse files
committed
fix(init): ask for a reinstall only when a dependency pin moves, and stop calling @latest the baked specifier
A CI, hook, or script pin is a launcher line with nothing to reinstall, so the stale-pin notice and the init recipe now ask for a dependency reinstall only when a package.json dependency pin is among the stale ones. The package-manager docblock now gives a pinned release as the baked specifier example.
1 parent 6803171 commit 2fac42e

4 files changed

Lines changed: 37 additions & 6 deletions

File tree

‎packages/cli/src/agent/init.md‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,10 @@ first step, not the whole job.
8383
or commit on your own; the git operations are theirs.
8484

8585
3. **Offer to bump every stale pin.** For each `pinnedCli` entry, offer
86-
the user the update to the installed CLI, along with reinstalling
87-
dependencies. When the installed CLI is a nightly and the pin names
86+
the user the update to the installed CLI. A pin in a `package.json`
87+
dependency field also needs dependencies reinstalled; a script, CI, or
88+
hook pin is a launcher line, and editing it is the whole fix. When the
89+
installed CLI is a nightly and the pin names
8890
`@taskless/cli`, or the reverse, the move switches package, since a
8991
nightly version only exists on the nightly package. When `migrated`
9092
is present with `from` above `0`, say plainly that CI breaks without

‎packages/cli/src/install/pinned-cli.ts‎

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -472,13 +472,22 @@ export function getPinnedCliNotice(
472472
options.migrated !== undefined && options.migrated.from > 0
473473
? options.migrated
474474
: undefined;
475+
// Only a dependency pin has anything to reinstall. A script, CI, or hook pin
476+
// is a launcher line, and editing it is the whole fix.
477+
const reinstall = pins.some((pin) =>
478+
(DEPENDENCY_FIELDS as readonly string[]).includes(pin.location)
479+
);
475480
const consequence =
476481
upgraded === undefined
477482
? `Anything that runs these pins (a script, CI, a git hook) runs a CLI older than this project expects and will likely fail. ` +
478-
`Offer to update them as shown as part of this change, then reinstall dependencies.`
483+
(reinstall
484+
? `Offer to update them as shown as part of this change, then reinstall dependencies.`
485+
: `Offer to update them as shown as part of this change.`)
479486
: `This upgrade migrated .taskless/ from schema version ${String(upgraded.from)} to ${String(upgraded.to)}, and a CLI that predates that schema refuses the project (SCAFFOLD_VERSION_MISMATCH). ` +
480487
`CI, scripts, and git hooks that run these pins will break on the push that carries the migrated files. ` +
481-
`Offer to update them as shown and reinstall dependencies, in the same commit as .taskless/.`;
488+
(reinstall
489+
? `Offer to update them as shown and reinstall dependencies, in the same commit as .taskless/.`
490+
: `Offer to update them as shown, in the same commit as .taskless/.`);
482491
return renderNoticeBox("UPDATE PINNED TASKLESS VERSIONS", [
483492
`This project pins a Taskless CLI older than ${cliVersion}, the version that just ran here:`,
484493
{ items: pins.map((pin) => describePin(pin, cliVersion)) },

‎packages/cli/src/util/package-manager.ts‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ import { buildInvocation, isProductionInvocation } from "./invocation";
66
* How the CLI answers "what would the reader type to run me again?"
77
*
88
* The answer has two halves that come from different places. The **package
9-
* specifier** (`@taskless/cli@latest`, `@taskless/cli-nightly@0.11.0-…`) is a
10-
* build-time fact, baked in by `scripts/build-target.ts`. The **launcher**
9+
* specifier** (`@taskless/cli@0.11.2`, `@taskless/cli-nightly@0.11.0-…`) is a
10+
* build-time fact, baked in by `scripts/build-target.ts`: every published
11+
* build pins its own version, and `@latest` survives only in
12+
* `readmeSpecifier()`. The **launcher**
1113
* (`npx`, `pnpm dlx`) is a runtime fact, and the only honest source for it is
1214
* the path this process was launched from.
1315
*

‎packages/cli/test/pinned-cli.test.ts‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,24 @@ describe("getPinnedCliNotice", () => {
588588
expect(notice).not.toContain("likely");
589589
});
590590

591+
it("asks for a reinstall only when a dependency pin moves", () => {
592+
expect(pinnedNotice([releasePin], "0.11.2")).toContain(
593+
"reinstall dependencies"
594+
);
595+
// A CI or hook pin is a launcher line: there is nothing to reinstall.
596+
const ciPin: PinnedCli = {
597+
manifest: ".github/workflows/ci.yml",
598+
location: "line 12",
599+
name: "@taskless/cli",
600+
spec: "0.10.2",
601+
installed: null,
602+
};
603+
expect(pinnedNotice([ciPin], "0.11.2")).not.toContain("reinstall");
604+
expect(
605+
pinnedNotice([ciPin], "0.11.2", { migrated: { from: 5, to: 9 } })
606+
).not.toContain("reinstall");
607+
});
608+
591609
it("does not call a fresh install an upgrade", () => {
592610
// A fresh `init` creates `.taskless/` by migrating from schema 0.
593611
const notice = pinnedNotice([releasePin], "0.11.2", {

0 commit comments

Comments
 (0)