Files
glint/ROADMAP.md
T
k3nny 54b5850835
ci / vet, staticcheck, test, build (push) Successful in 2m12s
release / Build and publish release (push) Successful in 1m7s
feat(linter): GL033 static dead-rules detection
Add rule GL033 that warns when every rule in a job's rules: block has
an explicit when: never, making the job permanently excluded from any
pipeline run. This is a pure static check — no if: evaluation or context
required. Only rules with literal when: never trigger it; rules with no
when: (defaults to on_success), when: manual, when: always, or
when: on_failure are treated as reachable.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-14 09:13:19 +02:00

12 KiB
Raw Permalink Blame History

Roadmap

This document tracks planned improvements to glint. Items are grouped by theme, roughly in priority order within each group. Nothing here is a commitment — the tool is experimental and the list will shift as real usage surfaces better priorities.


Context-aware validation — ✓ single-context shipped in v0.2.0; expression evaluator hardened post-v0.2.0; implicit defaults and --list-vars shipped v0.2.11; variable expansion and scalar handling shipped v0.2.13; workflow evaluation and output fixes shipped v0.2.14

Single-context simulation is fully implemented. Pass --branch, --tag, --source, or --var to either glint check or glint graph; jobs are evaluated and shown as active / manual / skipped.

# shipped: single-context simulation
glint check --branch develop .gitlab-ci.yml
glint check --tag v1.2.0 .gitlab-ci.yml
glint check --source merge_request_event --var CI_MERGE_REQUEST_TARGET_BRANCH_NAME=main .gitlab-ci.yml
glint graph tree --branch main .gitlab-ci.yml   # tree annotated with [skipped] / [manual]

Shipped post-v0.2.0 (unreleased)

  • workflow:rules:variables: propagation — variables defined on the matching workflow:rules: entry are injected into the evaluation context before job rules:if: expressions are evaluated. Pipeline-level variables: defaults are also available. Priority chain (highest wins): --var > shortcuts > workflow-rule vars > pipeline defaults.
  • Expression evaluator: multi-line expressions — newlines in block-scalar and folded YAML if: values are now treated as whitespace; || / && on a continuation line evaluate correctly.
  • Expression evaluator: ${VAR} curly-brace syntax${CI_COMMIT_BRANCH} is equivalent to $CI_COMMIT_BRANCH everywhere.
  • Expression evaluator: regex flags/pattern/i, /pattern/m, /pattern/s are now supported; i maps to (?i) in Go's regexp.
  • Expression evaluator: variable as regex RHS$BRANCH =~ $PATTERN where $PATTERN holds a /regex/ string is evaluated correctly.
  • Expression evaluator: bare true / false keywords — treated as the strings "true" / "false" matching GitLab CI's own behaviour; $GATEWAY_ENABLED == true now evaluates correctly.
  • Expression evaluator: integer literals$COUNT == 4, $ENABLED == 1, $DISABLED == 0 compare as decimal strings.

Implicit default context — ✓ shipped v0.2.11; glint check and glint graph default to --branch main --source push when no context flag is given, so rules:if: expressions are always evaluated out of the box.

--list-vars debug flag — ✓ shipped v0.2.11; prints sorted KEY=VALUE of all collected variables (pipeline YAML + included files + workflow-rule union + effective context) to stderr.

Shipped in v0.2.13

  • Variable expansion$VAR / ${VAR} references within variable values are expanded after all sources are merged; transitive chains resolve over multiple passes; visible in --list-vars effective-context output.
  • Non-string scalar variablesBUILD: true, RETRIES: 3 and similar bare boolean/integer values now render correctly in --list-vars and are injected into the evaluation context as string equivalents; previously shown as (complex) and silently dropped.
  • YAML \/ escape in double-quoted strings — regex patterns like /^us\// in double-quoted if: blocks no longer cause a parse error; the raw bytes are preprocessed before YAML unmarshalling.

Shipped in v0.2.14

  • Workflow rule strict evaluation — workflow rules:if: now uses strict mode (parse failure → skip rule, not match); fixes premature matching that blocked later rules and injected wrong variables.
  • Single = operator= is now accepted as an alias for == in rules:if: expressions, matching common user intent.
  • Source location through extends: resolutionFile and Line are now preserved when a job is rebuilt via extends, so findings reference the correct source location.
  • Sorted findings output — findings are sorted by (File, Line, Rule); same-file issues group together in line order.
  • Consistent warning format — all warnings use ruff-style path: [warning] message format.
  • --version / -v flag — prints compiled version; version also shown at the top of every --help output.

Remaining work

  • Multi-context simulation — run multiple contexts in one invocation and print a comparison table:
    glint check --context branch=main --context branch=develop --context tag=v1.0.0 .gitlab-ci.yml
    
  • Context-scoped linting — skip needs:/dependencies: cross-checks for jobs that are statically unreachable in the given context
  • rules:changes: evaluation — path glob evaluation against the local git tree

Lint coverage

The current rule set covers the most common sources of broken pipelines. These are the gaps most likely to matter in practice.

  • Variable reference validation (GL032) — ✓ shipped v0.2.11; warns when a rules:if: expression references $VAR / ${VAR} not declared anywhere in pipeline YAML; predefined GitLab namespaces (CI_*, GITLAB_*, …) exempt; variables from included files are also considered
  • rules:if: static reachability (GL033) — ✓ shipped v0.2.15; warns when every rule in a job's rules: block has when: never, making the job permanently excluded from any pipeline run; no if: evaluation required
  • services: validation — map form requires name; alias must be a valid DNS label
  • rules:changes / rules:exists — warn on glob patterns that can never match (e.g. absolute paths, double ** on unsupported versions)
  • timeout format — must be a duration string GitLab understands (1h 30m, 90 minutes, etc.)
  • id_tokens: / secrets: — presence and required-key checks
  • pages:publish — validate that the path is consistent with artifacts.paths
  • inherit: completeness — flag when a job overrides a default field that would require inherit: default: false to suppress
  • Unreachable jobs — detect jobs that can never run because every rules: branch evaluates to never (static analysis only)
  • Duplicate stage names — GitLab silently merges them; warn to avoid confusion
  • cache:key:files — must be a list of paths, not a glob

Include resolution

  • include: local: full resolution — ✓ shipped in v0.2.0; local files are read from disk, recursively resolved, and merged before linting
  • include: remote: (URL) — ✓ shipped post-v0.2.0; plain HTTPS URLs are fetched (unauthenticated), parsed, and merged; sub-includes are resolved recursively; unreachable URLs emit [WARNING] and linting continues
  • Recursive include depth limit — guard against include cycles across files
  • Offline mode / cache — persist fetched remote templates to a local cache directory; --offline flag to skip network calls and use only cached copies
  • include: inputs: — substitute CI component input values into fetched templates before merging, so component-scoped jobs get their correct stage: and keyword values

Output formats

Right now the only output is plain-text findings. Structured output enables integration with other tools.

  • JSON (--format json) — machine-readable findings with file, job, severity, rule ID, and message; stable schema
  • SARIF (--format sarif) — Static Analysis Results Interchange Format; consumed natively by GitHub Code Scanning and GitLab SAST
  • JUnit XML (--format junit) — lets CI pipelines publish lint results as a test report artifact
  • GitHub / GitLab annotation format — emit ::error file=…,line=…::message lines so findings appear as inline comments in PR diffs

Pipeline graph improvements

The SVG renderer and terminal tree cover the basic layout. These would bring it closer to GitLab's full interactive view.

  • Terminal job tree — ✓ shipped in v0.2.0 as glint graph tree; stages as branches, jobs as leaves, context-aware annotations
  • glint graph includes shows jobs per file — ✓ shipped post-v0.2.0; each include node shows the jobs it defines as dashed-arrow rounded nodes in a distinct style
  • Multi-job connector accuracy — draw one connector per job pair rather than one per stage pair in classic mode, so pipelines with uneven columns look correct
  • Job tooltip / detail panel — embed a hidden <title> and <desc> per chip so SVG viewers show stage, when, image, and needs on hover
  • when: on_failure visual distinction — dashed border or distinct icon for failure-path jobs
  • Blocked / skipped state colouring — grey out jobs that are statically unreachable given known rules: conditions
  • Interactive HTML output — self-contained .html file with pan/zoom and a job-detail sidebar; no external dependencies
  • Mermaid pipeline output — keep pipeline.go but wire it up through --graph pipeline --format mermaid for users who want to paste into mermaid.live

Findings quality — ✓ file and line numbers shipped post-v0.2.0; ruff-style format shipped v0.2.11

File and line numbers on findings — ✓ shipped post-v0.2.0; every finding includes the source file and exact line of the job key. Works across local includes, remote project templates, and fetched component templates.

Ruff-style output format — ✓ shipped v0.2.11; findings follow file:line: RULEID [severity] message matching the convention used by ruff and other modern linters.

Remaining improvements

  • needs: optional: true false-positive errors — ✓ shipped post-v0.2.0; optional missing needs are downgraded to [WARNING]
  • extends: jobs with missing script false errors — ✓ shipped post-v0.2.0; jobs using extends: that have no script after resolution emit [WARNING] (the script may come from an unfetchable remote base)
  • rules:if: static reachability — report when a job's entire rules: block can never evaluate to when: on_success given the declared pipeline variables (pure static, no context required)

CI / editor integration

  • GitLab CI template — a .gitlab-ci.yml snippet that runs glint as a pipeline-validation job before the real pipeline executes; publishable to the GitLab CI/CD Catalog
  • GitHub Actions actionuses: k3nny/glint@v1 wrapper for repositories that mirror or manage GitLab pipelines from GitHub
  • Pre-commit hook — entry for pre-commit so glint runs automatically on git commit when .gitlab-ci.yml changes
  • LSP serverglint lsp mode exposing diagnostics over the Language Server Protocol; enables inline squiggles in VS Code, JetBrains, Neovim, etc. without a dedicated extension
  • VS Code extension — thin wrapper around the LSP server with syntax highlighting for .gitlab-ci.yml

Configuration

  • .glint.yml config file — project-level configuration for:
    • Rule suppression by rule ID (e.g. ignore: [no-only, missing-stages])
    • Severity overrides (demote specific errors to warnings)
    • Custom stages allowlist for projects that use a non-standard default set
    • Token and URL defaults so flags are not needed in every invocation
  • Inline suppression comments# glint: ignore next-line <rule-id> in the pipeline YAML

Reliability and developer experience

  • Structured rule IDs — ✓ shipped post-v0.2.0; GL001GL031 assigned; GL032 added v0.2.11
  • --explain <rule-id> — print the rule description, rationale, and an example fix
  • Semantic versioning and first release — shipped as v0.1.0 (2026-06-07)
  • Subcommand CLI — shipped as v0.2.0 (2026-06-11); glint check / glint graph [mode] with ruff-style --help
  • Changelog automation — generate release notes from Conventional Commits via git-cliff or similar
  • Fuzz testing — add a go test -fuzz target for the YAML parser to harden it against malformed input