Compare commits

...

5 Commits

Author SHA1 Message Date
k3nny 1df72a5124 docs(docs): update CHANGELOG and README badge for v0.3.1
ci / vet, staticcheck, test, build (push) Successful in 2m29s
release / Build and publish release (push) Successful in 1m13s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 22:54:15 +02:00
k3nny 5ace9d5756 feat(cli): make tree the sole default for glint graph
ci / vet, staticcheck, test, build (push) Successful in 2m16s
Previously glint graph with no mode printed tree + separator + includes
Mermaid. Now glint graph defaults to tree only; use glint graph includes
for the Mermaid include-dependency output. Simplifies the common case
and makes the default output immediately actionable.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 22:51:44 +02:00
k3nny 8449a9317c docs(cli): document missing --proxy, --cache-dir, --offline flags in --help
ci / vet, staticcheck, test, build (push) Successful in 2m8s
glint check --help was missing --proxy.
glint graph --help was missing --cache-dir, --offline, and --proxy.
All three flags were already implemented; only the Usage text was absent.

Also added --no-warn, --no-skipped, and --proxy examples to the
respective command example sections.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 22:49:39 +02:00
k3nny 263bbbd1ed docs(docs): sync README and FEATURES with current CLI
ci / vet, staticcheck, test, build (push) Successful in 1m55s
README: add render command, fix exit code descriptions (2/10 not 1),
bump integration version references to v0.3.0, fix Usage command list.

FEATURES: document glint render section, colorized text output format,
exit code table, --no-warn, --no-skipped, --list-vars in developer
tools table, bump JSON schema example to v0.3.0 with column field.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 22:47:08 +02:00
k3nny a68993d26f test(config): skip permission test when running as root
ci / vet, staticcheck, test, build (push) Successful in 1m58s
The Gitea CI runner executes as root, which bypasses filesystem
permission checks. The TestLoad_ReadError test creates a mode-0000 file
and expects a read error, but root can always read files regardless of
mode. Skip the test under root instead of removing it so it still runs
in restricted environments.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 22:41:30 +02:00
5 changed files with 103 additions and 27 deletions
+12
View File
@@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
This project uses [Semantic Versioning](https://semver.org).
## [0.3.1] - 2026-06-26
### Changed
- **`glint graph` default mode** — no-mode invocation now prints the job tree only (previously printed tree + `---` separator + Mermaid include graph). Use `glint graph includes` for the Mermaid include-dependency output.
### Fixed
- **`glint check --help`** — `--proxy` option was implemented but missing from the help text.
- **`glint graph --help`** — `--cache-dir`, `--offline`, and `--proxy` options were implemented but missing from the help text.
- **CI test** — `TestLoad_ReadError` skipped when running as root; Gitea runners execute as root which bypasses file permission checks, causing the test to fail.
## [0.3.0] - 2026-06-26
### Added
+48 -9
View File
@@ -118,6 +118,30 @@ Jobs whose name starts with `.` are reusable templates; most rules are skipped f
---
## Pipeline rendering (`glint render`)
`glint render <PIPELINE>` resolves all `include:` and `extends:` chains and
writes the fully flattened pipeline to a single YAML file. This is what GitLab
CI processes server-side.
```bash
glint render .gitlab-ci.yml # writes rendered.gitlab-ci.yml
glint render --output merged.yml .gitlab-ci.yml # custom output path
glint render --output - .gitlab-ci.yml | yq . # stream to stdout
glint render --offline --cache-dir ~/.cache/glint .gitlab-ci.yml
```
**Output order:** `stages`, `variables`, `default`, `workflow`, template jobs
(`.name`, alphabetical), then regular jobs (alphabetical). The `include:` key
(consumed by resolution) and `extends:` keys (merged into each job) are
stripped. All other fields are preserved verbatim.
Accepts the same network flags as `glint check`: `--token`, `--gitlab-url`,
`--cache-dir`, `--offline`, `--proxy`. When writing to a file (not stdout)
a summary line is printed to stderr: `rendered: <file> (N job(s), M stage(s))`.
---
## Context simulation
Pass `--branch`, `--tag`, `--source`, or `--var` to evaluate `rules:if:` and
@@ -184,27 +208,37 @@ test-job active active
---
## Output formats
## Output formats (`glint check`)
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` |
| Text (default) | `--format text` | Four aligned columns (location, rule, severity, message); `error` in bold red, `warning` in bold orange; colors auto-detected (suppressed when `NO_COLOR` is set or stdout is not a terminal) |
| 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 |
**Exit codes:**
| Code | Meaning |
|------|---------|
| `0` | No findings (clean pipeline) |
| `2` | One or more error findings |
| `10` | One or more warning findings, no errors |
Use `--no-warn` to suppress all warnings; a pipeline with only warnings then exits `0`.
**JSON schema (`schema_version: 1`):**
```json
{
"schema_version": 1,
"glint_version": "v0.2.20",
"glint_version": "v0.3.0",
"pipeline": ".gitlab-ci.yml",
"findings": [
{"rule":"GL004","severity":"error","file":".gitlab-ci.yml","line":14,
{"rule":"GL004","severity":"error","file":".gitlab-ci.yml","line":14,"column":1,
"job":"deploy","message":"stage \"production\" is not defined in 'stages'"}
],
"summary": {"total": 1, "errors": 1, "warnings": 0}
@@ -283,10 +317,10 @@ jobs or pipeline-level findings.
| 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) |
| `tree` *(default)* | Terminal job tree: stages as branches, jobs as leaves; annotated with `[manual]`, `[delayed]`, `[trigger]` where applicable |
| `includes` | Mermaid flowchart of include dependencies to stdout; colour-coded 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 |
| `all` | `includes` Mermaid to stdout + `pipeline` SVG/PNG path to stderr |
**`glint graph pipeline --format <FORMAT>`**
@@ -296,6 +330,8 @@ jobs or pipeline-level findings.
| `mermaid` | Print Mermaid flowchart to stdout (paste into [mermaid.live](https://mermaid.live)) |
| `html` | Write self-contained HTML to `--out` with mouse pan/zoom and a click-to-open job-detail sidebar |
**Context flags** (`--branch`, `--tag`, `--source`, `--var`, `--changes`, `--changes-from`) work on all modes and annotate or colour jobs based on their evaluated state. Use `--no-skipped` to remove jobs that would not run in the given context from all output entirely (tree, SVG, HTML, and Mermaid).
**Visual distinctions in SVG and HTML output:**
- **Regular** — blue circle with checkmark
@@ -303,7 +339,7 @@ jobs or pipeline-level findings.
- **Trigger** — purple circle with chevron
- **Delayed** — yellow circle with clock
- **`when: on_failure`** — red circle (`#d9534f`) with X mark; dashed chip border
- **Skipped** (with `--branch`/`--tag`/`--source` context flags) — grey circle, dimmed job name
- **Skipped** (with context flags, without `--no-skipped`) — grey circle, dimmed job name
In DAG pipelines (any job has `needs:`) the pipeline graph uses job-to-job
Bézier connectors. In classic mode a bus-bar pattern (vertical rail + per-job
@@ -318,7 +354,10 @@ shown as a tooltip in SVG viewers and as a sidebar panel in HTML output.
| Tool | Description |
|------|-------------|
| `glint render <PIPELINE>` | Resolve all includes and extends; write the fully merged pipeline to a single YAML file. See [Pipeline rendering](#pipeline-rendering-glint-render) above. |
| `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. |
| `--no-warn` | (`glint check`) Discard all warning findings before output and exit-code calculation. |
| `--no-skipped` | (`glint graph`) Remove skipped jobs from graph output entirely. |
| `--list-vars` | (`glint check`, `glint graph`) Print all resolved pipeline variables to stderr before continuing. |
| `--version` / `-v` | Print the compiled version string. |
+10 -10
View File
@@ -6,7 +6,7 @@
<p align="center">
<a href="LICENSE"><img src="https://img.shields.io/badge/license-Apache%202.0-blue.svg" alt="License"></a>
<a href="CHANGELOG.md"><img src="https://img.shields.io/badge/release-v0.3.0-blue.svg" alt="Release"></a>
<a href="CHANGELOG.md"><img src="https://img.shields.io/badge/release-v0.3.1-blue.svg" alt="Release"></a>
</p>
> **Disclaimer:** This tool was built through iterative AI-assisted development with [Claude](https://claude.ai). It is experimental, incomplete, and not intended for production use. Coverage of GitLab CI keywords is best-effort and may lag behind GitLab's evolving spec. Use it at your own discretion — no correctness guarantees are made. Contributions and bug reports are welcome.
@@ -21,7 +21,7 @@ A local tool to validate and lint `.gitlab-ci.yml` pipelines without needing a G
- **Simulates context** — `--branch`, `--tag`, `--source` flags evaluate `rules:if:` and `only`/`except` to show which jobs would be active, manual, or skipped; `--context branch=main --context branch=develop` prints a multi-column comparison table across multiple contexts in one run
- **Multiple output formats** — `--format text` (default, colorized and column-aligned), `json`, `sarif` (GitHub Code Scanning / GitLab SAST), `junit`, `github` (PR annotations); exits `2` on errors, `10` on warnings only
- **Project config** — `.glint.yml` for rule suppression, severity overrides, token/URL/proxy defaults; `# glint: ignore RULE` for per-job inline suppression; `--no-warn` flag to suppress all warnings
- **Graph visualization** — `glint graph` prints a terminal job tree; `glint graph pipeline` renders a GitLab CI-style SVG/PNG; `--format mermaid` emits a Mermaid flowchart; `--format html` produces a self-contained HTML file with pan/zoom and a job-detail sidebar; `--no-skipped` hides jobs that would not run in the given context
- **Graph visualization** — `glint graph` prints a terminal job tree (default); `glint graph includes` emits a Mermaid include-dependency graph; `glint graph pipeline` renders a GitLab CI-style SVG/PNG; `--format mermaid` or `--format html` for alternative pipeline output; `--no-skipped` hides jobs that would not run in the given context
- **LSP server** — `glint lsp` starts a Language Server Protocol server over stdin/stdout; connect with any LSP client to get inline diagnostics (rule ID as code, error/warning severity) in VS Code, Neovim, Emacs, JetBrains, etc.
- **VS Code extension** — `editors/vscode/` wraps the LSP server; inline squiggles for every glint rule directly in the editor
@@ -52,15 +52,15 @@ task build
glint [OPTIONS] <COMMAND>
Commands:
check Lint a pipeline file — exits 0 (clean) or 1 (errors found)
check Lint a pipeline file — exits 0 (clean), 2 (errors), or 10 (warnings only)
render Resolve all includes and extends into a single merged YAML file
graph Visualise the pipeline as a job tree or Mermaid graph
explain Print description and fix for a lint rule
explain Show description and fix for a lint rule (e.g. glint explain GL007)
lsp Start a Language Server Protocol server (stdin/stdout)
```
Run `glint <command> --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.
Run `glint <command> --help` for all flags. See [FEATURES.md](FEATURES.md) for the
complete feature reference.
## Integrations
@@ -71,7 +71,7 @@ Add to `.pre-commit-config.yaml` in your repository to run glint automatically w
```yaml
repos:
- repo: https://git.k3nny.fr/k3nny/glint
rev: v0.2.28
rev: v0.3.0
hooks:
- id: glint
```
@@ -89,7 +89,7 @@ include:
# As a Catalog component (after publishing to a GitLab instance):
include:
- component: $CI_SERVER_FQDN/k3nny/glint/check@v0.2.28
- component: $CI_SERVER_FQDN/k3nny/glint/check@v0.3.0
inputs:
stage: validate # optional, default: validate
allow_failure: true # optional, default: false
@@ -102,7 +102,7 @@ The component downloads the glint Linux binary, runs `glint check`, and respects
Copy [`action.yml`](action.yml) from this repository, or mirror this repo to GitHub as `k3nny/glint` and reference it directly:
```yaml
- uses: k3nny/glint@v0.2.28
- uses: k3nny/glint@v0.3.0
with:
file: .gitlab-ci.yml # optional, default: .gitlab-ci.yml
args: '--format sarif' # optional
+30 -8
View File
@@ -181,6 +181,12 @@ Options:
having no token). Implies the default cache dir (~/.cache/glint) when
--cache-dir is not set.
--proxy <URL>
HTTP proxy URL for remote includes and GitLab API calls
(e.g. http://proxy:8080). Overrides system proxy env vars
(HTTP_PROXY / HTTPS_PROXY). Also configurable via proxy: in
.glint.yml.
--branch <NAME>
Simulate a branch push. Populates: CI_COMMIT_BRANCH,
CI_COMMIT_REF_NAME, CI_COMMIT_REF_SLUG, CI_PIPELINE_SOURCE=push.
@@ -244,10 +250,12 @@ Examples:
glint check --branch main --var DEPLOY_ENV=production .gitlab-ci.yml
glint check --cache-dir ~/.cache/glint .gitlab-ci.yml
glint check --offline --cache-dir ~/.cache/glint .gitlab-ci.yml
glint check --no-warn .gitlab-ci.yml
glint check --changes src/main.go --changes Dockerfile .gitlab-ci.yml
glint check --changes-from origin/main .gitlab-ci.yml
glint check --context branch=main --context branch=develop .gitlab-ci.yml
glint check --context branch=main --context tag=v1.0.0 --context source=schedule .gitlab-ci.yml
glint check --proxy http://proxy.example.com:8080 .gitlab-ci.yml
`)
}
_ = fs.Parse(args)
@@ -490,12 +498,12 @@ func cmdGraph(args []string) {
format := fs.String("format", "svg", "pipeline output format: svg, mermaid, or html")
fs.Usage = func() {
fmt.Fprintf(os.Stderr, "glint %s\n\n", version)
fmt.Fprint(os.Stderr, `Visualise the pipeline as a job tree and/or Mermaid graph.
fmt.Fprint(os.Stderr, `Visualise the pipeline as a job tree or graph.
Usage: glint graph [MODE] [OPTIONS] <PIPELINE>
Arguments:
[MODE] Graph mode; must appear before options [default: tree+includes]
[MODE] Graph mode; must appear before options [default: tree]
[possible values: tree, includes, pipeline, all]
<PIPELINE> Path to the .gitlab-ci.yml file
@@ -522,6 +530,22 @@ Options:
GitLab instance URL.
[env: CI_SERVER_URL | GITLAB_URL] [default: https://gitlab.com]
--cache-dir <DIR>
Cache fetched remote templates (project: and component: includes) in
DIR. The directory is created on first use. Subsequent runs read from
cache first, avoiding repeated network calls.
--offline
Do not make any network calls. All remote includes must already be
present in --cache-dir; missing entries emit a warning. Implies the
default cache dir (~/.cache/glint) when --cache-dir is not set.
--proxy <URL>
HTTP proxy URL for remote includes and GitLab API calls
(e.g. http://proxy:8080). Overrides system proxy env vars
(HTTP_PROXY / HTTPS_PROXY). Also configurable via proxy: in
.glint.yml.
--branch <NAME>
Simulate a branch push. Jobs in tree output are annotated with their
evaluated state ([skipped] or [manual]; no tag means active).
@@ -568,15 +592,17 @@ always evaluated.
Examples:
glint graph .gitlab-ci.yml
glint graph tree .gitlab-ci.yml
glint graph includes .gitlab-ci.yml > includes.mmd
glint graph tree --branch develop .gitlab-ci.yml
glint graph tree --tag v1.0.0 .gitlab-ci.yml
glint graph tree --list-vars .gitlab-ci.yml
glint graph tree --changes src/main.go .gitlab-ci.yml
glint graph includes .gitlab-ci.yml > includes.mmd
glint graph pipeline .gitlab-ci.yml
glint graph pipeline --out /tmp/graphs .gitlab-ci.yml
glint graph pipeline --format mermaid .gitlab-ci.yml
glint graph pipeline --format html .gitlab-ci.yml
glint graph tree --no-skipped --branch main .gitlab-ci.yml
glint graph pipeline --no-skipped --branch develop .gitlab-ci.yml
glint graph all .gitlab-ci.yml > includes.mmd
`)
}
@@ -683,11 +709,7 @@ Examples:
}
switch mode {
case "default":
fmt.Print(graph.Tree(p, ctx))
fmt.Println("---")
fmt.Print(graph.Includes(path, p.Include, cfg))
case "tree":
case "default", "tree":
fmt.Print(graph.Tree(p, ctx))
case "includes":
fmt.Print(graph.Includes(path, p.Include, cfg))
+3
View File
@@ -103,6 +103,9 @@ func TestLoad_StopsAtGitRoot(t *testing.T) {
// TestLoad_ReadError covers the !os.IsNotExist(err) branch (config.go:59-61)
// when the file exists but is not readable.
func TestLoad_ReadError(t *testing.T) {
if os.Getuid() == 0 {
t.Skip("skipping: root bypasses file permission checks")
}
tmp := t.TempDir()
cfgPath := filepath.Join(tmp, Filename)
if err := os.WriteFile(cfgPath, []byte("ignore: []"), 0o000); err != nil {