Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| aca37de2b1 | |||
| 51b3e1f297 |
+3
-3
@@ -1,11 +1,11 @@
|
||||
# Compiled binaries
|
||||
gitlab-sim
|
||||
gitlab-sim.exe
|
||||
glint
|
||||
glint.exe
|
||||
dist/
|
||||
bin/
|
||||
|
||||
# Graph output directory
|
||||
gitlab-sim-out/
|
||||
glint-out/
|
||||
|
||||
# Go test & coverage artifacts
|
||||
*.test
|
||||
|
||||
+11
-2
@@ -5,6 +5,15 @@ 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).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- **Cross-platform release builds** — two new Taskfile tasks for producing tagged release binaries:
|
||||
- `task build-windows` — cross-compiles for Windows x64; output: `glint-<tag>.exe`
|
||||
- `task build-linux` — cross-compiles for Linux x64; output: `glint-<tag>-linux-amd64`
|
||||
- Both tasks enforce that the current commit carries an exact git tag (`git describe --tags --exact-match`); they abort with a clear error otherwise
|
||||
|
||||
## [0.1.0] - 2026-06-07
|
||||
|
||||
### Added
|
||||
@@ -24,7 +33,7 @@ This project uses [Semantic Versioning](https://semver.org).
|
||||
|
||||
- **Graph output (`--graph`)** — visualises the pipeline instead of running lint rules:
|
||||
- `--graph includes` — [Mermaid](https://mermaid.js.org) flowchart of include dependencies written to stdout; one node per `include:` entry (project, component, local, remote, template), colour-coded by type; pipe to a `.mmd` file or paste into [mermaid.live](https://mermaid.live)
|
||||
- `--graph pipeline` — GitLab CI-style SVG/PNG pipeline graph written to a timestamped file in `--graph-out` (default: `gitlab-sim-out/`); jobs rendered as white chip cards with a coloured status indicator (blue: regular, orange: manual, purple: trigger, amber: delayed); DAG mode draws job-to-job Bézier arrows when any job has `needs:`, classic mode draws L-shaped connectors between stage columns; converted to PNG automatically when `rsvg-convert`, `inkscape`, or `magick` is available
|
||||
- `--graph pipeline` — GitLab CI-style SVG/PNG pipeline graph written to a timestamped file in `--graph-out` (default: `glint-out/`); jobs rendered as white chip cards with a coloured status indicator (blue: regular, orange: manual, purple: trigger, amber: delayed); DAG mode draws job-to-job Bézier arrows when any job has `needs:`, classic mode draws L-shaped connectors between stage columns; converted to PNG automatically when `rsvg-convert`, `inkscape`, or `magick` is available
|
||||
- `--graph all` — include Mermaid to stdout, pipeline file path to stderr
|
||||
- New `internal/graph` package (`includes.go`, `pipeline.go`, `render.go`); no new external dependencies
|
||||
|
||||
@@ -74,7 +83,7 @@ This project uses [Semantic Versioning](https://semver.org).
|
||||
- `only`/`rules` or `except`/`rules` used together (error)
|
||||
- No `stages` block defined (warning)
|
||||
- Deprecated `only`/`except` usage (warning)
|
||||
- **CLI** — `gitlab-sim <file>` exits 0 on clean pipelines, 1 on errors; prints findings with severity, job name, and message
|
||||
- **CLI** — `glint <file>` exits 0 on clean pipelines, 1 on errors; prints findings with severity, job name, and message
|
||||
- **YAML parser** — two-pass parse: reserved top-level keys (`stages`, `variables`, `default`, `include`, `workflow`) are decoded into typed structs; remaining keys are treated as job definitions
|
||||
- **Taskfile** — `build`, `test`, `lint-go`, `validate`, `ci`, `clean` tasks via [Task](https://taskfile.dev)
|
||||
- **Testdata fixtures** — `valid.yml`, `invalid.yml`, `extends.yml`, `needs.yml`, `needs_cycle.yml`
|
||||
|
||||
@@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
||||
|
||||
## Project Overview
|
||||
|
||||
`gitlab-sim` is a CLI tool that validates and lints `.gitlab-ci.yml` pipelines locally, without a GitLab server. It resolves `extends:` inheritance, fetches remote `include: project:` templates and `include: component:` catalog entries, then runs a set of lint rules over the fully-merged pipeline.
|
||||
`glint` is a CLI tool that validates and lints `.gitlab-ci.yml` pipelines locally, without a GitLab server. It resolves `extends:` inheritance, fetches remote `include: project:` templates and `include: component:` catalog entries, then runs a set of lint rules over the fully-merged pipeline.
|
||||
|
||||
**Goals:** catch misconfigured pipelines early in a developer's workflow, before pushing to GitLab. Eventual goal: produce a visual graph of the pipeline DAG.
|
||||
|
||||
@@ -34,7 +34,7 @@ This order is intentional and must be preserved: includes must be merged before
|
||||
| `internal/resolver` | `extends:` resolution and `include:` merging |
|
||||
| `internal/fetcher` | GitLab REST API client (token auth, file fetch) |
|
||||
| `internal/graph` | Mermaid graph generators (include dependencies, pipeline jobs) |
|
||||
| `cmd/gitlab-sim` | CLI entrypoint, flag parsing, output formatting |
|
||||
| `cmd/glint` | CLI entrypoint, flag parsing, output formatting |
|
||||
|
||||
### Two-pass YAML parser (`internal/model/parser.go`)
|
||||
|
||||
@@ -138,7 +138,7 @@ All commit messages must follow this format:
|
||||
- `resolver`: extends/include resolution (`internal/resolver/`)
|
||||
- `fetcher`: GitLab API client (`internal/fetcher/`)
|
||||
- `graph`: Mermaid graph generators (`internal/graph/`)
|
||||
- `cli`: CLI entrypoint (`cmd/gitlab-sim/`)
|
||||
- `cli`: CLI entrypoint (`cmd/glint/`)
|
||||
- `testdata`: fixture files only
|
||||
- `docs`: README, CHANGELOG, or other documentation
|
||||
- `build`: Taskfile, go.mod, go.sum, CI config
|
||||
@@ -185,3 +185,9 @@ Every fixture in `testdata/` is run by `task validate`. Files whose expected beh
|
||||
|
||||
Token resolution order (first non-empty wins): `GITLAB_TOKEN` → `CI_JOB_TOKEN` → `GITLAB_PRIVATE_TOKEN`.
|
||||
URL resolution order: `--gitlab-url` flag → `CI_SERVER_URL` → `GITLAB_URL` → `https://gitlab.com`.
|
||||
|
||||
---
|
||||
|
||||
## README and CHANGELOG
|
||||
|
||||
On each modification, complete README.md and CHANGELOG.md with the changes made.
|
||||
@@ -1,4 +1,4 @@
|
||||
# gitlab-sim
|
||||
# glint
|
||||
|
||||
[](LICENSE)
|
||||
[](CHANGELOG.md)
|
||||
@@ -29,9 +29,9 @@ A local tool to validate and lint `.gitlab-ci.yml` pipelines without needing a G
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
git clone https://git.k3nny.fr/gitlab-sim
|
||||
cd gitlab-sim
|
||||
go build -o gitlab-sim ./cmd/gitlab-sim/...
|
||||
git clone https://git.k3nny.fr/glint
|
||||
cd glint
|
||||
go build -o glint ./cmd/glint/...
|
||||
```
|
||||
|
||||
Or with Task:
|
||||
@@ -43,7 +43,7 @@ task build
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
gitlab-sim [options] <pipeline.yml>
|
||||
glint [options] <pipeline.yml>
|
||||
```
|
||||
|
||||
Exits `0` when no errors are found, `1` when at least one error is reported.
|
||||
@@ -51,20 +51,20 @@ Exits `0` when no errors are found, `1` when at least one error is reported.
|
||||
### Remote project includes
|
||||
|
||||
Pipelines that include templates from other GitLab projects are supported.
|
||||
Provide a token so `gitlab-sim` can fetch them:
|
||||
Provide a token so `glint` can fetch them:
|
||||
|
||||
```bash
|
||||
# personal access token (read_api scope)
|
||||
GITLAB_TOKEN=glpat-xxxx gitlab-sim .gitlab-ci.yml
|
||||
GITLAB_TOKEN=glpat-xxxx glint .gitlab-ci.yml
|
||||
|
||||
# CI/CD job token (when running inside a pipeline)
|
||||
CI_JOB_TOKEN=$CI_JOB_TOKEN gitlab-sim .gitlab-ci.yml
|
||||
CI_JOB_TOKEN=$CI_JOB_TOKEN glint .gitlab-ci.yml
|
||||
|
||||
# self-hosted GitLab
|
||||
GITLAB_TOKEN=glpat-xxxx GITLAB_URL=https://gitlab.example.com gitlab-sim .gitlab-ci.yml
|
||||
GITLAB_TOKEN=glpat-xxxx GITLAB_URL=https://gitlab.example.com glint .gitlab-ci.yml
|
||||
|
||||
# or via flags
|
||||
gitlab-sim --token glpat-xxxx --gitlab-url https://gitlab.example.com .gitlab-ci.yml
|
||||
glint --token glpat-xxxx --gitlab-url https://gitlab.example.com .gitlab-ci.yml
|
||||
```
|
||||
|
||||
**Project includes** require a token; without one they are skipped with a
|
||||
@@ -101,7 +101,7 @@ The component file is looked up in order:
|
||||
|
||||
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`; `gitlab-sim` skips those fields rather
|
||||
placeholders in fields like `stage`; `glint` skips those fields rather
|
||||
than producing false positive errors.
|
||||
|
||||
### Graph output
|
||||
@@ -110,17 +110,17 @@ Pass `--graph` to visualise the pipeline instead of running lint rules.
|
||||
|
||||
```bash
|
||||
# Include dependency graph (which files include which) → Mermaid to stdout
|
||||
gitlab-sim --graph includes .gitlab-ci.yml > includes.mmd
|
||||
glint --graph includes .gitlab-ci.yml > includes.mmd
|
||||
|
||||
# GitLab-like pipeline layout → PNG (or SVG fallback) written to --graph-out dir
|
||||
gitlab-sim --graph pipeline .gitlab-ci.yml
|
||||
# prints the output file path, e.g.: gitlab-sim-out/pipeline-20260607-143022.png
|
||||
glint --graph pipeline .gitlab-ci.yml
|
||||
# prints the output file path, e.g.: glint-out/pipeline-20260607-143022.png
|
||||
|
||||
# Both at once: Mermaid to stdout + pipeline file path to stderr
|
||||
gitlab-sim --graph all .gitlab-ci.yml > includes.mmd
|
||||
glint --graph all .gitlab-ci.yml > includes.mmd
|
||||
|
||||
# Custom output directory
|
||||
gitlab-sim --graph pipeline --graph-out /tmp/graphs .gitlab-ci.yml
|
||||
glint --graph pipeline --graph-out /tmp/graphs .gitlab-ci.yml
|
||||
```
|
||||
|
||||
**Include graph** (`--graph includes`) — [Mermaid](https://mermaid.js.org) flowchart written to stdout.
|
||||
@@ -134,7 +134,7 @@ One node per include entry, colour-coded by type:
|
||||
- Light orange: GitLab-provided `template:` includes
|
||||
|
||||
**Pipeline graph** (`--graph pipeline`) — GitLab CI-style SVG rendered to a timestamped file
|
||||
in the `--graph-out` directory (default: `gitlab-sim-out/`). Converted to PNG automatically
|
||||
in the `--graph-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
|
||||
@@ -152,16 +152,16 @@ pipeline event. The pipeline is still fully linted; context output is printed fi
|
||||
|
||||
```bash
|
||||
# What runs on a push to develop?
|
||||
gitlab-sim --branch develop .gitlab-ci.yml
|
||||
glint --branch develop .gitlab-ci.yml
|
||||
|
||||
# What runs when a v1.2.0 tag is pushed?
|
||||
gitlab-sim --tag v1.2.0 .gitlab-ci.yml
|
||||
glint --tag v1.2.0 .gitlab-ci.yml
|
||||
|
||||
# Merge request pipeline
|
||||
gitlab-sim --source merge_request_event .gitlab-ci.yml
|
||||
glint --source merge_request_event .gitlab-ci.yml
|
||||
|
||||
# Arbitrary variable overrides (repeatable)
|
||||
gitlab-sim --branch main --var DEPLOY_ENV=production .gitlab-ci.yml
|
||||
glint --branch main --var DEPLOY_ENV=production .gitlab-ci.yml
|
||||
```
|
||||
|
||||
**Evaluated:**
|
||||
@@ -285,6 +285,8 @@ task test # run Go unit tests
|
||||
task lint-go # run go vet
|
||||
task validate # run the binary against all testdata fixtures
|
||||
task ci # full check: vet → test → build → validate
|
||||
task build-windows # cross-compile for Windows x64 (requires a tagged commit → glint-<tag>.exe)
|
||||
task build-linux # cross-compile for Linux x64 (requires a tagged commit → glint-<tag>-linux-amd64)
|
||||
task clean # remove build artifacts
|
||||
```
|
||||
|
||||
@@ -292,7 +294,7 @@ task clean # remove build artifacts
|
||||
|
||||
```
|
||||
.
|
||||
├── cmd/gitlab-sim/ # CLI entrypoint
|
||||
├── cmd/glint/ # CLI entrypoint
|
||||
├── internal/
|
||||
│ ├── cicontext/ # CI variable context, rules:if: evaluator, job reachability
|
||||
│ ├── fetcher/ # GitLab API client (project include fetching)
|
||||
|
||||
+13
-13
@@ -1,12 +1,12 @@
|
||||
# Roadmap
|
||||
|
||||
This document tracks planned improvements to `gitlab-sim`. 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.
|
||||
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
|
||||
|
||||
Pipelines in Git Flow, Trunk-Based Development, or any branching strategy are rarely uniform: jobs activate or skip based on `$CI_COMMIT_BRANCH`, `$CI_COMMIT_TAG`, `$CI_PIPELINE_SOURCE`, and similar runtime variables. Today `gitlab-sim` validates structure but cannot tell which jobs are actually reachable for a given context.
|
||||
Pipelines in Git Flow, Trunk-Based Development, or any branching strategy are rarely uniform: jobs activate or skip based on `$CI_COMMIT_BRANCH`, `$CI_COMMIT_TAG`, `$CI_PIPELINE_SOURCE`, and similar runtime variables. Today `glint` validates structure but cannot tell which jobs are actually reachable for a given context.
|
||||
|
||||
The plan is to make the execution context injectable so the linter can evaluate `rules:if:` / `only` / `except` conditions and report per-context reachability.
|
||||
|
||||
@@ -14,23 +14,23 @@ The plan is to make the execution context injectable so the linter can evaluate
|
||||
|
||||
```bash
|
||||
# Push to develop
|
||||
gitlab-sim --branch develop .gitlab-ci.yml
|
||||
glint --branch develop .gitlab-ci.yml
|
||||
|
||||
# Tag push (v1.2.0) — sets CI_COMMIT_TAG and clears CI_COMMIT_BRANCH
|
||||
gitlab-sim --tag v1.2.0 .gitlab-ci.yml
|
||||
glint --tag v1.2.0 .gitlab-ci.yml
|
||||
|
||||
# Merge request pipeline
|
||||
gitlab-sim --source merge_request_event \
|
||||
glint --source merge_request_event \
|
||||
--var CI_MERGE_REQUEST_TARGET_BRANCH_NAME=main \
|
||||
.gitlab-ci.yml
|
||||
|
||||
# Explicit variable overrides for anything not covered by the shortcuts
|
||||
gitlab-sim --var CI_COMMIT_BRANCH=feat/my-feature \
|
||||
glint --var CI_COMMIT_BRANCH=feat/my-feature \
|
||||
--var CI_ENVIRONMENT_NAME=staging \
|
||||
.gitlab-ci.yml
|
||||
|
||||
# Simulate multiple contexts in one run (print per-context job tables)
|
||||
gitlab-sim --context branch=main \
|
||||
glint --context branch=main \
|
||||
--context branch=develop \
|
||||
--context tag=v1.0.0 \
|
||||
.gitlab-ci.yml
|
||||
@@ -112,22 +112,22 @@ The SVG renderer covers the basic layout. These would bring it closer to GitLab'
|
||||
|
||||
## CI / editor integration
|
||||
|
||||
- **GitLab CI template** — a `.gitlab-ci.yml` snippet that runs `gitlab-sim` as a pipeline-validation job before the real pipeline executes; publishable to the GitLab CI/CD Catalog
|
||||
- **GitHub Actions action** — `uses: k3nny/gitlab-sim@v1` wrapper for repositories that mirror or manage GitLab pipelines from GitHub
|
||||
- **Pre-commit hook** — entry for [pre-commit](https://pre-commit.com) so `gitlab-sim` runs automatically on `git commit` when `.gitlab-ci.yml` changes
|
||||
- **LSP server** — `gitlab-sim lsp` mode exposing diagnostics over the Language Server Protocol; enables inline squiggles in VS Code, JetBrains, Neovim, etc. without a dedicated extension
|
||||
- **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 action** — `uses: k3nny/glint@v1` wrapper for repositories that mirror or manage GitLab pipelines from GitHub
|
||||
- **Pre-commit hook** — entry for [pre-commit](https://pre-commit.com) so `glint` runs automatically on `git commit` when `.gitlab-ci.yml` changes
|
||||
- **LSP server** — `glint 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
|
||||
|
||||
- **`.gitlab-sim.yml` config file** — project-level configuration for:
|
||||
- **`.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** — `# gitlab-sim: ignore next-line <rule-id>` in the pipeline YAML
|
||||
- **Inline suppression comments** — `# glint: ignore next-line <rule-id>` in the pipeline YAML
|
||||
|
||||
---
|
||||
|
||||
|
||||
+37
-5
@@ -1,7 +1,7 @@
|
||||
version: "3"
|
||||
|
||||
vars:
|
||||
BINARY: gitlab-sim
|
||||
BINARY: glint
|
||||
GO: /usr/local/go/bin/go
|
||||
|
||||
tasks:
|
||||
@@ -10,9 +10,9 @@ tasks:
|
||||
cmd: task --list
|
||||
|
||||
build:
|
||||
desc: Build the gitlab-sim binary
|
||||
desc: Build the glint binary
|
||||
cmds:
|
||||
- "{{.GO}} build -o {{.BINARY}} ./cmd/gitlab-sim/..."
|
||||
- "{{.GO}} build -o {{.BINARY}} ./cmd/glint/..."
|
||||
sources:
|
||||
- "**/*.go"
|
||||
- go.mod
|
||||
@@ -24,7 +24,7 @@ tasks:
|
||||
cmd: "{{.GO}} test ./..."
|
||||
|
||||
validate:
|
||||
desc: Run gitlab-sim against all testdata fixtures
|
||||
desc: Run glint against all testdata fixtures
|
||||
deps: [build]
|
||||
cmds:
|
||||
- cmd: ./{{.BINARY}} testdata/valid.yml
|
||||
@@ -68,6 +68,38 @@ tasks:
|
||||
- task: build
|
||||
- task: validate
|
||||
|
||||
build-windows:
|
||||
desc: Build the glint binary for Windows x64 (requires a tagged commit)
|
||||
vars:
|
||||
TAG:
|
||||
sh: git describe --tags --exact-match
|
||||
preconditions:
|
||||
- sh: git describe --tags --exact-match
|
||||
msg: "Current commit is not tagged — Windows build requires a git tag"
|
||||
cmds:
|
||||
- "GOOS=windows GOARCH=amd64 {{.GO}} build -o {{.BINARY}}-{{.TAG}}.exe ./cmd/glint/..."
|
||||
sources:
|
||||
- "**/*.go"
|
||||
- go.mod
|
||||
generates:
|
||||
- "{{.BINARY}}-{{.TAG}}.exe"
|
||||
|
||||
build-linux:
|
||||
desc: Build the glint binary for Linux x64 (requires a tagged commit)
|
||||
vars:
|
||||
TAG:
|
||||
sh: git describe --tags --exact-match
|
||||
preconditions:
|
||||
- sh: git describe --tags --exact-match
|
||||
msg: "Current commit is not tagged — Linux build requires a git tag"
|
||||
cmds:
|
||||
- "GOOS=linux GOARCH=amd64 {{.GO}} build -o {{.BINARY}}-{{.TAG}}-linux-amd64 ./cmd/glint/..."
|
||||
sources:
|
||||
- "**/*.go"
|
||||
- go.mod
|
||||
generates:
|
||||
- "{{.BINARY}}-{{.TAG}}-linux-amd64"
|
||||
|
||||
clean:
|
||||
desc: Remove build artifacts
|
||||
cmd: rm -f {{.BINARY}}
|
||||
cmd: rm -f {{.BINARY}} {{.BINARY}}-*.exe {{.BINARY}}-*-linux-amd64
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"git.k3nny.fr/glint/internal/cicontext"
|
||||
"git.k3nny.fr/glint/internal/fetcher"
|
||||
"git.k3nny.fr/glint/internal/graph"
|
||||
"git.k3nny.fr/glint/internal/linter"
|
||||
"git.k3nny.fr/glint/internal/model"
|
||||
"git.k3nny.fr/glint/internal/resolver"
|
||||
)
|
||||
|
||||
// multiFlag allows a flag to be specified multiple times.
|
||||
type multiFlag []string
|
||||
|
||||
func (f *multiFlag) String() string { return strings.Join(*f, ", ") }
|
||||
func (f *multiFlag) Set(v string) error {
|
||||
*f = append(*f, v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
var (
|
||||
token = flag.String("token", "", "GitLab personal access token (overrides GITLAB_TOKEN)")
|
||||
gitlabURL = flag.String("gitlab-url", "", "GitLab instance URL (overrides CI_SERVER_URL / GITLAB_URL)")
|
||||
graphMode = flag.String("graph", "", "graph mode: includes | pipeline | all")
|
||||
graphOut = flag.String("graph-out", "glint-out", "output directory for pipeline graph files")
|
||||
branch = flag.String("branch", "", "simulate a branch push (sets CI_COMMIT_BRANCH, …)")
|
||||
tag = flag.String("tag", "", "simulate a tag push (sets CI_COMMIT_TAG, …)")
|
||||
source = flag.String("source", "", "set CI_PIPELINE_SOURCE")
|
||||
vars multiFlag
|
||||
)
|
||||
flag.Var(&vars, "var", "set a CI variable as KEY=VALUE; repeatable")
|
||||
flag.Usage = func() {
|
||||
fmt.Fprintf(os.Stderr, "usage: glint [options] <pipeline.yml>\n\n")
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
flag.Parse()
|
||||
|
||||
if flag.NArg() != 1 {
|
||||
flag.Usage()
|
||||
os.Exit(2)
|
||||
}
|
||||
path := flag.Arg(0)
|
||||
|
||||
cfg := fetcher.AutoConfig().WithOverrides(*gitlabURL, *token)
|
||||
|
||||
p, err := model.Parse(path)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
warnings := resolver.ResolveIncludes(p, cfg)
|
||||
for _, w := range warnings {
|
||||
fmt.Fprintf(os.Stderr, "[WARNING] include %s\n", w)
|
||||
}
|
||||
|
||||
if err := resolver.Resolve(p); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error: resolving extends: %v\n", err)
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
if *graphMode != "" {
|
||||
runGraph(p, path, *graphMode, *graphOut)
|
||||
return
|
||||
}
|
||||
|
||||
ctx := cicontext.New(*branch, *tag, *source, vars)
|
||||
if !ctx.IsEmpty() {
|
||||
printContext(p, ctx)
|
||||
}
|
||||
|
||||
findings := linter.Lint(p)
|
||||
hasErrors := false
|
||||
for _, f := range findings {
|
||||
fmt.Println(f)
|
||||
if f.Severity == linter.Error {
|
||||
hasErrors = true
|
||||
}
|
||||
}
|
||||
|
||||
jobCount := len(p.Jobs)
|
||||
stageCount := len(p.Stages)
|
||||
|
||||
if len(findings) == 0 {
|
||||
fmt.Printf("OK: %s — no issues found (%d job(s), %d stage(s))\n", path, jobCount, stageCount)
|
||||
} else {
|
||||
errCount := 0
|
||||
for _, f := range findings {
|
||||
if f.Severity == linter.Error {
|
||||
errCount++
|
||||
}
|
||||
}
|
||||
fmt.Printf("%d finding(s): %d error(s)\n", len(findings), errCount)
|
||||
}
|
||||
|
||||
if hasErrors {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func runGraph(p *model.Pipeline, path, mode, outDir string) {
|
||||
switch mode {
|
||||
case "includes":
|
||||
fmt.Print(graph.Includes(path, p.Include))
|
||||
case "pipeline":
|
||||
outPath, err := graph.RenderPipeline(p, outDir)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error: rendering pipeline graph: %v\n", err)
|
||||
os.Exit(2)
|
||||
}
|
||||
fmt.Println(outPath)
|
||||
case "all":
|
||||
fmt.Print(graph.Includes(path, p.Include))
|
||||
outPath, err := graph.RenderPipeline(p, outDir)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error: rendering pipeline graph: %v\n", err)
|
||||
os.Exit(2)
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, outPath)
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "error: unknown --graph value %q; use: includes, pipeline, all\n", mode)
|
||||
os.Exit(2)
|
||||
}
|
||||
}
|
||||
|
||||
func printContext(p *model.Pipeline, ctx *cicontext.Context) {
|
||||
fmt.Printf("Context: %s\n\n", ctx.Summary())
|
||||
|
||||
byState := map[cicontext.JobState][]string{}
|
||||
for name, job := range p.Jobs {
|
||||
if strings.HasPrefix(name, ".") {
|
||||
continue
|
||||
}
|
||||
state := cicontext.EvalJob(job, ctx)
|
||||
byState[state] = append(byState[state], name)
|
||||
}
|
||||
for _, jobs := range byState {
|
||||
sort.Strings(jobs)
|
||||
}
|
||||
|
||||
printJobGroup("Active ", byState[cicontext.JobActive])
|
||||
printJobGroup("Manual ", byState[cicontext.JobManual])
|
||||
printJobGroup("Skipped", byState[cicontext.JobSkipped])
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
func printJobGroup(label string, jobs []string) {
|
||||
if len(jobs) == 0 {
|
||||
return
|
||||
}
|
||||
fmt.Printf("%s (%d): %s\n", label, len(jobs), strings.Join(jobs, ", "))
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
module git.k3nny.fr/gitlab-sim
|
||||
module git.k3nny.fr/glint
|
||||
|
||||
go 1.26.4
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"git.k3nny.fr/gitlab-sim/internal/model"
|
||||
"git.k3nny.fr/glint/internal/model"
|
||||
)
|
||||
|
||||
// JobState describes whether a job would be included in a pipeline run for the
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"git.k3nny.fr/gitlab-sim/internal/model"
|
||||
"git.k3nny.fr/glint/internal/model"
|
||||
)
|
||||
|
||||
// Pipeline returns a Mermaid flowchart of pipeline jobs grouped by stage.
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.k3nny.fr/gitlab-sim/internal/model"
|
||||
"git.k3nny.fr/glint/internal/model"
|
||||
)
|
||||
|
||||
// Layout constants (pixels) – tuned to resemble GitLab's full pipeline graph view.
|
||||
|
||||
@@ -3,7 +3,7 @@ package linter
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.k3nny.fr/gitlab-sim/internal/model"
|
||||
"git.k3nny.fr/glint/internal/model"
|
||||
)
|
||||
|
||||
func checkDependencies(p *model.Pipeline) []Finding {
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"git.k3nny.fr/gitlab-sim/internal/model"
|
||||
"git.k3nny.fr/glint/internal/model"
|
||||
)
|
||||
|
||||
var validJobWhen = map[string]bool{
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"git.k3nny.fr/gitlab-sim/internal/model"
|
||||
"git.k3nny.fr/glint/internal/model"
|
||||
)
|
||||
|
||||
type Severity string
|
||||
|
||||
@@ -3,7 +3,7 @@ package linter
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.k3nny.fr/gitlab-sim/internal/model"
|
||||
"git.k3nny.fr/glint/internal/model"
|
||||
)
|
||||
|
||||
func checkNeeds(p *model.Pipeline) []Finding {
|
||||
|
||||
@@ -3,7 +3,7 @@ package resolver
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.k3nny.fr/gitlab-sim/internal/model"
|
||||
"git.k3nny.fr/glint/internal/model"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"git.k3nny.fr/gitlab-sim/internal/fetcher"
|
||||
"git.k3nny.fr/gitlab-sim/internal/model"
|
||||
"git.k3nny.fr/glint/internal/fetcher"
|
||||
"git.k3nny.fr/glint/internal/model"
|
||||
)
|
||||
|
||||
// IncludeWarning describes a remote include entry that could not be resolved.
|
||||
|
||||
Vendored
+1
-1
@@ -3,7 +3,7 @@ stages:
|
||||
- deploy
|
||||
|
||||
# CI/CD catalog components. Without network access or a token the fetches will
|
||||
# fail and gitlab-sim emits a WARNING for each, then lints the local jobs normally.
|
||||
# fail and glint emits a WARNING for each, then lints the local jobs normally.
|
||||
include:
|
||||
# Standard single-segment component path (file layout: templates/sast.yml)
|
||||
- component: gitlab.com/components/sast/sast@latest
|
||||
|
||||
Vendored
+1
-1
@@ -3,7 +3,7 @@ stages:
|
||||
- test
|
||||
|
||||
# This pipeline includes templates from another GitLab project.
|
||||
# Without a token, gitlab-sim warns but still lints the local jobs.
|
||||
# Without a token, glint warns but still lints the local jobs.
|
||||
include:
|
||||
- project: my-group/ci-templates
|
||||
ref: main
|
||||
|
||||
Reference in New Issue
Block a user