From bec7b5967199baaf4c26b8bce17675409a986011 Mon Sep 17 00:00:00 2001 From: emil Date: Mon, 22 Jun 2026 05:03:34 +0000 Subject: [PATCH] GRM-51: ci: add actionlint and act_runner exec for workflow verification --- .gitea/actionlint.yaml | 9 +++++++++ .gitea/workflows/auto-merge.yml | 12 ++++++++---- .gitea/workflows/ci.yml | 16 ++++++++++++---- .gitea/workflows/post-merge.yml | 2 +- .pre-commit-config.yaml | 9 +++++++++ AGENTS.md | 23 ++++++++++++++++++++++- Makefile | 19 +++++++++++++++++-- 7 files changed, 78 insertions(+), 12 deletions(-) create mode 100644 .gitea/actionlint.yaml diff --git a/.gitea/actionlint.yaml b/.gitea/actionlint.yaml new file mode 100644 index 0000000..fddf0b5 --- /dev/null +++ b/.gitea/actionlint.yaml @@ -0,0 +1,9 @@ +# actionlint configuration for Gitea Actions workflows +# https://github.com/rhysd/actionlint/blob/main/docs/config.md +# +# Run: actionlint -config-file .gitea/actionlint.yaml .gitea/workflows/*.yml + +# Custom self-hosted runner labels used in runs-on +self-hosted-runner: + labels: + - docker diff --git a/.gitea/workflows/auto-merge.yml b/.gitea/workflows/auto-merge.yml index 6134d48..b17a6ff 100644 --- a/.gitea/workflows/auto-merge.yml +++ b/.gitea/workflows/auto-merge.yml @@ -18,10 +18,14 @@ jobs: REPO_TOKEN: ${{ secrets.REPO_TOKEN }} VIKUNJA_TOKEN: ${{ secrets.VIKUNJA_TOKEN }} PYTHONPATH: src + HEAD_REF: ${{ github.head_ref }} + PR_TITLE: ${{ github.event.pull_request.title }} + REPOSITORY: ${{ github.repository }} + PR_NUMBER: ${{ github.event.number }} run: | python3 scripts/ci/auto_merge.py \ - "${{ github.head_ref }}" \ - "${{ github.event.pull_request.title }}" \ - "${{ github.repository }}" \ - "${{ github.event.number }}" \ + "$HEAD_REF" \ + "$PR_TITLE" \ + "$REPOSITORY" \ + "$PR_NUMBER" \ "ready-to-merge" diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index a18c1fb..b2f94f1 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -13,9 +13,16 @@ jobs: - uses: actions/checkout@v4 - name: Set up environment run: make setup + - name: Install actionlint + run: | + set -euo pipefail + mkdir -p "$HOME/.local/bin" + bash <(curl -sL https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash) latest "$HOME/.local/bin" + echo "$HOME/.local/bin" >> "$GITHUB_PATH" - name: Lint all run: | . .venv/bin/activate + export PATH="$HOME/.local/bin:$PATH" make lint-all - name: Unit tests with 100% coverage run: | @@ -150,14 +157,14 @@ jobs: # Skip if runner index exceeds available runners if [ "$RUNNER_INDEX" -gt "$MAX_RUNNERS" ]; then echo "Skipping — runner index $RUNNER_INDEX > max runners $MAX_RUNNERS" - echo "TEST_PAIRS=" >> $GITHUB_ENV - echo "SKIP=true" >> $GITHUB_ENV + echo "TEST_PAIRS=" >> "$GITHUB_ENV" + echo "SKIP=true" >> "$GITHUB_ENV" exit 0 fi PAIRS=$(python3 scripts/ci/distribute_molecule.py --runner-index "$RUNNER_INDEX" --max-runners "$MAX_RUNNERS") echo "Assigned pairs: $PAIRS" - echo "TEST_PAIRS=$PAIRS" >> $GITHUB_ENV - echo "SKIP=false" >> $GITHUB_ENV + echo "TEST_PAIRS=$PAIRS" >> "$GITHUB_ENV" + echo "SKIP=false" >> "$GITHUB_ENV" - name: Run molecule tests if: env.SKIP != 'true' run: | @@ -167,6 +174,7 @@ jobs: echo "No test pairs assigned — skipping" exit 0 fi + # shellcheck disable=SC2086 # intentional word splitting for argument expansion python3 scripts/ci/molecule_ci_guard.py $TEST_PAIRS env: GITEA_URL: ${{ github.server_url }} diff --git a/.gitea/workflows/post-merge.yml b/.gitea/workflows/post-merge.yml index dc459f5..fe05c59 100644 --- a/.gitea/workflows/post-merge.yml +++ b/.gitea/workflows/post-merge.yml @@ -155,7 +155,7 @@ jobs: git checkout --orphan badges git rm -rf . cp -r .badges/* . - git add *.svg + git add ./*.svg git commit --no-verify -m "Update badges [skip ci]" git push origin badges --force - name: Notify on failure diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 58fcec4..51b82a0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -48,6 +48,15 @@ repos: pass_filenames: false stages: [pre-commit] + - id: workflow-lint + name: actionlint (workflow YAML) + entry: make workflow-lint + language: system + files: ^\.gitea/workflows/ + types: [yaml] + pass_filenames: false + stages: [pre-commit] + - id: pytest-cov name: pytest with 100% coverage entry: make pytest-cov diff --git a/AGENTS.md b/AGENTS.md index acee97e..2eb4454 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,14 +4,35 @@ ```bash make setup # Create venv, install deps, set up hooks -make lint-all # ruff + pyright + bandit + ansible-lint + checkmake +make lint-all # ruff + pyright + bandit + ansible-lint + checkmake + actionlint make pytest-cov # Unit tests with 100% coverage enforcement make test-unit # Unit tests without coverage make molecule # All 6 scenarios on Ubuntu 22.04 make molecule-all # All 6 scenarios on all 4 supported OSes make test-all # pytest-cov + molecule +make workflow-lint # Static lint of .gitea/workflows/*.yml (actionlint) +make workflow-dryrun # Dry-run all workflows in Docker (act_runner exec --dryrun) +make workflow-check # workflow-lint + workflow-dryrun ``` +## Workflow Verification (Before Push) + +Workflow YAML files (`.gitea/workflows/*.yml`) are verified with two tools: + +1. **actionlint** — Static linter that catches syntax errors, invalid + expressions, unknown keys, type mismatches, and shellcheck issues. + Config: `.gitea/actionlint.yaml` (registers custom `docker` runner label). + Install: `bash <(curl -sL https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash)` + +2. **act_runner exec --dryrun** — Gitea's own runner in dry-run mode. + Validates job dependencies, step ordering, and Docker image selection + without starting containers. Install from + [gitea/act_runner releases](https://gitea.com/gitea/act_runner/releases). + +Both run via `make workflow-check` and are part of `make lint-all`. +The pre-commit hook runs actionlint automatically when workflow files change. +The CI `quality` job installs actionlint and runs `make lint-all`. + ## Architecture - **Python CLI** (`src/gitea_runner_manager/`) — Click-based CLI that delegates to Ansible diff --git a/Makefile b/Makefile index 498750b..fe165a2 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: all setup install update lint ansible-lint makefile-lint lint-all test test-unit pytest-cov molecule molecule-all test-all clean +.PHONY: all setup install update lint ansible-lint makefile-lint lint-all test test-unit pytest-cov molecule molecule-all test-all clean workflow-lint workflow-dryrun workflow-check PYTHON := python3 VENV := .venv @@ -86,7 +86,22 @@ ansible-lint: makefile-lint: @$(CHECKMAKE) Makefile -lint-all: lint ansible-lint makefile-lint +lint-all: lint ansible-lint makefile-lint workflow-lint + +workflow-lint: + @command -v actionlint >/dev/null 2>&1 || { \ + echo "actionlint not found. Install: bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash)"; \ + exit 1; \ + } + actionlint -config-file .gitea/actionlint.yaml .gitea/workflows/*.yml + +workflow-dryrun: + @command -v act_runner >/dev/null 2>&1 || { echo "act_runner not found. Install: https://gitea.com/gitea/act_runner/releases"; exit 1; } + @echo "Dry-running all workflows (no Docker containers started)..." + act_runner exec --dryrun -W .gitea/workflows/ 2>&1 | grep -E 'DRYRUN|ERROR|FAIL|Job' + +workflow-check: workflow-lint workflow-dryrun + @echo "Workflow checks passed (static lint + dry-run)." test-unit: $(BIN)/pytest tests/unit/ -v --no-cov