The README features block grew to 38 bullets plus a full lint-rules table, making the document hard to scan. This commit: - Creates FEATURES.md with a structured reference covering lint rules (GL001–GL043 tables), include resolution, context simulation, output formats, configuration, graph visualization, and developer tools. - Replaces the flat bullet list in README with a 6-line "What it does" category summary that links to FEATURES.md and ROADMAP.md. - Removes the redundant ## Lint rules section from README (now in FEATURES.md). - Adds 'explain' to the commands block in the README Usage section. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
11 KiB
glint — feature reference
This document describes every capability in the current release. For planned work see ROADMAP.md.
Lint rules
Every finding carries a stable rule ID (GL001 – GL043) that can be used to
suppress, filter, or look up the check. Run glint explain <ID> for a
description, bad-YAML example, and fix.
Pipeline-level
| ID | Sev | Rule |
|---|---|---|
| GL001 | WARN | No stages: block — GitLab falls back to its built-in default stages |
| GL002 | ERR | workflow.rules[*].when must be always or never |
| GL036 | ERR | default.timeout is not a valid GitLab CI duration string |
| GL040 | WARN | A stage name appears more than once in stages: |
Job structure
| ID | Sev | Rule |
|---|---|---|
| GL003 | ERR | Job missing required script: (or run:) |
| GL004 | ERR | Job stage: references a stage not declared in stages: |
| GL005 | ERR | only: and rules: used together |
| GL006 | ERR | except: and rules: used together |
| GL007 | WARN | only: / except: used (deprecated; prefer rules:) |
Keyword constraints
| ID | Sev | Rule |
|---|---|---|
| GL008 | ERR | when: has an invalid value |
| GL009 | ERR | when: delayed without start_in: |
| GL010 | ERR | start_in: set but when: is not delayed |
| GL011 | ERR | parallel: integer not in range 2–200, or map form missing matrix: |
| GL012 | ERR | retry: integer not in range 0–2, or retry.max out of range |
| GL013 | ERR | retry.when: contains an unrecognised failure type |
| GL014 | ERR | allow_failure: is not a boolean or a map with exit_codes: |
| GL015 | ERR | interruptible: is not a boolean |
| GL016 | ERR | Trigger job also defines script: |
| GL017 | ERR | trigger: map missing project: or include: |
| GL018 | ERR | coverage: is not a regex wrapped in /…/ |
| GL019 | ERR | release: missing required tag_name:, or is not a map |
| GL020 | ERR | environment.url set without environment.name, or invalid action: |
| GL021 | ERR | artifacts.when invalid, or expose_as set without paths |
| GL022 | WARN | pages job artifacts.paths does not include public/ |
| GL023 | ERR | cache.when or cache.policy has an invalid value |
| GL024 | ERR | rules[*].when has an invalid value |
| GL025 | ERR | image: map form missing name: |
| GL026 | ERR | inherit.default / inherit.variables is not a boolean or list |
| GL034 | ERR | services: map form missing name:, or alias: is not a valid DNS label |
| GL036 | ERR | timeout: is not a valid GitLab CI duration string |
| GL037 | ERR | id_tokens: entry missing required aud: |
| GL038 | ERR | secrets: entry missing a provider key (vault, gcp_secret_manager, azure_key_vault) |
| GL039 | WARN | Job uses pages: keyword but artifacts.paths doesn't include the publish directory |
| GL041 | WARN | cache.key.files entry looks like a glob; must be an exact file path |
Cross-job graph
| ID | Sev | Rule |
|---|---|---|
| GL027 | ERR/WARN | needs: references a job that doesn't exist (WARN when optional: true) |
| GL028 | ERR | needs: references a job in a later stage |
| GL029 | ERR | Circular dependency in needs: graph |
| GL030 | ERR | dependencies: references a job that doesn't exist |
| GL031 | ERR | dependencies: references a job in the same or a later stage |
Expression & reachability
| ID | Sev | Rule |
|---|---|---|
| GL032 | WARN | rules:if: references $VAR not declared in any variables: block (may be false-positive for project-setting variables) |
| GL033 | WARN | Every rule in rules: has when: never — job permanently excluded (provable without evaluating if: expressions) |
| GL035 | WARN | rules:changes / rules:exists path is absolute — absolute paths never match in GitLab CI |
| GL042 | WARN | Every rules:if: condition evaluates to false given the declared variable values — job statically unreachable (only fires when all referenced variables are declared in YAML) |
Inheritance
| ID | Sev | Rule |
|---|---|---|
| GL043 | WARN | inherit: default: declared but the pipeline has no default: block (dead declaration), or the list form names fields not set in default: |
Hidden jobs
Jobs whose name starts with . are reusable templates; most rules are skipped for them. This matches GitLab CI's own behaviour.
Include resolution
| Capability | Notes |
|---|---|
include: local: |
Read from disk, recursively merged before linting |
include: remote: |
HTTPS URL fetched unauthenticated; unreachable URLs produce a warning, linting continues |
include: project: |
Fetched from the GitLab REST API using the configured token; skipped with a warning when no token is available |
include: component: |
Fetched from the GitLab CI/CD Catalog; public components work without a token |
include: inputs: |
$[[ inputs.KEY ]] and $[[ inputs.KEY | default(…) ]] placeholders substituted from the with: block before parsing |
| Depth limit | Include chains capped at 100 levels (matches GitLab); circular cross-file references detected via visited-set tracking |
| Offline mode | --offline serves all remote includes from the cache; missing entries produce a warning |
| Include cache | --cache-dir DIR (or ~/.cache/glint by default with --offline) persists fetched templates; keyed by SHA-256 of the request coordinates |
Token resolution order (first non-empty wins):
| Source | Header |
|---|---|
--token flag / GITLAB_TOKEN env |
PRIVATE-TOKEN |
CI_JOB_TOKEN env |
JOB-TOKEN |
GITLAB_PRIVATE_TOKEN env |
PRIVATE-TOKEN |
URL resolution order: --gitlab-url flag → CI_SERVER_URL env → GITLAB_URL env → https://gitlab.com
Context simulation
Pass --branch, --tag, --source, or --var to evaluate rules:if: and
only/except filters against a specific pipeline event. When no context flag
is given glint defaults to --branch main --source push so that rules:if:
expressions are always evaluated.
Evaluated at lint time:
rules:if:— full expression language:==,!=,=~,!~,&&,||,!,(…),$VAR/${VAR}, string literals,null, regex flags (/pat/i)only:/except:— ref keywords, branch-name globs, and/regex/patternsworkflow:rules:— evaluated to determine whether the pipeline would run; matching rule'svariables:are injected before job evaluation- Variable expansion —
$VAR/${VAR}references in variable values expanded after all sources merge; transitive chains resolved (up to 10 passes)
Not evaluated (no git tree at lint time): rules:changes:, rules:exists:.
Predefined variables set by shortcut flags:
| Flag | Variables populated |
|---|---|
--branch NAME |
CI_COMMIT_BRANCH, CI_COMMIT_REF_NAME, CI_COMMIT_REF_SLUG, CI_PIPELINE_SOURCE=push |
--tag NAME |
CI_COMMIT_TAG, CI_COMMIT_REF_NAME, CI_COMMIT_REF_SLUG, CI_PIPELINE_SOURCE=push; clears CI_COMMIT_BRANCH |
--source EVENT |
CI_PIPELINE_SOURCE |
--var KEY=VALUE |
any variable; overrides shortcuts; repeatable |
Use --list-vars to print the resolved variable table to stderr.
Output formats
Pass --format to glint check. In structured formats the summary line is
written to stderr so stdout contains only the machine-readable payload.
| Format | Flag | Description |
|---|---|---|
| Text (default) | --format text |
Ruff-style file:line: RULE [sev] message |
| JSON | --format json |
Stable schema (version 1); findings array + summary block |
| SARIF 2.1.0 | --format sarif |
Consumed by GitHub Code Scanning and GitLab SAST |
| JUnit XML | --format junit |
CI test-report artifact (artifacts:reports:junit) |
| GitHub annotations | --format github |
::error file=…,line=…,title=RULE::message inline PR comments |
JSON schema (schema_version: 1):
{
"schema_version": 1,
"glint_version": "v0.2.20",
"pipeline": ".gitlab-ci.yml",
"findings": [
{"rule":"GL004","severity":"error","file":".gitlab-ci.yml","line":14,
"job":"deploy","message":"stage \"production\" is not defined in 'stages'"}
],
"summary": {"total": 1, "errors": 1, "warnings": 0}
}
Configuration
.glint.yml project config file
Searched from the pipeline file's directory upward to the first .git boundary.
# Suppress rules globally for this project.
ignore:
- GL007 # migrating from only:/except:
- GL032 # dynamic variables injected by CI
# Override rule severity (error | warning | ignore).
# 'ignore' is equivalent to listing the rule in ignore:.
severity:
GL004: warning # demote during a stage migration
GL035: error # promote to hard error for this project
# Extra stage names valid beyond those in the pipeline's own stages: block.
stages:
- quality
- security
# Default GitLab token (lower priority than --token and GITLAB_TOKEN).
token: glpat-xxxx
# Default GitLab instance URL.
url: https://gitlab.example.com
# Default cache directory.
cache_dir: ~/.cache/glint
Priority chain: --token/--gitlab-url flags > .glint.yml > environment variables.
Inline suppression (# glint: ignore)
Suppress a finding for a specific job by placing a comment immediately before it:
# glint: ignore GL007
legacy-job:
only: [main]
script: echo ok
# Multiple rules (comma- or space-separated):
# glint: ignore GL007, GL032
other-job:
script: echo ok
# Suppress every rule for this job:
# glint: ignore all
noisy-job:
script: echo ok
Suppressions are scoped to the single job they precede and do not affect other jobs or pipeline-level findings.
Graph visualization (glint graph)
| Mode | Output |
|---|---|
tree (default) |
Terminal job tree: stages as branches, jobs as leaves; annotated with [manual], [delayed], [trigger] where applicable |
includes |
Mermaid flowchart to stdout; colour-coded nodes by include type (local, remote, project, component, template) |
pipeline |
GitLab CI-style SVG/PNG written to --out directory (default: glint-out/); converted to PNG when rsvg-convert, inkscape, or magick is available |
all |
includes to stdout + pipeline file path to stderr |
In DAG pipelines (any job has needs:) the pipeline graph uses job-to-job
Bézier connectors instead of stage-to-stage lines.
Developer tools
| Tool | Description |
|---|---|
glint explain <RULE> |
Print description, rationale, bad-YAML example, and fix for a rule. Case-insensitive (gl007 = GL007). |
glint explain |
List all rules with ID, severity, and title. |
--list-vars |
Print all resolved pipeline variables (pipeline + workflow rules + context) to stderr before linting. |
--version / -v |
Print the compiled version string. |