Mark as done:
- include: remote: URL fetching
- workflow:rules:variables: propagation
- Expression evaluator: multi-line, \${VAR}, regex flags, variable regex
RHS, bare true/false/integer literals
- File and line numbers on findings
- needs: optional: true downgraded to warning
- extends: missing script downgraded to warning
- glint graph includes jobs-per-file
Add new remaining items:
- rules:if: static reachability analysis (future)
- Findings quality section broken out from lint coverage
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
9.5 KiB
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
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 matchingworkflow:rules:entry are injected into the evaluation context before jobrules:if:expressions are evaluated. Pipeline-levelvariables: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_BRANCHeverywhere. - ✓ Expression evaluator: regex flags —
/pattern/i,/pattern/m,/pattern/sare now supported;imaps to(?i)in Go's regexp. - ✓ Expression evaluator: variable as regex RHS —
$BRANCH =~ $PATTERNwhere$PATTERNholds a/regex/string is evaluated correctly. - ✓ Expression evaluator: bare
true/falsekeywords — treated as the strings"true"/"false"matching GitLab CI's own behaviour;$GATEWAY_ENABLED == truenow evaluates correctly. - ✓ Expression evaluator: integer literals —
$COUNT == 4,$ENABLED == 1,$DISABLED == 0compare as decimal strings.
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 — warn when a job references
$VAR(or${VAR}) that is not declared anywhere invariables:,default.variables, or the job itself services:validation — map form requiresname;aliasmust be a valid DNS labelrules:changes/rules:exists— warn on glob patterns that can never match (e.g. absolute paths, double**on unsupported versions)timeoutformat — must be a duration string GitLab understands (1h 30m,90 minutes, etc.)id_tokens:/secrets:— presence and required-key checkspages:publish— validate that the path is consistent withartifacts.pathsinherit:completeness — flag when a job overrides a default field that would requireinherit: default: falseto suppress- Unreachable jobs — detect jobs that can never run because every
rules:branch evaluates tonever(static analysis only, no variable expansion) - 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
— ✓ shipped in v0.2.0; local files are read from disk, recursively resolved, and merged before lintinginclude: local:full resolution— ✓ shipped post-v0.2.0; plain HTTPS URLs are fetched (unauthenticated), parsed, and merged; sub-includes are resolved recursively; unreachable URLs emitinclude: remote:(URL)[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;
--offlineflag 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 correctstage: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=…::messagelines 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 asglint graph tree; stages as branches, jobs as leaves, context-aware annotations— ✓ shipped post-v0.2.0; each include node shows the jobs it defines as dashed-arrow rounded nodes in a distinct styleglint graph includesshows jobs per file- 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 showstage,when,image, andneedson hover when: on_failurevisual 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
.htmlfile with pan/zoom and a job-detail sidebar; no external dependencies - Mermaid pipeline output — keep
pipeline.gobut wire it up through--graph pipeline --format mermaidfor users who want to paste into mermaid.live
Findings quality — ✓ file and line numbers shipped post-v0.2.0
File and line numbers on findings — ✓ shipped post-v0.2.0; every [ERROR] / [WARNING] now includes the source file and exact line of the job key (e.g. job "deploy" (src/deploy.yml:14): …). Works across local includes, remote project templates, and fetched component templates.
Remaining improvements
— ✓ shipped post-v0.2.0; optional missing needs are downgraded toneeds: optional: truefalse-positive errors[WARNING]— ✓ shipped post-v0.2.0; jobs usingextends:jobs with missing script false errorsextends:that have noscriptafter resolution emit[WARNING](the script may come from an unfetchable remote base)rules:if:static reachability — report when a job's entirerules:block can never evaluate towhen: on_successgiven the declared pipeline variables (pure static, no context required)
CI / editor integration
- GitLab CI template — a
.gitlab-ci.ymlsnippet that runsglintas a pipeline-validation job before the real pipeline executes; publishable to the GitLab CI/CD Catalog - GitHub Actions action —
uses: k3nny/glint@v1wrapper for repositories that mirror or manage GitLab pipelines from GitHub - Pre-commit hook — entry for pre-commit so
glintruns automatically ongit commitwhen.gitlab-ci.ymlchanges - LSP server —
glint lspmode 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.ymlconfig 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
stagesallowlist for projects that use a non-standard default set - Token and URL defaults so flags are not needed in every invocation
- Rule suppression by rule ID (e.g.
- Inline suppression comments —
# glint: ignore next-line <rule-id>in the pipeline YAML
Reliability and developer experience
- Structured rule IDs — assign a stable short ID to every rule (e.g.
GS001) so suppression, documentation, and SARIF output are stable across versions --explain <rule-id>— print the rule description, rationale, and an example fixSemantic versioning and first release— shipped asv0.1.0(2026-06-07)Subcommand CLI— shipped asv0.2.0(2026-06-11);glint check/glint graph [mode]with ruff-style--help- Changelog automation — generate release notes from Conventional Commits via
git-cliffor similar - Fuzz testing — add a
go test -fuzztarget for the YAML parser to harden it against malformed input