Skip to content

feat: add library variable set list and view commands - #697

Draft
NickJosevski wants to merge 1 commit into
mainfrom
nj/issue-346
Draft

feat: add library variable set list and view commands#697
NickJosevski wants to merge 1 commit into
mainfrom
nj/issue-346

Conversation

@NickJosevski

Copy link
Copy Markdown
Contributor

Refs #346

What this adds

A new top-level octopus library-variable-set command (aliases library-variable-sets, lvs) with two read-only subcommands.

library-variable-set list — lists the variable sets in the space, sorted by name, with --filter/-q for substring matching. Script modules share the libraryvariablesets endpoint and are excluded (the existing sharedVariable.GetAllLibraryVariableSets helper already filters on ContentType == "Variables").

library-variable-set view [<name> | <id>] — this is the part aimed at the issue. It resolves the set, fetches its variables, and groups every value stored under the same variable name into one entry, with the unscoped value first because it is the fallback the scoped ones override. --filter/-q narrows to variables whose name contains a string; --web/-w opens the set in the browser. Prompts for the set in interactive mode, errors in --no-prompt.

This addresses both pain points named in the issue:

  • "scoped and non-scoped versions show up as separate entries" — they are now one entry with several values.
  • "switching between /libraryvariablesets/ and /variables/ doesn't make much sense" — one command does both requests and stitches the result together.

Scope IDs are resolved to display names via the variable set's ScopeValues, falling back to the raw ID rather than erroring, so one unresolvable scope value can't break the whole view (this differs deliberately from pkg/cmd/project/variables/shared.ToScopeValues, which returns an error).

Table output (-f table, the default) repeats the variable name only on the first row of each group:

NAME         VALUE            SCOPE                    ID
Slack.Token  ***              (unscoped)               Variables-4
Slack.Url    https://default  (unscoped)               Variables-1
             https://prod     Environment: Production  Variables-2
             https://test     Environment: Test        Variables-3

-f json emits the set's own fields plus Variables as an array of {Name, Values[]}, each value carrying Id, Value, IsSensitive, IsScoped, a Scope object using the API's own property names (Environment, Role, Machine, TenantTag, …) with resolved names, and a flat ScopeSummary string. -f basic prints a grouped text block. Sensitive values are masked as ***.

This also lights up the previously-unused annotations.IsLibrary group, so the root help now has a LIBRARY COMMANDS section.

Deliberately left out

  • No create / update / delete, of sets or of variables. Editing a variable through /api/{space}/variables/variableset-… is a read-modify-write of the whole variable set with optimistic concurrency on Version; doing it safely (and prompting for scopes) is a much bigger surface than a first slice should carry, and it deserves its own design discussion — see the open questions below.
  • Script modules — filtered out of list, and view won't resolve one.
  • Variable set templates (the tenant-facing variable templates on a set) — view reports only TemplateCount.
  • No new selector in pkg/question/selectors — resolution lives in pkg/cmd/libraryvariableset/shared, mirroring pkg/cmd/channel/shared.ResolveChannel. Worth promoting if a second command needs it.

Follow-up surface, roughly in the order I'd do it: create/delete of a set (cheap, the SDK has Add/Update/DeleteByID); variable create/update/delete within a set, reusing pkg/cmd/project/variables/shared scope flags; script-module support; templates.

Test evidence

go build ./... — clean.

go test ./pkg/... — pass, exit 0, no failures. New tests:

  • pkg/cmd/libraryvariableset/shared/shared_test.go — grouping (including case-insensitive names and unscoped-first ordering), scope ID→name resolution, fallback to the raw ID for unknown scope values, tolerance of a missing ScopeValues lookup, sensitive masking.
  • pkg/cmd/libraryvariableset/list/list_test.go — table/json/basic, name filter, script modules excluded.
  • pkg/cmd/libraryvariableset/view/view_test.go — required-in-automation-mode error, unknown-set error, interactive prompt, grouped table, basic, json.

Test support changes: NewLibraryVariableSet / NewVariableSetForLibraryVariableSet fixtures, and the LibraryVariables + Variables links added to testutil.NewRootResource() (they were missing, so the SDK couldn't build those service paths under test).

Open questions / options

1. Command noun. I went with library-variable-set.

  • library-variable-set (chosen) — matches the API resource and the Octopus UI's "Library › Variable Sets", and matches the existing kebab-case multi-word convention (worker-pool, project-group, build-information). Downside: long to type, hence the lvs alias.
  • variable-set — shorter, but ambiguous with project variable sets and with octopus project variable.
  • library variable-set — a library parent for future library certificate, library script-module, library step-template etc. Arguably the tidiest long-term shape, but it commits us to a library namespace now, and none of the sibling commands exist.

Recommendation: keep library-variable-set. If we later want a library parent we can add it and alias.

2. Should variable editing live in the CLI at all?

  • Read-only (this PR) — cheap, safe, and already solves the issue's stated problem, which is understanding a variable set, not editing one.
  • Full CRUD — the issue author links the "update a variable set variable" docs page, so they clearly want to write too. But the write path is a whole-variable-set read-modify-write with a Version for optimistic concurrency; a CLI that silently clobbers a concurrent edit would be worse than no CLI. It also needs sensitive-value handling (the server never returns them, so a naive round-trip would blank them).
  • Middle ground — support set-value for an existing variable identified by name + scope, which is the common case and can be done as a targeted read-modify-write with the version echoed back.

Recommendation: ship read-only now; do the middle ground next, with explicit conflict detection on Version, before attempting general create/delete. I'd like a call on whether we're comfortable with the CLI writing variable sets at all before I build it.

3. How should scoping be displayed and filtered? Currently: names resolved from ScopeValues, joined as Environment: Production, Test; Role: web-server, with (unscoped) for the empty scope, and the only filter is on variable name.

Open sub-questions:

  • Should view gain scope filters (--environment-scope, --role-scope, …) matching the flag names pkg/cmd/project/variables/shared already uses for writes? Consistent, but it's seven more flags on a read command and the semantics of "matching" a partial scope are not obvious (exact set match? contains?).
  • Should JSON carry scope IDs as well as names? Right now it carries a name, falling back to the ID when unresolvable — which means a consumer can't reliably tell which it got. Emitting both (Scope with names, ScopeIds with raw IDs) is more useful for tooling but noisier.
  • Should the table repeat the variable name on every row instead of blanking it? Blanking reads better for a human; repeating is friendlier to grep/awk. -f json is the answer for machines, so I chose readability — but it is a judgement call.

Recommendation: leave the filters as name-only for now; add ScopeIds to the JSON if anyone is actually scripting against it. Happy to change the table blanking if reviewers prefer repetition.

🤖 Generated with Claude Code

Adds `octopus library-variable-set` with `list` and `view`. `view` fetches
the set and its variables in one command and groups values that share a
name, so a variable with several scoped values reads as one entry instead
of several, in table, basic and json output.

Read-only for now; create/update/delete of sets and variables is not
included.

Refs #346

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.

1 participant