Skip to content

cli/cmd: tt package add, remove, update - #1359

Open
bigbes wants to merge 5 commits into
v3from
bigbes/tntp-9958-package-deps-cli
Open

cli/cmd: tt package add, remove, update#1359
bigbes wants to merge 5 commits into
v3from
bigbes/tntp-9958-package-deps-cli

Conversation

@bigbes

@bigbes bigbes commented Aug 31, 2026

Copy link
Copy Markdown
Collaborator

Three commands that edit [dependencies] in app.manifest.toml and re-resolve the lock: add takes * when no constraint is given, writes to [dev_dependencies] under --dev and rewrites the constraint of a dependency already declared; remove drops it from both files; update re-resolves one dependency or all of them.

  • cli/manifest/resolve: prefer pinned versions — editing the manifest changes manifest_hash, which forces a re-resolve, so without pins add and remove would drag every unrelated dependency forward, contradicting the rule that only an explicit update pulls fresh versions from a registry. A pin is a preference: one that no longer fits is dropped with a warning, never an error, and that has to be caught in two places because the greedy walk discovers the two failure modes at different times.
  • cli/manifest: edit dependency tables in place — the manifest is edited rather than re-marshalled, because go-toml does not preserve comments. A position-preserving editor locates the byte range of the entry it touches through the unstable parser and splices the text, so comments, key order and the formatting of every untouched part of the file survive. It handles the short, inline-table, sub-table and root dotted-key forms, and refuses anything else by name instead of guessing.
  • cli/manifest/deps: edit and re-resolve deps — the orchestration the three commands share, exported so tt package resolve can reuse the same re-resolve step.
  • cli/cmd: tt package add, remove, update — the CLI surface plus integration tests.
  • lint/ci: enforce the strict ruleset on every push — found while doing the above: the strict default: all ruleset had a CI job that never ran, because it is gated on a full-commit-check PR label, and two packages had joined its declared scope and accumulated 254 findings unnoticed. The scope now names the packages that are actually clean, the ruleset gets a job with no label condition, and both lint targets resolve and version-check the linter they need instead of using whatever is on PATH.

Closes TNTP-9958

Engine.Resolve always took the newest version a constraint allowed,
so any command that rewrites the manifest - add, remove - would drag
every unrelated dependency forward on the re-resolve the changed
manifest_hash forces. That contradicts the rule IsStale states: only
an explicit tt package update pulls fresh registry versions.

Add ResolvePinned, which prefers a caller-supplied version per rock,
and PinsFromLock, which builds that set from an existing lock minus
the names the caller wants freed. Resolve is now ResolvePinned with
an empty set, so its callers are unaffected.

A pin is a preference, not a constraint: it rides as an extra "=="
on the constraint expression handed to the adapter, and when it no
longer fits - the constraints moved, a later edge in the walk demands
more, or the registry stopped serving that version - it is dropped,
the rock resolves normally and a warning says so. Putting it in the
expression is also what keeps a pinned and an unpinned query for the
same rock in distinct slots of the shared resolution cache.

Part of TNTP-9958
Add an Editor that rewrites [dependencies]/[dev_dependencies] in the
manifest's own TOML text instead of re-marshaling the model, so
comments, key order and the formatting of every untouched line
survive an edit. Locating is done with go-toml's unstable expression
parser; splicing works on the byte ranges it reports.

Short, inline-table, sub-table and root dotted-key declarations are
all editable; anything else is refused by name rather than guessed at.

Part of TNTP-9958
Add the package behind tt package add/remove/update: edit the
declaration with the position-preserving TOML editor, write the
manifest, re-parse it so manifest_hash matches what is on disk,
re-resolve under a per-command pin set and rewrite the lock.

The pin set is what separates the three commands. Add and remove
hold every version the lock already chose; a bare update pins
nothing, which is the only way newer registry versions are pulled;
update NAME frees that one rock and holds the rest.

depguard's allow list gains cli/manifest/resolve, which the strict
ruleset refused even though cli/manifest/build already imports it.

Part of TNTP-9958
Wire the three dependency commands onto cli/manifest/deps. add takes
an optional constraint and a --dev flag choosing the table; remove
and update take a name, and update takes none for a whole-closure
refresh.

Each prints what it changed and then the closure diff, one rock per
line: a rock that arrived shows only its new version, one that left
is marked dropped, one that moved shows both ends.

Closes TNTP-9958
The strict `default: all` ruleset had a job in CI and never ran: that
job is gated on a full-commit-check PR label. Two packages joined its
declared scope in the meantime and accumulated 254 findings between
them with nothing going red.

Scope the config to the packages that are actually clean under it,
naming them rather than claiming the whole cli/manifest subtree - build,
build/backend, inventory and pack are the outstanding work and rejoin
the rule once each lints clean. Then give the ruleset a job of its own
with no label condition. It needs no build step, nothing in the adopted
scope being generated, so it is cheap enough to run on every push.

The linter itself is now resolved rather than assumed. Two configs live
in the tree, one per major version, and neither major can read the
other's format; both targets shelled out to whatever golangci-lint was
on PATH, so a mismatch surfaced as a parser error naming neither the
binary nor the config. Each target states the major it needs, prefers
the version-suffixed name a side-by-side install provides, and verifies
the major it actually got. GOLANGCI_LINT_V1 and GOLANGCI_LINT_V2 name an
alternative binary and are honored exactly, so a wrong major is an error
rather than a silent fallback. The strict ruleset gets its own
lint:strict target, since it needs a v2 linter where lint:golang needs
a v1 one.

Part of TNTP-9958
@bigbes
bigbes force-pushed the bigbes/tntp-9958-package-deps-cli branch from c6191bf to 9edb8aa Compare September 1, 2026 06:26
submodules: recursive

- name: Setup Go
uses: actions/setup-go@v4

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
uses: actions/setup-go@v4
uses: actions/setup-go@v7

Up to you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants