diff --git a/.gitea/actions/notify-failure/action.yml b/.gitea/actions/notify-failure/action.yml new file mode 100644 index 0000000..ad6c888 --- /dev/null +++ b/.gitea/actions/notify-failure/action.yml @@ -0,0 +1,47 @@ +name: 'Notify on failure' +description: 'Create a Gitea issue when a CI workflow fails (calls devx.ci.notify_failure)' + +# Composite action for the common "Notify on failure" step pattern. +# Replaces the repeated inline: +# - name: Notify on failure +# if: failure() +# env: +# CI_GITEA_API_TOKEN: ${{ secrets.CI_GITEA_API_TOKEN }} +# run: | +# . .venv/bin/activate 2>/dev/null || true +# export PATH="$HOME/.local/bin:$PATH" +# python3 -m devx.ci.notify_failure \ +# --repo "${{ github.repository }}" \ +# --run-id "${{ github.run_id }}" \ +# --workflow "ci/validate" \ +# --commit "${{ github.sha }}" \ +# --auto-login +# +# Gitea 1.27 notes: +# - `if: failure()` is evaluated in the calling workflow's context and +# propagates correctly to composite action steps. +# - `secrets` are not accessible here; the calling workflow's top-level +# `env:` CI_GITEA_API_TOKEN is used via `${{ env.* }}`. + +inputs: + workflow: + description: 'Workflow/job name used in the Gitea issue title (e.g., ci/validate)' + required: true + +runs: + using: 'composite' + steps: + - name: Notify on failure + if: failure() + shell: bash + env: + CI_GITEA_API_TOKEN: ${{ env.CI_GITEA_API_TOKEN }} + run: | + . .venv/bin/activate 2>/dev/null || true + export PATH="$HOME/.local/bin:$PATH" + python3 -m devx.ci.notify_failure \ + --repo "${{ github.repository }}" \ + --run-id "${{ github.run_id }}" \ + --workflow "${{ inputs.workflow }}" \ + --commit "${{ github.sha }}" \ + --auto-login diff --git a/.gitea/actions/quality-checks/action.yml b/.gitea/actions/quality-checks/action.yml new file mode 100644 index 0000000..81c726c --- /dev/null +++ b/.gitea/actions/quality-checks/action.yml @@ -0,0 +1,89 @@ +name: 'Quality checks' +description: 'Run lint, unit tests with coverage, test speed, docs, translations, and security scan' + +# Composite action for the 6-step quality check sequence used by the +# devx validate job. Replaces the inline block: +# - Lint all +# - Unit tests with 100% coverage +# - Check unit test speed +# - Documentation gate (coverage + stale refs + lint + version refs + prose) +# - Translation completeness check +# - Dependency security scan +# +# Each step activates the venv defensively (`. .venv/bin/activate 2>/dev/null +# || true`) so the action works whether or not the setup step created a +# venv at the repo root (pre-built CI images symlink /opt/venv to .venv). +# +# Gitea 1.27 notes: +# - Every `run` step needs explicit `shell:`. +# - Inputs are string-typed; numeric thresholds are passed through as +# strings to `devx.tools.check_test_speed`. + +inputs: + package: + description: 'Package name for doc version checks (e.g., devx, grm). Empty = no DEVX_DOC_VERSIONS_PKG override.' + required: false + default: '' + test-speed-max: + description: 'Max total test seconds (passed to check_test_speed --max-seconds)' + required: false + default: '15' + test-speed-max-single: + description: 'Max single test seconds (passed to check_test_speed --max-single-seconds)' + required: false + default: '0.5' + translations-file: + description: 'Path to translations.json (empty = default location src/devx/translations.json)' + required: false + default: '' + +runs: + using: 'composite' + steps: + - name: Lint all + shell: bash + run: | + . .venv/bin/activate 2>/dev/null || true + export PATH="$HOME/.local/bin:$PATH" + make lint-all + - name: Unit tests with 100% coverage + shell: bash + run: | + . .venv/bin/activate 2>/dev/null || true + make pytest-cov + - name: Check unit test speed + shell: bash + run: | + . .venv/bin/activate 2>/dev/null || true + python3 -m devx.tools.check_test_speed \ + --max-seconds "${{ inputs.test-speed-max }}" \ + --max-single-seconds "${{ inputs.test-speed-max-single }}" + - name: Documentation gate (coverage + stale refs + lint + version refs + prose) + shell: bash + env: + DEVX_DOC_COVERAGE_STRICT: "1" + DEVX_VALE_LEVEL: warning + run: | + . .venv/bin/activate 2>/dev/null || true + export PATH="$HOME/.local/bin:$PATH" + if [ -n "${{ inputs.package }}" ]; then + export DEVX_DOC_VERSIONS_PKG="${{ inputs.package }}" + fi + make devx-docs-check + - name: Translation completeness check + shell: bash + run: | + . .venv/bin/activate 2>/dev/null || true + if [ -n "${{ inputs.translations-file }}" ]; then + python3 -m devx.ci.check_translations --translations "${{ inputs.translations-file }}" + else + python3 -m devx.ci.check_translations + fi + - name: Dependency security scan + shell: bash + run: | + . .venv/bin/activate 2>/dev/null || true + # Install pip in venv if missing (needed by pip-audit) + .venv/bin/python -m ensurepip 2>/dev/null || true + PIPAPI_PYTHON_LOCATION=$PWD/.venv/bin/python \ + pip-audit --desc --skip-editable 2>&1 || true diff --git a/.gitea/actions/setup-env/action.yml b/.gitea/actions/setup-env/action.yml new file mode 100644 index 0000000..b41a5c2 --- /dev/null +++ b/.gitea/actions/setup-env/action.yml @@ -0,0 +1,37 @@ +name: 'Set up environment' +description: 'Set up CI environment with venv and PATH (calls make setup-image)' + +# Composite action for the common "Set up environment" step pattern. +# Replaces the repeated inline: +# - name: Set up environment +# env: +# CI_GITEA_API_TOKEN: ${{ secrets.CI_GITEA_API_TOKEN }} +# run: make setup-image +# +# Gitea 1.27 notes: +# - Every `run` step needs explicit `shell:`. +# - Composite actions cannot access `secrets` directly; they read from +# the `env:` context which the calling workflow must populate. +# - The calling workflow's top-level `env:` block (CI_GITEA_API_TOKEN, +# CI_GITEA_USERNAME) is visible here via `${{ env.* }}`. + +inputs: + extras: + description: 'Extra pip install groups passed to make setup-image (e.g., ci,lint,release)' + required: false + default: '' + +runs: + using: 'composite' + steps: + - name: Set up environment + shell: bash + env: + CI_GITEA_API_TOKEN: ${{ env.CI_GITEA_API_TOKEN }} + CI_GITEA_USERNAME: ${{ env.CI_GITEA_USERNAME }} + run: | + if [ -n "${{ inputs.extras }}" ]; then + make setup-image EXTRAS="${{ inputs.extras }}" + else + make setup-image + fi diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 8bc4a72..9c729cc 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -36,45 +36,14 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 - - name: Set up environment - env: - CI_GITEA_API_TOKEN: ${{ secrets.CI_GITEA_API_TOKEN }} - CI_GITEA_USERNAME: ${{ vars.CI_GITEA_USERNAME }} - run: make setup-image EXTRAS=ci,lint - # --- quality steps --- - - name: Lint all - run: | - . .venv/bin/activate 2>/dev/null || true - export PATH="$HOME/.local/bin:$PATH" - make lint-all - - name: Unit tests with 100% coverage - run: | - . .venv/bin/activate 2>/dev/null || true - make pytest-cov - - name: Documentation gate (coverage + stale refs + lint + version refs + prose) - env: - DEVX_DOC_COVERAGE_STRICT: "1" - DEVX_DOC_VERSIONS_PKG: grm - DEVX_VALE_LEVEL: warning - run: | - . .venv/bin/activate 2>/dev/null || true - export PATH="$HOME/.local/bin:$PATH" - make devx-docs-check - - name: Translation completeness check - run: | - . .venv/bin/activate 2>/dev/null || true - python3 -m devx.ci.check_translations --translations src/grm/translations.json - - name: Check unit test speed - run: | - . .venv/bin/activate 2>/dev/null || true - python3 -m devx.tools.check_test_speed --max-seconds 4 --max-single-seconds 0.5 - - name: Dependency security scan - run: | - . .venv/bin/activate 2>/dev/null || true - # Install pip in venv if missing (needed by pip-audit) - .venv/bin/python -m ensurepip 2>/dev/null || true - PIPAPI_PYTHON_LOCATION=$PWD/.venv/bin/python \ - pip-audit --desc --skip-editable 2>&1 || true + - uses: ./.gitea/actions/setup-env + with: + extras: "ci,lint" + - uses: ./.gitea/actions/quality-checks + with: + package: grm + test-speed-max: "4" + translations-file: src/grm/translations.json - name: Workflow dry-run validation run: | . .venv/bin/activate 2>/dev/null || true @@ -144,18 +113,9 @@ jobs: --owner "${{ github.repository_owner }}" \ --repo "${{ github.event.repository.name }}" \ --github-output - - name: Notify on failure - if: failure() - env: - CI_GITEA_API_TOKEN: ${{ secrets.CI_GITEA_API_TOKEN }} - run: | - . .venv/bin/activate 2>/dev/null || true - export PATH="$HOME/.local/bin:$PATH" - python3 -m devx.ci.notify_failure --auto-login \ - --repo "${{ github.repository }}" \ - --run-id "${{ github.run_id }}" \ - --workflow "ci/validate" \ - --commit "${{ github.sha }}" + - uses: ./.gitea/actions/notify-failure + with: + workflow: "ci/validate" molecule-tests: needs: [validate] @@ -288,11 +248,9 @@ jobs: with: fetch-depth: 0 token: ${{ secrets.CI_GITEA_API_TOKEN }} - - name: Set up environment - env: - CI_GITEA_API_TOKEN: ${{ secrets.CI_GITEA_API_TOKEN }} - CI_GITEA_USERNAME: ${{ vars.CI_GITEA_USERNAME }} - run: make setup-image EXTRAS=ci + - uses: ./.gitea/actions/setup-env + with: + extras: "ci" - name: Post approval review env: REVIEWER_GITEA_API_TOKEN: ${{ secrets.REVIEWER_GITEA_API_TOKEN }} diff --git a/.gitea/workflows/post-merge.yml b/.gitea/workflows/post-merge.yml index 6aed530..ad92494 100644 --- a/.gitea/workflows/post-merge.yml +++ b/.gitea/workflows/post-merge.yml @@ -53,11 +53,9 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 - - name: Set up environment - env: - CI_GITEA_API_TOKEN: ${{ secrets.CI_GITEA_API_TOKEN }} - CI_GITEA_USERNAME: ${{ vars.CI_GITEA_USERNAME }} - run: make setup-image EXTRAS=ci + - uses: ./.gitea/actions/setup-env + with: + extras: "ci" - name: Ensure branch protection and labels env: DEVX_REPO_NAME: grm @@ -91,18 +89,9 @@ jobs: --base "HEAD~1" \ --head "HEAD" \ --github-output - - name: Notify on failure - if: failure() - env: - CI_GITEA_API_TOKEN: ${{ secrets.CI_GITEA_API_TOKEN }} - run: | - . .venv/bin/activate 2>/dev/null || true - export PATH="$HOME/.local/bin:$PATH" - python3 -m devx.ci.notify_failure --auto-login \ - --repo "${{ github.repository }}" \ - --run-id "${{ github.run_id }}" \ - --workflow "post-merge/detect-and-configure" \ - --commit "${{ github.sha }}" + - uses: ./.gitea/actions/notify-failure + with: + workflow: "post-merge/detect-and-configure" release-and-maintain: needs: [detect-and-configure] @@ -125,11 +114,9 @@ jobs: fetch-depth: 0 ref: master token: ${{ secrets.CI_GITEA_API_TOKEN }} - - name: Set up environment - env: - CI_GITEA_API_TOKEN: ${{ secrets.CI_GITEA_API_TOKEN }} - CI_GITEA_USERNAME: ${{ vars.CI_GITEA_USERNAME }} - run: make setup-image EXTRAS=ci,lint + - uses: ./.gitea/actions/setup-env + with: + extras: "ci,lint" - name: Configure git env: CI_GITEA_API_TOKEN: ${{ secrets.CI_GITEA_API_TOKEN }} @@ -189,15 +176,6 @@ jobs: git fetch origin master git reset --hard origin/master python3 -m devx.ci.push_badges - - name: Notify on failure - if: failure() - env: - CI_GITEA_API_TOKEN: ${{ secrets.CI_GITEA_API_TOKEN }} - run: | - . .venv/bin/activate 2>/dev/null || true - export PATH="$HOME/.local/bin:$PATH" - python3 -m devx.ci.notify_failure --auto-login \ - --repo "${{ github.repository }}" \ - --run-id "${{ github.run_id }}" \ - --workflow "post-merge/release-and-maintain" \ - --commit "${{ github.sha }}" + - uses: ./.gitea/actions/notify-failure + with: + workflow: "post-merge/release-and-maintain" diff --git a/CHANGELOG.md b/CHANGELOG.md index 8bc5204..52178fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +### CI + +- Convert `ci.yml` and `post-merge.yml` to use composite actions + (`setup-env`, `quality-checks`, `notify-failure`) copied from devx, + reducing workflow duplication + ### Bug Fixes - Fix `register.yml` premature service start: removed step that ran