Skip to content

feat: actlint v0.1.0 - kinda works - #1

Merged
tomdavidson merged 20 commits into
mainfrom
develop
Mar 28, 2026
Merged

tomdavidson merged 20 commits into
mainfrom
develop

Conversation

@tomdavidson

Copy link
Copy Markdown
Owner

Summary

actlint is a Rust CLI that validates GitHub Actions workflow files and action definition files for metadata correctness. It checks schema structure, expression syntax, context availability, and cross-references.

Phase checklist

  • Phase 1 — Bootstrap crate structure and wire dependencies
  • Phase 2 — Source file and span infrastructure (SourceFile, LintDiagnostic, line/col mapper)
  • Phase 3 — File type detection and classification (workflow vs composite/JS/Docker action)
  • Phase 4 — Action AST definition and yaml-rust2 parser with position tracking
  • Phase 5 — Workflow AST definition and yaml-rust2 parser
  • Phase 6 — GitHub Actions expression lexer, recursive-descent parser, and type checker
  • Phase 7 — Context availability matrix (which contexts are valid in which scope)
  • Phase 8 — Action linting rules (schema, inputs, outputs, runs, branding, expressions)
  • Phase 9 — Workflow linting rules (jobs, steps, expressions)
  • Phase 10 — Linter orchestrator (directory walking, file classification, rule dispatch)
  • Phase 11 — CLI commands (lint, init), schematic config, output formats (text/JSON/SARIF)
  • Phase 12 — Integration tests and comprehensive fixtures (100 tests total)
  • Phase 13 — README, MIT license, and final polish

Verification

  • cargo build succeeds with no warnings
  • cargo clippy --workspace passes with no warnings
  • dprint check passes
  • cargo test --workspace passes all 100 tests (76 unit + 24 integration)
  • cargo run -p actlint-cli -- lint on valid files exits 0
  • cargo run -p actlint-cli -- lint on invalid fixtures exits 1 with diagnostics
  • cargo run -p actlint-cli -- lint --format json produces valid JSON
  • cargo run -p actlint-cli -- init creates .actlint.yml
  • Every commit on develop compiles independently

Add 77 new tests covering edge cases across all parsing and linting modules:

- Empty YAML files: verify parse-error diagnostic for both parsers
- Comment-only YAML: verify empty-document error path
- Multi-document YAML: verify only first document is parsed
- Binary/non-UTF8 content: verify io-error diagnostic via tempfile
- Deeply nested expressions: lexer handles 100 nested parens,
  parser handles 50 nested parens and 50-operand chains
- Expression edge cases: empty ${{ }}, unclosed ${{, nested ${{ }},
  whitespace-only expressions, wrapped/unwrapped delimiter handling
- Circular needs: unit tests for self-cycle, 2-node cycle, 3-node cycle,
  diamond (no cycle), plus integration test via fixture
- Extremely long lines: 100K char single line, 500-job workflow
- Unexpected YAML types: integer name, boolean description, list root,
  null inputs, non-string runs.using, scalar root actions
- Source span boundary cases: line 0, huge line/col, col 0,
  unicode content, long single lines, empty content
- Lexer edge cases: empty input, whitespace-only, incomplete operators,
  unterminated strings, unexpected chars, very long identifiers
- Detection edge cases: empty/comment-only/list/scalar action.yml,
  invalid YAML, unknown using values, multi-document

Adds tempfile as dev-dependency for binary file integration test.
Adds circular-needs.yml and edge-case fixture directory.

Total: 145 unit + 32 integration = 177 tests (was 100).
Wire fuzz-core to fuzz actlint-core with two targets:
- parse_document_unstructured: raw bytes as UTF-8 YAML
- parse_document_structured: Arbitrary-derived YAML documents

fuzz-common is now a library providing Arbitrary types that generate
plausible workflow and action YAML (FuzzDocument, FuzzWorkflow,
FuzzAction, etc.) with to_yaml() rendering.

actlint-core/build.rs scans fuzz-core/regressions/ at compile time
and generates #[test] functions using include_bytes!(), closing the
feedback loop: every minimized crash becomes a permanent regression
test (10 existing regressions now run as unit tests).

Fix fuzz-run.sh and fuzz-manage.sh paths (engine/fuzz -> fuzz-core).
Fix engine-fuzz references in moon.yml.

Add CONTRIBUTING.md with instructions for building, running the CLI
locally, testing, linting, formatting, and fuzz testing.

Total: 155 unit + 32 integration = 187 tests.
…nt scope

- Add file_path() getter to LintDiagnostic; include path in text,
  JSON, and SARIF output formats
- Normalize builtin function lookup to lowercase so fromJSON, toJson,
  hashFiles, startsWith, endsWith all resolve case-insensitively
- Replace the "." default path with config-driven resolve_paths():
  when no CLI paths are given, use workflow_paths + action_paths from
  .actlint.yml (defaults to .github/workflows only), preventing
  test fixtures from producing false positives
- Change action_paths config default from ["."] to [] so zero-config
  runs stay scoped to .github/
@tomdavidson tomdavidson changed the title feat: actlint v0.1.0 — GitHub Actions metadata linter feat: actlint v0.1.0 - kinda works Mar 28, 2026
@tomdavidson
tomdavidson merged commit 7552316 into main Mar 28, 2026
4 of 8 checks passed
@tomdavidson
tomdavidson deleted the develop branch March 28, 2026 23:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant