gitlab-sim/README.md
2026-06-05 01:29:07 +02:00

145 lines
4.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# gitlab-sim
A local tool to validate and lint `.gitlab-ci.yml` pipelines without needing a GitLab server.
## Features
- **YAML validation** — detects malformed pipeline files early
- **Stage validation** — every job's `stage` must be declared in `stages`
- **`extends:` resolution** — resolves single and multi-level template inheritance before linting, so derived jobs are evaluated against their fully merged definition
- **`needs:` DAG validation** — checks that `needs:` references exist, respect stage ordering, and contain no circular dependencies
- **Deprecation warnings** — flags `only`/`except` usage in favour of `rules`
## Requirements
- Go 1.21 or later
- [Task](https://taskfile.dev) (optional, for development tasks)
## Installation
```bash
git clone https://git.k3nny.fr/gitlab-sim
cd gitlab-sim
go build -o gitlab-sim ./cmd/gitlab-sim/...
```
Or with Task:
```bash
task build
```
## Usage
```bash
gitlab-sim <pipeline.yml>
```
Exits `0` when no errors are found, `1` when at least one error is reported.
### Example output
```
# Clean pipeline
OK: .gitlab-ci.yml — no issues found (5 jobs, 3 stages)
# Pipeline with issues
[ERROR] job "deploy": stage "production" is not defined in 'stages'
[ERROR] job "test": needs unknown job "build-app"
[WARNING] job "old-job": 'only'/'except' are deprecated; prefer 'rules'
3 finding(s): 2 error(s)
```
## Lint rules
### Pipeline-level
| Severity | Rule |
|----------|------|
| ERROR | `workflow.rules[*].when` is not `always` or `never` |
| WARNING | No `stages` defined (GitLab falls back to default stages) |
### Job-level — structure
| Severity | Rule |
|----------|------|
| ERROR | Job is missing required `script` (or `run`) — non-trigger, non-template jobs |
| ERROR | Job references a `stage` not declared in `stages` |
| ERROR | `only` and `rules` used together on the same job |
| ERROR | `except` and `rules` used together on the same job |
| WARNING | `only`/`except` used (deprecated, prefer `rules`) |
### Job-level — keyword constraints
| Severity | Rule |
|----------|------|
| ERROR | `when` is not one of `on_success`, `on_failure`, `always`, `manual`, `delayed`, `never` |
| ERROR | `when: delayed` without `start_in` |
| ERROR | `start_in` set but `when` is not `delayed` |
| ERROR | `parallel` integer not in range 2200 |
| ERROR | `parallel` map form missing `matrix` key |
| ERROR | `retry` integer not in range 02 |
| ERROR | `retry.max` not in range 02 |
| ERROR | `retry.when` contains an invalid failure type |
| ERROR | `allow_failure` is not a boolean or a map with `exit_codes` |
| ERROR | `interruptible` is not a boolean |
| ERROR | `trigger` job also has `script` |
| ERROR | `trigger` map missing `project` or `include` |
| ERROR | `coverage` is not a regex pattern wrapped in `/` |
| ERROR | `release` missing required `tag_name` |
| ERROR | `environment.url` set without `environment.name` |
| ERROR | `environment.action` is not one of `start`, `stop`, `prepare`, `verify`, `access` |
| ERROR | `artifacts.when` is not `on_success`, `on_failure`, or `always` |
| ERROR | `artifacts.expose_as` set without `artifacts.paths` |
| ERROR | `cache.when` is not `on_success`, `on_failure`, or `always` |
| ERROR | `cache.policy` is not `pull`, `push`, or `pull-push` |
| ERROR | `rules[*].when` is not one of the valid `when` values |
| ERROR | `image` map form missing `name` key |
| ERROR | `inherit.default` / `inherit.variables` is not a boolean or list |
| WARNING | `pages` job `artifacts.paths` does not include `public` |
### Cross-job graph
| Severity | Rule |
|----------|------|
| ERROR | `needs:` references a job that does not exist |
| ERROR | `needs:` references a job in a later stage |
| ERROR | Circular dependency detected in `needs:` graph |
| ERROR | `dependencies:` references a job that does not exist |
| ERROR | `dependencies:` references a job in the same or a later stage |
| ERROR | `extends:` references an unknown job |
| ERROR | Cycle detected in `extends:` graph |
### Hidden jobs (templates)
Jobs whose name starts with `.` are treated as reusable templates and skipped for most rules. This matches GitLab's own behaviour.
## Development
This project uses [Task](https://taskfile.dev) as a task runner.
```bash
task # list available tasks
task build # compile the binary
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 clean # remove build artifacts
```
## Project structure
```
.
├── cmd/gitlab-sim/ # CLI entrypoint
├── internal/
│ ├── linter/ # lint rules and findings
│ ├── model/ # pipeline data structures and YAML parser
│ └── resolver/ # extends: resolution
├── testdata/ # sample pipelines used for manual validation
├── Taskfile.yml
└── go.mod
```