diff --git a/README.md b/README.md index 03170ec..42ddfe3 100644 --- a/README.md +++ b/README.md @@ -54,332 +54,9 @@ Commands: explain Print description and fix for a lint rule ``` -Run `glint --help` for command-specific options and examples. - -### `glint check` - -```bash -glint check .gitlab-ci.yml -``` - -Exits `0` when no errors are found, `1` when at least one error is reported. - -### Output formats - -Pass `--format` to control the output. Plain text is the default. - -```bash -# Default: ruff-style text (human-readable) -glint check .gitlab-ci.yml - -# JSON — stable schema, machine-readable -glint check --format json .gitlab-ci.yml - -# SARIF 2.1.0 — GitHub Code Scanning / GitLab SAST -glint check --format sarif .gitlab-ci.yml > glint.sarif - -# JUnit XML — CI test-report artifact (GitLab: artifacts:reports:junit) -glint check --format junit .gitlab-ci.yml > glint-junit.xml - -# GitHub Actions annotations — inline PR diff comments -glint check --format github .gitlab-ci.yml -``` - -In structured formats (`json`, `sarif`, `junit`, `github`) the summary line -(`OK: … no issues found` or `N finding(s): M error(s)`) is written to stderr -so stdout contains only the machine-readable payload. - -**JSON schema (`schema_version: 1`):** -```json -{ - "schema_version": 1, - "glint_version": "v0.2.18", - "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} -} -``` - -**GitHub annotation lines:** -``` -::error file=.gitlab-ci.yml,line=14,title=GL004::job "deploy": stage "production" is not defined in 'stages' -``` - -### Project configuration (`.glint.yml`) - -Place a `.glint.yml` file next to your pipeline (or anywhere in the directory tree up to the repository root) to configure glint for that project. glint searches upward from the pipeline file's directory, stopping at the first `.git` boundary. - -```yaml -# .glint.yml - -# Suppress specific rules entirely. -ignore: - - GL007 # we still use only:/except:, migration in progress - - GL032 # lots of dynamic variables injected by CI - -# Override the severity of specific rules. -severity: - GL004: warning # demote stage errors to warnings during a migration - GL035: error # promote absolute-path warning to error for this project - -# Extra stages that are valid but not declared in the pipeline YAML itself -# (e.g. injected by an include template we can't edit). -stages: - - quality - - security - -# Default token — overridden by --token flag and GITLAB_TOKEN env. -token: glpat-xxxx - -# Default GitLab instance URL. -url: https://gitlab.example.com - -# Default cache directory for fetched remote includes. -cache_dir: ~/.cache/glint -``` - -**Priority chain for token and URL:** `--token`/`--gitlab-url` flags > `.glint.yml` values > `GITLAB_TOKEN`/`CI_SERVER_URL` environment variables. - -### Inline suppression (`# glint: ignore`) - -Suppress a finding for a specific job by placing a `# glint: ignore RULE` comment immediately before the job definition: - -```yaml -# glint: ignore GL007 -legacy-job: - stage: build - only: - - main - script: echo ok - -# Multiple rules — comma- or space-separated: -# glint: ignore GL007, GL032 -another-job: - stage: build - script: echo ok - -# Suppress all rules for this job: -# glint: ignore all -noisy-job: - stage: build - script: echo ok -``` - -Inline suppressions are scoped to the single job they precede. They do not affect other jobs or pipeline-level findings. For project-wide suppression use `.glint.yml` `ignore:`. - -### Remote project includes - -Pipelines that include templates from other GitLab projects are supported. -Provide a token so `glint` can fetch them: - -```bash -# personal access token (read_api scope) -GITLAB_TOKEN=glpat-xxxx glint check .gitlab-ci.yml - -# CI/CD job token (when running inside a pipeline) -CI_JOB_TOKEN=$CI_JOB_TOKEN glint check .gitlab-ci.yml - -# self-hosted GitLab -GITLAB_TOKEN=glpat-xxxx GITLAB_URL=https://gitlab.example.com glint check .gitlab-ci.yml - -# or via flags -glint check --token glpat-xxxx --gitlab-url https://gitlab.example.com .gitlab-ci.yml -``` - -**Project includes** require a token; without one they are skipped with a -warning and the rest of the pipeline is linted as-is. - -### Include cache and offline mode - -Pass `--cache-dir` to cache fetched remote templates so repeated runs skip the network: - -```bash -# First run: fetches and caches -glint check --cache-dir ~/.cache/glint .gitlab-ci.yml - -# Subsequent runs: served from cache -glint check --cache-dir ~/.cache/glint .gitlab-ci.yml - -# Fully offline (uses ~/.cache/glint automatically when --cache-dir is absent) -glint check --offline .gitlab-ci.yml -glint check --offline --cache-dir ~/.cache/glint .gitlab-ci.yml -``` - -Cache entries are keyed by SHA-256 of the full request URL/coordinates and stored as plain YAML files in the cache directory. There is currently no automatic expiry — delete the directory or individual entries to force a fresh fetch. - -**Component includes** (`include: component: ...`) attempt the fetch -unauthenticated, so public [CI/CD Catalog](https://gitlab.com/explore/catalog) -components work without a token. A warning is emitted if the fetch fails. - -Token resolution order (first non-empty wins): - -| Source | Header used | -|--------|-------------| -| `--token` flag / `GITLAB_TOKEN` | `PRIVATE-TOKEN` | -| `CI_JOB_TOKEN` | `JOB-TOKEN` | -| `GITLAB_PRIVATE_TOKEN` | `PRIVATE-TOKEN` | - -Instance URL resolution order: `--gitlab-url` flag → `CI_SERVER_URL` → -`GITLAB_URL` → `https://gitlab.com` - -### Component reference format - -``` -//@ - -gitlab.com/components/secret-detection/secret-detection@v0.1.0 -gitlab.com/my-org/ci-catalog/lint@main -gitlab.example.com/platform/components/build@~latest -``` - -The component file is looked up in order: -1. `templates/.yml` (single-file layout) -2. `templates//template.yml` (directory layout) - -Component input parameters (`with:`) are not validated — they are resolved by -GitLab at runtime. Jobs in fetched components may use `$[[ inputs.xxx ]]` -placeholders in fields like `stage`; `glint` skips those fields rather -than producing false positive errors. - -### `glint graph` - -Visualise the pipeline. Without a mode word, prints a job tree and the include -dependency graph separated by `---`. - -```bash -# Default: job tree + include dependency graph -glint graph .gitlab-ci.yml - -# Job tree only (stages → jobs, like the tree command) -glint graph tree .gitlab-ci.yml - -# Include dependency graph → Mermaid flowchart to stdout -glint graph includes .gitlab-ci.yml > includes.mmd - -# GitLab-like pipeline layout → PNG (or SVG fallback) written to --out dir -glint graph pipeline .gitlab-ci.yml -# prints the output file path, e.g.: glint-out/pipeline-20260607-143022.png - -# Mermaid to stdout + pipeline file path to stderr -glint graph all .gitlab-ci.yml > includes.mmd - -# Custom output directory (pipeline mode) -glint graph pipeline --out /tmp/graphs .gitlab-ci.yml -``` - -**Job tree** (`graph tree`) — stages as branches, jobs as leaves. Jobs with -`when: manual`, `when: delayed`, or `trigger:` are annotated in brackets. - -**Include graph** (`graph includes`) — [Mermaid](https://mermaid.js.org) flowchart written to stdout. -Pipe to a `.mmd` file or paste into [mermaid.live](https://mermaid.live). -One node per include entry, colour-coded by type: -- Orange (bold): the main pipeline file -- Purple: `project:` includes -- Green: `component:` includes -- Blue: `local:` includes -- Grey: `remote:` URL includes -- Light orange: GitLab-provided `template:` includes - -**Pipeline graph** (`graph pipeline`) — GitLab CI-style SVG rendered to a timestamped file -in the `--out` directory (default: `glint-out/`). Converted to PNG automatically -when `rsvg-convert`, `inkscape`, or `magick` is available; falls back to SVG otherwise. -Jobs are colour-coded by type: -- Blue (`#1f75cb`): regular jobs -- Orange (`#fc6d26`): `when: manual` jobs -- Purple (`#6b4fbb`): `trigger:` jobs -- Amber (`#fca326`): `when: delayed` jobs - -DAG mode (job-to-job Bézier arrows) activates automatically when any job has a `needs:` list. -Classic mode draws L-shaped or straight connectors between stage columns otherwise. - -### `glint explain` - -Print the documentation for a specific lint rule. - -```bash -# Show description, example, and fix for GL007 -glint explain GL007 - -# Case-insensitive: gl007 and GL007 are equivalent -glint explain gl007 - -# List all rules with their IDs and severity -glint explain -``` - -### Context simulation - -Pass `--branch`, `--tag`, or `--source` to `glint check` to see which jobs -would run for a given pipeline event. The pipeline is still fully linted; -context output is printed first. - -```bash -# What runs on a push to develop? -glint check --branch develop .gitlab-ci.yml - -# What runs when a v1.2.0 tag is pushed? -glint check --tag v1.2.0 .gitlab-ci.yml - -# Merge request pipeline -glint check --source merge_request_event .gitlab-ci.yml - -# Arbitrary variable overrides (repeatable) -glint check --branch main --var DEPLOY_ENV=production .gitlab-ci.yml -``` - -**Evaluated:** -- `rules:if:` — full expression language: `==`, `!=`, `=~`, `!~`, `&&`, `||`, `!`, `()`, `$VAR`, string literals, `null` -- `only:` / `except:` — ref keywords (`branches`, `tags`, `merge_requests`, `schedules`, …), branch name globs (`feat/*`), and `/regex/` patterns -- Variable expansion — `$VAR` / `${VAR}` references within variable values are expanded after all sources are merged; use `--list-vars` to inspect the resolved values - -**Not evaluated** (no git tree at lint time): `rules:changes:`, `rules:exists:`. -Rules without an `if:` clause always match. - -**Predefined variables** set automatically by the shortcut flags: - -| Flag | Variables populated | -|------|---------------------| -| `--branch ` | `CI_COMMIT_BRANCH`, `CI_COMMIT_REF_NAME`, `CI_COMMIT_REF_SLUG`, `CI_PIPELINE_SOURCE=push` | -| `--tag ` | `CI_COMMIT_TAG`, `CI_COMMIT_REF_NAME`, `CI_COMMIT_REF_SLUG`, `CI_PIPELINE_SOURCE=push` (clears `CI_COMMIT_BRANCH`) | -| `--source ` | `CI_PIPELINE_SOURCE` | -| `--var KEY=VALUE` | any variable; overrides shortcuts | - -### Example output - -``` -# Clean pipeline (implicit default: --branch main --source push) -Context: branch=main, source=push - -Active (5): build, deploy-staging, test, ... - -OK: .gitlab-ci.yml — no issues found (5 job(s), 3 stage(s)) - -# With --branch develop context -Context: branch=develop, source=push - -Active (3): build, deploy-staging, test -Skipped (2): deploy-prod, release-notes - -OK: .gitlab-ci.yml — no issues found (5 job(s), 3 stage(s)) - -# With --tag v1.0.0 context -Context: tag=v1.0.0, source=push - -Active (4): build, deploy-prod, release-notes, test -Skipped (1): deploy-staging - -OK: .gitlab-ci.yml — no issues found (5 job(s), 3 stage(s)) - -# Pipeline with issues -.gitlab-ci.yml:14: GL004 [error] job "deploy": stage "production" is not defined in 'stages' -.gitlab-ci.yml:22: GL027 [error] job "test": needs unknown job "build-app" -.gitlab-ci.yml:31: GL007 [warning] job "old-job": 'only'/'except' are deprecated; prefer 'rules' - -3 finding(s): 2 error(s) -``` +Run `glint --help` for all flags. See [USAGE.md](USAGE.md) for full +examples covering output formats, context simulation, remote includes, cache, +graph modes, and project configuration. ## Development diff --git a/USAGE.md b/USAGE.md new file mode 100644 index 0000000..dc9edb2 --- /dev/null +++ b/USAGE.md @@ -0,0 +1,314 @@ +# glint — usage reference + +Full examples and option descriptions for every command. +For the lint rules reference see [FEATURES.md](FEATURES.md). + +--- + +## `glint check` + +Lint a pipeline file. Exits `0` when no errors are found, `1` when at least +one error is reported (warnings alone do not fail). + +```bash +glint check .gitlab-ci.yml +``` + +### Output formats + +Pass `--format` to control the output. Plain text is the default. + +```bash +# Default: ruff-style text (human-readable) +glint check .gitlab-ci.yml + +# JSON — stable schema, machine-readable +glint check --format json .gitlab-ci.yml + +# SARIF 2.1.0 — GitHub Code Scanning / GitLab SAST +glint check --format sarif .gitlab-ci.yml > glint.sarif + +# JUnit XML — CI test-report artifact (GitLab: artifacts:reports:junit) +glint check --format junit .gitlab-ci.yml > glint-junit.xml + +# GitHub Actions annotations — inline PR diff comments +glint check --format github .gitlab-ci.yml +``` + +In structured formats (`json`, `sarif`, `junit`, `github`) the summary line is +written to stderr so stdout contains only the machine-readable payload. + +**JSON schema (`schema_version: 1`):** +```json +{ + "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} +} +``` + +**GitHub annotation lines:** +``` +::error file=.gitlab-ci.yml,line=14,title=GL004::job "deploy": stage "production" is not defined in 'stages' +``` + +### Context simulation + +Pass `--branch`, `--tag`, or `--source` to evaluate `rules:if:` and +`only`/`except` against a specific pipeline event. The pipeline is still fully +linted; the context summary is printed first. When no context flag is given, +glint defaults to `--branch main --source push`. + +```bash +# What runs on a push to develop? +glint check --branch develop .gitlab-ci.yml + +# What runs when a v1.2.0 tag is pushed? +glint check --tag v1.2.0 .gitlab-ci.yml + +# Merge request pipeline +glint check --source merge_request_event .gitlab-ci.yml + +# Arbitrary variable overrides (repeatable) +glint check --branch main --var DEPLOY_ENV=production .gitlab-ci.yml + +# Debug variable resolution +glint check --list-vars .gitlab-ci.yml +``` + +**Predefined variables** set by the 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 | + +### Remote project includes + +Provide a token so glint can fetch `include: project:` templates: + +```bash +# Personal access token (read_api scope) +GITLAB_TOKEN=glpat-xxxx glint check .gitlab-ci.yml + +# CI/CD job token (when running inside a pipeline) +CI_JOB_TOKEN=$CI_JOB_TOKEN glint check .gitlab-ci.yml + +# Self-hosted GitLab +GITLAB_TOKEN=glpat-xxxx GITLAB_URL=https://gitlab.example.com glint check .gitlab-ci.yml + +# Via flags (override env vars) +glint check --token glpat-xxxx --gitlab-url https://gitlab.example.com .gitlab-ci.yml +``` + +Project includes are skipped with a warning when no token is available; linting +continues with whatever is resolved. + +**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` + +### Include cache and offline mode + +```bash +# Cache fetched templates to disk (keyed by SHA-256 of the request coordinates) +glint check --cache-dir ~/.cache/glint .gitlab-ci.yml + +# Fully offline — serve from cache, warn on misses, make no network calls +glint check --offline .gitlab-ci.yml +glint check --offline --cache-dir /path/to/cache .gitlab-ci.yml +``` + +`--offline` without `--cache-dir` uses `~/.cache/glint` automatically. +There is no automatic cache expiry; delete the directory or individual entries +to force a re-fetch. + +### Component reference format + +`include: component:` references follow the format: + +``` +//@ + +gitlab.com/components/secret-detection/secret-detection@v0.1.0 +gitlab.com/my-org/ci-catalog/lint@main +gitlab.example.com/platform/components/build@~latest +``` + +The component file is looked up in order: +1. `templates/.yml` (single-file layout) +2. `templates//template.yml` (directory layout) + +Public Catalog components work without a token. `with:` input parameters are +substituted locally before parsing; they are not validated against the +component spec. + +### Example output + +``` +# Clean pipeline +Context: branch=main, source=push + +Active (5): build, deploy-staging, test, lint, security-scan + +OK: .gitlab-ci.yml — no issues found (5 job(s), 3 stage(s)) + +# With --branch develop +Context: branch=develop, source=push + +Active (3): build, deploy-staging, test +Skipped (2): deploy-prod, release-notes + +OK: .gitlab-ci.yml — no issues found (5 job(s), 3 stage(s)) + +# Pipeline with issues +.gitlab-ci.yml:14: GL004 [error] job "deploy": stage "production" is not defined in 'stages' +.gitlab-ci.yml:22: GL027 [error] job "test": needs unknown job "build-app" +.gitlab-ci.yml:31: GL007 [warning] job "old-job": 'only'/'except' are deprecated; prefer 'rules' + +3 finding(s): 2 error(s) +``` + +--- + +## `glint graph` + +Visualise the pipeline. Without a mode word, prints a job tree and the include +dependency graph separated by `---`. + +```bash +# Default: job tree + include dependency graph +glint graph .gitlab-ci.yml + +# Job tree only +glint graph tree .gitlab-ci.yml + +# Job tree with context (shows active/manual/skipped annotations) +glint graph tree --branch develop .gitlab-ci.yml + +# Include dependency graph → Mermaid flowchart to stdout +glint graph includes .gitlab-ci.yml > includes.mmd + +# GitLab-like pipeline layout → PNG/SVG written to --out dir +glint graph pipeline .gitlab-ci.yml +# prints the output path, e.g.: glint-out/pipeline-20260614-143022.png + +# Mermaid to stdout + pipeline file path to stderr +glint graph all .gitlab-ci.yml > includes.mmd + +# Custom output directory +glint graph pipeline --out /tmp/graphs .gitlab-ci.yml +``` + +**Job tree** — stages as branches, jobs as leaves; annotated with `[manual]`, +`[delayed]`, `[trigger]` where applicable. Context flags apply the same +evaluation as `glint check`. + +**Include graph** — [Mermaid](https://mermaid.js.org) flowchart; pipe to `.mmd` +or paste into [mermaid.live](https://mermaid.live). Nodes are colour-coded by +include type: orange (main file), purple (project), green (component), blue +(local), grey (remote URL), light orange (GitLab template). + +**Pipeline graph** — GitLab CI-style SVG rendered to a timestamped file. +Converted to PNG when `rsvg-convert`, `inkscape`, or `magick` is available. +DAG mode (Bézier arrows between jobs) activates automatically when any job has +a `needs:` list; classic mode uses stage-column connectors otherwise. + +--- + +## `glint explain` + +Print the documentation for a specific lint rule. + +```bash +# Description, example, and fix for GL007 +glint explain GL007 + +# Case-insensitive +glint explain gl007 + +# List all rules with ID, severity, and title +glint explain +``` + +--- + +## Project configuration (`.glint.yml`) + +Place a `.glint.yml` anywhere in the directory tree from the pipeline file up +to the first `.git` boundary. glint searches upward and uses the first file it +finds. + +```yaml +# 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 under ignore:. +severity: + GL004: warning # demote during a stage migration + GL035: error # promote to hard error for this project + +# Extra stage names valid beyond those declared in the pipeline YAML +# (e.g. injected by an include template you don't own). +stages: + - quality + - security + +# Default token — lower priority than --token and GITLAB_TOKEN. +token: glpat-xxxx + +# Default GitLab instance URL. +url: https://gitlab.example.com + +# Default cache directory for fetched remote includes. +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 +the job definition in the pipeline YAML: + +```yaml +# glint: ignore GL007 +legacy-job: + stage: build + only: [main] + script: echo ok + +# Multiple rules — comma- or space-separated: +# glint: ignore GL007, GL032 +other-job: + stage: build + script: echo ok + +# Suppress every rule for this job: +# glint: ignore all +noisy-job: + stage: build + script: echo ok +``` + +Suppressions are scoped to the single job they precede and do not affect other +jobs or pipeline-level findings. For project-wide suppression, use `.glint.yml` +`ignore:` instead.