Add variable inspection: `glci variables` and `run --show-variables`
Closes #87 (closed).
Surfaces the CI/CD variables each job resolves to — with the source of every value and the rule-evaluation trace — for debugging variable precedence and rules: matching.
glci variables [job...] (alias vars)
Resolves variables locally without running the pipeline, so jobs excluded by their rules: are still shown (status excluded) — the main way to debug why a rule did or did not match. It focuses on the variables you control: predefined CI_* variables are hidden by default (--all to show them), except any a rule's if: references. The rule trace is shown by default; variables are grouped by source precedence.
deploy_prod (stage: deploy) [included, when: on_success]
Rules:
#0 $CI_COMMIT_BRANCH (main) == $CI_DEFAULT_BRANCH (main) → true
Variables:
KEY VALUE SOURCE
CI_COMMIT_BRANCH main predefined
DEPLOY_ENV production rules
GREETING hello globalFlags: --all/-a, --rules trace (default on), --stage, --json, --unmask, --expand, --env, --env-file, plus shared context/preset/watch flags.
glci run --show-variables
Emits each running job's fully resolved set (including API secrets, dotenv, runtime CI_*) plus its rule trace, just before the job log. Same rendering, predefined-filtering (--all-variables), masking, and truncation as the static command.
Implementation
pkg/variables.Resolve— layered merge with per-value source attribution and anOverriddenchain, sorted by source precedence then key.pkg/rules.EvaluateWithTrace— per-rule outcomes incl. aRefsmap (referenced var → evaluated value);Evaluatedelegates to a sharedwithTrace-gated core so the planner hot path stays allocation-free.MergeVariablesgained an explicit preset layer;--env/--env-fileare now sent as separate request fields so each attributes to its own source (precedence unchanged).- Daemon emits a transient
job_variablesevent (vars + rule trace).
Output details
- Sources:
predefined,global,api:group,api:project,flag(--env),env-file,glci-env,preset,dotenv,job,rules,registry,predefined:job. - Rule trace: each condition rendered with inline values (
$VAR (value)) and→ true/→ false; failingchanges:/exists:rules append a reason. - Truncation: long/multi-line values capped at 512 chars on a single line;
--expandfor full. JSON always carries full values. - JSON: each job carries
rule_trace(withmatchedbooleans +refs) and the fullvariablesarray for programmatic use.
Security
Secrets (masked vars + api:group/api:project sources) are masked by default everywhere they surface — variable table, Overridden chain, and inline rule-condition values — in both the table and --json (--unmask reveals). The job_variables event carries raw values but is transient: delivered only to the live client that requested --show-variables, never retained in the event bus, replayed to other watchers, or written to the on-disk event log.
Docs
user-guide/variables.md (Inspecting resolved variables), debugging.md, cli-reference/commands.md, SKILL.md, ARCHITECTURE.md — all with rendered sample output.
Testing
go build, go vet, gofmt, make test (1800+ unit tests) all green; pre-push glci check (fmt/vet/build) green. New unit tests cover resolution + source ordering, masking (incl. shadowed + rule-trace refs), the rule trace + Refs, condition annotation, predefined filtering, the transient event, and the daemon display helper. Reviewed via two rounds of /review-all (code, perf, security, docs, tests) with all findings fixed.