mirror of
https://github.com/profullstack/logicsrc.git
synced 2026-08-14 14:57:28 +00:00
fix(cli): make logicsrc update actually check for updates
`update` was three hardcoded console.log lines: it printed 0.1.0 as both current and latest, claimed "already up to date", and never checked or installed anything. `--version` was hardcoded the same way. A version comparison alone could not have worked either. install.sh ships a tarball of the master branch, not a tagged release, and packages/cli/package.json has been 0.1.0 since the repo began, so version equality says "up to date" no matter how far master has moved. The commit is the real signal. - install.sh records ref/commit/version/installed_at to $LOGICSRC_HOME/install.json. The sha comes from GitHub's Accept: application/vnd.github.sha media type, so this needs no jq. It is resolved before the download on purpose: if master moves mid-install we under-report (a spurious update) rather than falsely claim to be current. - update compares the installed commit against the remote ref head, falls back to version comparison for installs predating the manifest, and reports why it reached its verdict instead of just asserting one. --check reports without installing; otherwise it re-runs the installer. - --version now reads the package's real version. Verified against live GitHub in all three states: matching commit, stale commit, and no manifest. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
cf475f0f4c
commit
8c3c0bb12a
4 changed files with 339 additions and 8 deletions
95
packages/cli/src/update.test.ts
Normal file
95
packages/cli/src/update.test.ts
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { compareVersions, parseManifest, trackedRef, updateStatus } from "./update.js";
|
||||
|
||||
describe("compareVersions", () => {
|
||||
it("orders releases numerically, not lexically", () => {
|
||||
expect(compareVersions("0.2.0", "0.1.0")).toBe(1);
|
||||
expect(compareVersions("0.1.0", "0.2.0")).toBe(-1);
|
||||
expect(compareVersions("0.1.0", "0.1.0")).toBe(0);
|
||||
// "10" > "9" numerically but sorts lower as a string.
|
||||
expect(compareVersions("0.10.0", "0.9.0")).toBe(1);
|
||||
expect(compareVersions("1.0.0", "0.99.99")).toBe(1);
|
||||
});
|
||||
|
||||
it("tolerates v-prefixes, prereleases, and short versions", () => {
|
||||
expect(compareVersions("v1.2.3", "1.2.3")).toBe(0);
|
||||
expect(compareVersions("1.2.0-beta.1", "1.2.0")).toBe(0);
|
||||
expect(compareVersions("1.2", "1.2.0")).toBe(0);
|
||||
expect(compareVersions("garbage", "0.0.0")).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("parseManifest", () => {
|
||||
it("reads a manifest written by install.sh", () => {
|
||||
const m = parseManifest(
|
||||
JSON.stringify({ ref: "master", commit: "abc1234def", version: "0.1.0", installed_at: "2026-07-28T00:00:00Z" })
|
||||
);
|
||||
expect(m).toEqual({ ref: "master", commit: "abc1234def", version: "0.1.0", installed_at: "2026-07-28T00:00:00Z" });
|
||||
});
|
||||
|
||||
it("defaults the ref and nulls empty or missing fields", () => {
|
||||
// install.sh writes empty strings when the sha lookup or version read fails.
|
||||
expect(parseManifest(JSON.stringify({ commit: "", version: "" }))).toEqual({
|
||||
ref: "master",
|
||||
commit: null,
|
||||
version: null,
|
||||
installed_at: null
|
||||
});
|
||||
});
|
||||
|
||||
it("returns null for junk rather than throwing", () => {
|
||||
expect(parseManifest("not json")).toBeNull();
|
||||
expect(parseManifest("[]")).toBeNull();
|
||||
expect(parseManifest("null")).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("trackedRef", () => {
|
||||
it("prefers the environment, then the manifest, then master", () => {
|
||||
const manifest = { ref: "next", commit: null, version: null, installed_at: null };
|
||||
expect(trackedRef(manifest, { LOGICSRC_REF: "experiment" })).toBe("experiment");
|
||||
expect(trackedRef(manifest, {})).toBe("next");
|
||||
expect(trackedRef(null, {})).toBe("master");
|
||||
});
|
||||
});
|
||||
|
||||
describe("updateStatus", () => {
|
||||
const local = { version: "0.1.0", commit: "aaaaaaaaaaaa" };
|
||||
|
||||
it("reports up to date only when the commit actually matches", () => {
|
||||
const s = updateStatus(local, { version: "0.1.0", commit: "aaaaaaaaaaaa" });
|
||||
expect(s.upToDate).toBe(true);
|
||||
expect(s.reason).toContain("current commit");
|
||||
});
|
||||
|
||||
it("matches a short sha against a full one", () => {
|
||||
expect(updateStatus({ version: "0.1.0", commit: "aaaaaaa" }, { version: "0.1.0", commit: "aaaaaaaaaaaa" }).upToDate).toBe(true);
|
||||
});
|
||||
|
||||
it("detects a moved branch even when the version is unchanged", () => {
|
||||
// The bug this replaces: version-only comparison called this "up to date"
|
||||
// forever, because the installer ships a branch tarball, not a release.
|
||||
const s = updateStatus(local, { version: "0.1.0", commit: "bbbbbbbbbbbb" });
|
||||
expect(s.upToDate).toBe(false);
|
||||
expect(s.reason).toContain("moved on");
|
||||
});
|
||||
|
||||
it("detects a newer published version", () => {
|
||||
const s = updateStatus(local, { version: "0.2.0", commit: "aaaaaaaaaaaa" });
|
||||
expect(s.upToDate).toBe(false);
|
||||
expect(s.reason).toContain("0.1.0 → 0.2.0");
|
||||
});
|
||||
|
||||
it("never claims to be current when the local commit is unknown", () => {
|
||||
const s = updateStatus({ version: "0.1.0", commit: null }, { version: "0.1.0", commit: "bbbbbbbbbbbb" });
|
||||
expect(s.upToDate).toBe(false);
|
||||
expect(s.reason).toContain("predates update tracking");
|
||||
});
|
||||
|
||||
it("does not invent an update when GitHub is unreachable", () => {
|
||||
const s = updateStatus(local, { version: null, commit: null });
|
||||
expect(s.upToDate).toBe(true);
|
||||
expect(s.reason).toContain("could not reach GitHub");
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue