From bccef77e9d0a1e9655692847377100828eac3baf Mon Sep 17 00:00:00 2001 From: amimas Date: Sat, 6 Jun 2026 11:01:30 -0400 Subject: [PATCH 1/9] build(ci): restructure GH Actions workflow (#5) --- .github/workflows/_main.yml | 28 ++- .github/workflows/_releases.yml | 79 ++++---- .github/workflows/build.yml | 85 +++++++++ .github/workflows/docs.yml | 28 --- .github/workflows/linters.yml | 67 ------- .../workflows/{prs.yml => pr-ci-workflow.yml} | 57 ++---- .github/workflows/pr_on_main_repo.yml | 29 --- .../{pr_on_fork.yml => prs-entrypoint.yml} | 12 +- .github/workflows/static-analysis.yml | 56 ++++++ .github/workflows/tests-acceptance.yml | 92 +++++++++ .github/workflows/tests-premium.yml | 56 ------ .github/workflows/tests-standard.yml | 176 ------------------ .github/workflows/tests-ultimate.yml | 59 ------ .github/workflows/tests.yml | 49 +++++ 14 files changed, 362 insertions(+), 511 deletions(-) create mode 100644 .github/workflows/build.yml delete mode 100644 .github/workflows/docs.yml delete mode 100644 .github/workflows/linters.yml rename .github/workflows/{prs.yml => pr-ci-workflow.yml} (78%) delete mode 100644 .github/workflows/pr_on_main_repo.yml rename .github/workflows/{pr_on_fork.yml => prs-entrypoint.yml} (56%) create mode 100644 .github/workflows/static-analysis.yml create mode 100644 .github/workflows/tests-acceptance.yml delete mode 100644 .github/workflows/tests-premium.yml delete mode 100644 .github/workflows/tests-standard.yml delete mode 100644 .github/workflows/tests-ultimate.yml create mode 100644 .github/workflows/tests.yml diff --git a/.github/workflows/_main.yml b/.github/workflows/_main.yml index c021efbc3..d36d8a077 100644 --- a/.github/workflows/_main.yml +++ b/.github/workflows/_main.yml @@ -18,31 +18,29 @@ on: jobs: - build-docs: - uses: ./.github/workflows/docs.yml + build: + name: Build + uses: ./.github/workflows/build.yml with: BRANCH_REF: ${{ github.ref }} - linters: - uses: ./.github/workflows/linters.yml + static-analysis: + name: Lint + uses: ./.github/workflows/static-analysis.yml with: BRANCH_REF: ${{ github.ref }} - tests-standard: - uses: ./.github/workflows/tests-standard.yml + tests: + name: Tests + uses: ./.github/workflows/tests.yml with: BRANCH_REF: ${{ github.ref }} - - tests-premium: - uses: ./.github/workflows/tests-premium.yml - with: - BRANCH_REF: ${{ github.ref }} - environment: Main Tests secrets: inherit - tests-ultimate: - uses: ./.github/workflows/tests-ultimate.yml + tests-acceptance: + name: Acceptance Tests + uses: ./.github/workflows/tests-acceptance.yml with: BRANCH_REF: ${{ github.ref }} environment: Main Tests - secrets: inherit \ No newline at end of file + secrets: inherit diff --git a/.github/workflows/_releases.yml b/.github/workflows/_releases.yml index fff405b8b..179d7a9b4 100644 --- a/.github/workflows/_releases.yml +++ b/.github/workflows/_releases.yml @@ -13,50 +13,53 @@ permissions: jobs: - linters: - uses: ./.github/workflows/linters.yml + static-analysis: + name: Lint + uses: ./.github/workflows/static-analysis.yml with: BRANCH_REF: ${{ github.ref }} - tests-standard: - uses: ./.github/workflows/tests-standard.yml + tests: + name: Tests + uses: ./.github/workflows/tests.yml with: BRANCH_REF: ${{ github.ref }} + secrets: inherit - tests-premium: - uses: ./.github/workflows/tests-premium.yml + tests-acceptance: + name: Acceptance Tests + uses: ./.github/workflows/tests-acceptance.yml with: BRANCH_REF: ${{ github.ref }} environment: Main Tests secrets: inherit - tests-ultimate: - uses: ./.github/workflows/tests-ultimate.yml + build-and-test-artifacts: + name: Build & Test + needs: + - static-analysis + - tests + - tests-acceptance + uses: ./.github/workflows/build.yml with: BRANCH_REF: ${{ github.ref }} - environment: Main Tests - secrets: inherit publish-docs: + name: Docs needs: - - linters - - tests-standard - - tests-premium - - tests-ultimate + - build-and-test-artifacts runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - name: Checkout repository + uses: actions/checkout@v6 with: ref: ${{ github.ref }} fetch-depth: 2 - - name: Configure uv environment - uses: ./.github/actions/setup-uv-local - - name: Setup Python ${{ env.UV_PYTHON }} and install dependencies - run: | - uv sync --frozen --no-dev --group docs - - name: Build docs - run: | - uv run --no-sync docs build + - name: Download built documentation + uses: actions/download-artifact@v4 + with: + name: docs-site + path: site - name: Deploy 馃殌 uses: JamesIves/github-pages-deploy-action@v4.8.0 with: @@ -64,11 +67,9 @@ jobs: folder: site publish-to-github: + name: Publish Release needs: - - linters - - tests-standard - - tests-premium - - tests-ultimate + - build-and-test-artifacts runs-on: ubuntu-latest steps: - name: Create release in GitHub @@ -80,11 +81,9 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} publish-to-pypi: + name: Publish PyPI needs: - - linters - - tests-standard - - tests-premium - - tests-ultimate + - build-and-test-artifacts runs-on: ubuntu-latest environment: name: pypi @@ -92,15 +91,20 @@ jobs: permissions: id-token: write # IMPORTANT: this permission is mandatory for trusted publishing steps: - - uses: actions/checkout@v6 + - name: Checkout repository + uses: actions/checkout@v6 with: ref: ${{ github.ref }} fetch-depth: 2 - name: Configure uv environment uses: ./.github/actions/setup-uv-local + with: + python-version: "3.14" - name: Setup Python ${{ env.UV_PYTHON }} and install dependencies run: | uv sync --frozen --no-dev --group release + # TODO: In a separate PR, consume the build artifact (package-dist) uploaded + # in build-and-test-artifacts instead of rebuilding and verifying the package here. - name: Build and verify the package run: | uv run --no-sync package build @@ -110,15 +114,16 @@ jobs: uv run --no-sync package publish publish-to-ghcr: + name: Publish GHCR needs: - - linters - - tests-standard - - tests-premium - - tests-ultimate + - build-and-test-artifacts - publish-to-pypi runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + # TODO: In a separate PR, optimize docker image build to consume the built package artifact + # (package-dist) instead of copying and rebuilding from source if applicable. + - name: Checkout repository + uses: actions/checkout@v6 - name: Get the version from the tag run: echo "VERSION=${GITHUB_REF/refs\/tags\/v/}" >> $GITHUB_ENV - name: Docker metadata diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000..ac2fdd8e3 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,85 @@ +name: Build and package artifacts + +on: + workflow_call: + inputs: + BRANCH_REF: + type: string + required: true + +permissions: + contents: read + +jobs: + package: + name: Package + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + ref: ${{ inputs.BRANCH_REF }} + fetch-depth: 2 + - name: Configure uv environment + uses: ./.github/actions/setup-uv-local + with: + python-version: "3.14" + - name: Install dependencies + run: uv sync --frozen --no-dev --group release + - name: Build and verify the package + run: | + uv run --no-sync package build + uv run --no-sync package verify + - name: Upload package distribution + uses: actions/upload-artifact@v4 + with: + name: package-dist + path: dist/* + + docs: + name: Docs + runs-on: ubuntu-latest + needs: package + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + ref: ${{ inputs.BRANCH_REF }} + fetch-depth: 2 + - name: Configure uv environment + uses: ./.github/actions/setup-uv-local + with: + python-version: "3.14" + - name: Install dependencies + run: uv sync --frozen --no-dev --group docs + - name: Build documentation + run: uv run --no-sync docs build + - name: Upload docs site + uses: actions/upload-artifact@v4 + with: + name: docs-site + path: site + + smoke-tests: + name: "Smoke (${{ matrix.os == 'ubuntu-latest' && 'Linux' || matrix.os == 'macos-latest' && 'macOS' || 'Windows' }}, ${{ matrix.python-version }})" + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + python-version: ["3.14", "3.12"] + needs: package + runs-on: ${{ matrix.os }} + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + ref: ${{ inputs.BRANCH_REF }} + fetch-depth: 2 + - name: Configure uv environment + uses: ./.github/actions/setup-uv-local + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: uv sync --frozen --no-dev --group test + - name: Run smoke tests + run: uv run --no-sync gitlabform -V diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml deleted file mode 100644 index 916fc1dda..000000000 --- a/.github/workflows/docs.yml +++ /dev/null @@ -1,28 +0,0 @@ -name: Docs - -on: - workflow_call: - inputs: - BRANCH_REF: - type: string - required: true - -permissions: - contents: read - -jobs: - build-docs: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - with: - ref: ${{ inputs.BRANCH_REF }} - fetch-depth: 2 - - name: Configure uv environment - uses: ./.github/actions/setup-uv-local - - name: Setup Python ${{ env.UV_PYTHON }} and install dependencies - run: | - uv sync --frozen --no-dev --group docs - - name: Build documentation - run: | - uv run --no-sync docs build diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml deleted file mode 100644 index 3e0689b13..000000000 --- a/.github/workflows/linters.yml +++ /dev/null @@ -1,67 +0,0 @@ -name: Linters - -on: - workflow_call: - inputs: - BRANCH_REF: - type: string - required: true - -permissions: - contents: read - -jobs: - black-formatting: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - with: - ref: ${{ inputs.BRANCH_REF }} - fetch-depth: 2 - - name: Configure uv environment - uses: ./.github/actions/setup-uv-local - - name: Setup Python ${{ env.UV_PYTHON }} and install dependencies - run: | - uv sync --frozen --no-dev --group lint - - name: Run black formatting check - run: | - uv run --no-sync qa lint black --check . - - types: - runs-on: ubuntu-latest - strategy: - max-parallel: 1 - matrix: - python-version: [3.14] - fail-fast: false - steps: - - uses: actions/checkout@v6 - with: - ref: ${{ inputs.BRANCH_REF }} - fetch-depth: 2 - - name: Configure uv environment - uses: ./.github/actions/setup-uv-local - with: - python-version: ${{ matrix.python-version }} - - name: Setup Python ${{ matrix.python-version }} and install dependencies - run: | - uv sync --frozen --no-dev --group lint - - name: Run mypy - run: | - uv run --no-sync qa lint mypy . - - bandit: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - with: - ref: ${{ inputs.BRANCH_REF }} - fetch-depth: 2 - - name: Configure uv environment - uses: ./.github/actions/setup-uv-local - - name: Setup Python ${{ env.UV_PYTHON }} and install dependencies - run: | - uv sync --frozen --no-dev --group lint - - name: Run bandit - run: | - uv run --no-sync qa lint bandit . diff --git a/.github/workflows/prs.yml b/.github/workflows/pr-ci-workflow.yml similarity index 78% rename from .github/workflows/prs.yml rename to .github/workflows/pr-ci-workflow.yml index 6c4d006f1..454eb9190 100644 --- a/.github/workflows/prs.yml +++ b/.github/workflows/pr-ci-workflow.yml @@ -9,54 +9,31 @@ on: jobs: - approve: - runs-on: ubuntu-latest - steps: - - name: Approve - run: echo For security reasons, all pull requests need to be approved first before running the Premium Acceptance Tests. - - build-docs: - uses: ./.github/workflows/docs.yml - permissions: - contents: read - pull-requests: write - with: - branch_ref: ${{ inputs.BRANCH_REF }} - - linters: - uses: ./.github/workflows/linters.yml - permissions: - contents: read - pull-requests: write + build: + name: Build + uses: ./.github/workflows/build.yml with: - branch_ref: ${{ inputs.BRANCH_REF }} + BRANCH_REF: ${{ inputs.BRANCH_REF }} - tests-standard: - uses: ./.github/workflows/tests-standard.yml - permissions: - contents: read - pull-requests: write + static-analysis: + name: Lint + uses: ./.github/workflows/static-analysis.yml with: - branch_ref: ${{ inputs.BRANCH_REF }} + BRANCH_REF: ${{ inputs.BRANCH_REF }} - tests-premium: - uses: ./.github/workflows/tests-premium.yml - permissions: - contents: read - pull-requests: write + tests: + name: Tests + uses: ./.github/workflows/tests.yml with: - branch_ref: ${{ inputs.BRANCH_REF }} - environment: Integrate Pull Request # Our dummy environment + BRANCH_REF: ${{ inputs.BRANCH_REF }} secrets: inherit - tests-ultimate: - uses: ./.github/workflows/tests-ultimate.yml - permissions: - contents: read - pull-requests: write + tests-acceptance: + name: Acceptance Tests + uses: ./.github/workflows/tests-acceptance.yml with: - branch_ref: ${{ inputs.BRANCH_REF }} - environment: Integrate Pull Request # Our dummy environment + BRANCH_REF: ${{ inputs.BRANCH_REF }} + environment: Integrate Pull Request secrets: inherit analyze: diff --git a/.github/workflows/pr_on_main_repo.yml b/.github/workflows/pr_on_main_repo.yml deleted file mode 100644 index 3bbb29887..000000000 --- a/.github/workflows/pr_on_main_repo.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: Linter and tests (PRs raised on main repo) - -permissions: - actions: read - contents: read - pull-requests: write -concurrency: - group: ${{github.workflow}}-${{ github.event.pull_request.number }} - cancel-in-progress: true - -on: - pull_request: - branches: - - main - -jobs: - pr_jobs: - # github.event.pull_request object defined here: https://docs.github.com/en/rest/pulls/pulls?apiVersion=2026-03-10#get-a-pull-request - if: ${{ github.event.pull_request.head.repo.full_name == 'gitlabform/gitlabform' }} - uses: ./.github/workflows/prs.yml - permissions: - actions: read - contents: read - packages: read - pull-requests: write - security-events: write - with: - branch_ref: ${{ github.event.pull_request.head.sha }} - secrets: inherit \ No newline at end of file diff --git a/.github/workflows/pr_on_fork.yml b/.github/workflows/prs-entrypoint.yml similarity index 56% rename from .github/workflows/pr_on_fork.yml rename to .github/workflows/prs-entrypoint.yml index fe255d2bf..2bf22e722 100644 --- a/.github/workflows/pr_on_fork.yml +++ b/.github/workflows/prs-entrypoint.yml @@ -1,4 +1,4 @@ -name: Linter and tests (PRs raised from Forks) +name: PR CI permissions: actions: read @@ -9,15 +9,19 @@ concurrency: cancel-in-progress: true on: + pull_request: + branches: + - main pull_request_target: branches: - main jobs: pr_jobs: + name: PR CI # github.event.pull_request object defined here: https://docs.github.com/en/rest/pulls/pulls?apiVersion=2026-03-10#get-a-pull-request - if: ${{ github.event.pull_request.head.repo.full_name != 'gitlabform/gitlabform' }} - uses: ./.github/workflows/prs.yml + if: ${{ (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == 'gitlabform/gitlabform') || (github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name != 'gitlabform/gitlabform') }} + uses: ./.github/workflows/pr-ci-workflow.yml permissions: actions: read contents: read @@ -25,5 +29,5 @@ jobs: pull-requests: write security-events: write with: - branch_ref: ${{ github.event.pull_request.head.sha }} + BRANCH_REF: ${{ github.event.pull_request.head.sha }} secrets: inherit \ No newline at end of file diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml new file mode 100644 index 000000000..6c346402d --- /dev/null +++ b/.github/workflows/static-analysis.yml @@ -0,0 +1,56 @@ +name: Static Analysis + +on: + workflow_call: + inputs: + BRANCH_REF: + type: string + required: true + +permissions: + contents: read + +# This workflow currently covers lockfile verification plus lint-style checks. +# Bandit is treated here as a lightweight quality/security guardrail, not a full +# security scanning pipeline. Future security-only workflows can be introduced +# once heavier security tooling is needed. +jobs: + lockfile-verify: + name: Lockfile + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + ref: ${{ inputs.BRANCH_REF }} + - name: Configure uv environment + uses: ./.github/actions/setup-uv-local + - name: Verify that uv.lock is in sync + run: uv lock --check + + linting: + name: "${{ matrix.check == 'black' && 'Black' || matrix.check == 'mypy' && 'Mypy' || 'Bandit' }}" + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + check: [black, mypy, bandit] + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + ref: ${{ inputs.BRANCH_REF }} + fetch-depth: 2 + - name: Configure uv environment + uses: ./.github/actions/setup-uv-local + - name: Install dependencies + run: uv sync --frozen --no-dev --group lint + - name: Run ${{ matrix.check }} + run: | + if [ "${{ matrix.check }}" == "black" ]; then + uv run --no-sync qa lint black --check . + elif [ "${{ matrix.check }}" == "mypy" ]; then + uv run --no-sync qa lint mypy . + elif [ "${{ matrix.check }}" == "bandit" ]; then + uv run --no-sync qa lint bandit . + fi diff --git a/.github/workflows/tests-acceptance.yml b/.github/workflows/tests-acceptance.yml new file mode 100644 index 000000000..6b7f00b3f --- /dev/null +++ b/.github/workflows/tests-acceptance.yml @@ -0,0 +1,92 @@ +name: Acceptance Tests + +on: + workflow_call: + inputs: + BRANCH_REF: + type: string + required: true + environment: + description: "Environment to run licensed acceptance tests in: 'Integrate Pull Request' for PRs (requires approval) or 'Main Tests' for main/release runs." + type: string + required: true + secrets: + GITLAB_EE_LICENSE: + required: false + GITLAB_EE_ULTIMATE_LICENSE: + required: false + CODECOV_TOKEN: + required: true + +permissions: + contents: read + actions: read # Required by codecov/codecov-action + +jobs: + acceptance-standard: + name: "GitLab ${{ matrix.flavor == 'ce' && 'CE' || 'EE' }}" + runs-on: ubuntu-latest + strategy: + matrix: + flavor: [ce, ee] + fail-fast: false + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + ref: ${{ inputs.BRANCH_REF }} + fetch-depth: 2 + - name: Configure uv environment + uses: ./.github/actions/setup-uv-local + - name: Install dependencies + run: uv sync --frozen --no-dev --group test + - name: Start GitLab (${{ matrix.flavor }}) in docker + run: uv run --no-sync gitlab-local up --gitlab-flavor ${{ matrix.flavor }} + - name: Run Standard acceptance tests for ${{ matrix.flavor }} flavor + run: uv run --no-sync qa test tests/acceptance/standard --cov=. --cov-report=xml --durations=0 --reruns 3 --reruns-delay 10 --log-cli-level=WARNING + - name: Upload coverage + uses: Wandalen/wretry.action@v3 + with: + action: codecov/codecov-action@v3 + with: | + name: codecov-acceptance-test-standard-${{ matrix.flavor }} + flags: integration + token: ${{ secrets.CODECOV_TOKEN }} + attempt_limit: 5 + attempt_delay: 10000 + + acceptance-licensed: + name: "GitLab ${{ matrix.tier == 'premium' && 'Premium' || 'Ultimate' }}" + needs: [acceptance-standard] + environment: ${{ inputs.environment }} + runs-on: ubuntu-latest + strategy: + matrix: + tier: [premium, ultimate] + fail-fast: false + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + ref: ${{ inputs.BRANCH_REF }} + fetch-depth: 2 + - name: Configure uv environment + uses: ./.github/actions/setup-uv-local + - name: Install dependencies + run: uv sync --frozen --no-dev --group test + - name: Start GitLab (${{ matrix.tier }}) in docker + env: + GITLAB_EE_LICENSE: ${{ matrix.tier == 'premium' && secrets.GITLAB_EE_LICENSE || secrets.GITLAB_EE_ULTIMATE_LICENSE }} + run: uv run --no-sync gitlab-local up + - name: Run acceptance Tests for ${{ matrix.tier }} features + run: uv run --no-sync qa test tests/acceptance/${{ matrix.tier }} --cov=. --cov-report=xml --durations=0 --reruns 3 --reruns-delay 10 --log-cli-level=WARNING + - name: Upload coverage + uses: Wandalen/wretry.action@v3 + with: + action: codecov/codecov-action@v3 + with: | + name: codecov-acceptance-test-${{ matrix.tier }} + flags: integration + token: ${{ secrets.CODECOV_TOKEN }} + attempt_limit: 5 + attempt_delay: 10000 diff --git a/.github/workflows/tests-premium.yml b/.github/workflows/tests-premium.yml deleted file mode 100644 index 66f60eb62..000000000 --- a/.github/workflows/tests-premium.yml +++ /dev/null @@ -1,56 +0,0 @@ -name: Tests premium - -on: - workflow_call: - inputs: - BRANCH_REF: - type: string - required: true - environment: - description: "Environment to run tests in: 'Integrate Pull Request' for PRs (requires approval) or 'Main' for Main." - type: string - required: true - -permissions: - contents: read - -jobs: - acceptance-tests-premium: - environment: - name: ${{ inputs.environment }} - runs-on: ubuntu-latest - strategy: - max-parallel: 5 - matrix: - python-version: [3.14] - fail-fast : false - steps: - - uses: actions/checkout@v6 - with: - ref: ${{ inputs.BRANCH_REF }} - - name: Configure uv environment - uses: ./.github/actions/setup-uv-local - with: - python-version: ${{ matrix.python-version }} - - name: Setup Python ${{ matrix.python-version }} and install dependencies - run: | - uv sync --frozen --no-dev --group test - - name: Start GitLab in Docker - env: - GITLAB_EE_LICENSE: ${{ secrets.GITLAB_EE_LICENSE }} - run: | - uv run --no-sync gitlab-local up - - name: Run acceptance tests for premium features - run: | - uv run --no-sync qa test tests/acceptance/premium --cov=. --cov-report=xml --durations=0 --reruns 3 --reruns-delay 10 --log-cli-level=WARNING - - name: Upload coverage to Codecov - uses: Wandalen/wretry.action@v3 - with: - action: codecov/codecov-action@v3 - with: | - name: codecov-acceptance-test-premium - flags: integration - fail_ci_if_error: true - token: 3e6d6cb5-fcdb-41ea-b134-f6c5856363e9 - attempt_limit: 5 - attempt_delay: 10000 diff --git a/.github/workflows/tests-standard.yml b/.github/workflows/tests-standard.yml deleted file mode 100644 index 554e3dd43..000000000 --- a/.github/workflows/tests-standard.yml +++ /dev/null @@ -1,176 +0,0 @@ -name: Tests standard - -on: - workflow_call: - inputs: - BRANCH_REF: - type: string - required: true - -permissions: - contents: read - -jobs: - lockfile-verify: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - with: - ref: ${{ inputs.BRANCH_REF }} - - name: Configure uv environment - uses: ./.github/actions/setup-uv-local - - name: Verify that uv.lock is in sync with pyproject.toml - run: | - uv lock --check - - acceptance-tests: - runs-on: ubuntu-latest - strategy: - max-parallel: 5 - matrix: - python-version: [3.14] - fail-fast : false - steps: - - uses: actions/checkout@v6 - with: - ref: ${{ inputs.BRANCH_REF }} - fetch-depth: 2 - - name: Configure uv environment - uses: ./.github/actions/setup-uv-local - with: - python-version: ${{ matrix.python-version }} - - name: Setup Python ${{ matrix.python-version }} and install dependencies - run: | - uv sync --frozen --no-dev --group test - - name: Start GitLab in Docker - run: | - uv run --no-sync gitlab-local up - - name: Run standard acceptance tests against Enterprise Edition - run: uv run --no-sync qa test tests/acceptance/standard --cov=. --cov-report=xml --durations=0 --reruns 3 --reruns-delay 10 --log-cli-level=WARNING - - name: Upload coverage to Codecov - uses: Wandalen/wretry.action@v3 - with: - action: codecov/codecov-action@v3 - with: | - name: codecov-acceptance-test-standard - flags: integration - fail_ci_if_error: true - token: 3e6d6cb5-fcdb-41ea-b134-f6c5856363e9 - attempt_limit: 5 - attempt_delay: 10000 - - acceptance-tests-CE: - runs-on: ubuntu-latest - strategy: - max-parallel: 5 - matrix: - python-version: [ 3.14 ] - fail-fast: false - steps: - - uses: actions/checkout@v6 - with: - ref: ${{ inputs.BRANCH_REF }} - fetch-depth: 2 - - name: Configure uv environment - uses: ./.github/actions/setup-uv-local - with: - python-version: ${{ matrix.python-version }} - - name: Setup Python ${{ matrix.python-version }} and install dependencies - run: | - uv sync --frozen --no-dev --group test - - name: Start GitLab in Docker - run: | - uv run --no-sync gitlab-local up --gitlab-flavor ce - - name: Run standard acceptance tests against Community Edition - run: uv run --no-sync qa test tests/acceptance/standard --cov=. --cov-report=xml --durations=0 --reruns 3 --reruns-delay 10 --log-cli-level=WARNING - - name: Upload coverage to Codecov - uses: Wandalen/wretry.action@v3 - with: - action: codecov/codecov-action@v3 - with: | - name: codecov-acceptance-test-standard-ce - flags: integration - fail_ci_if_error: true - token: 3e6d6cb5-fcdb-41ea-b134-f6c5856363e9 - attempt_limit: 5 - attempt_delay: 10000 - - unit-tests: - runs-on: ubuntu-latest - strategy: - max-parallel: 1 - matrix: - python-version: [3.14, 3.12] - fail-fast : false - steps: - - uses: actions/checkout@v6 - with: - ref: ${{ inputs.BRANCH_REF }} - fetch-depth: 2 - - name: Configure uv environment - uses: ./.github/actions/setup-uv-local - with: - python-version: ${{ matrix.python-version }} - - name: Setup Python ${{ matrix.python-version }} and install dependencies - run: | - uv sync --frozen --no-dev --group test - - name: Run unit tests - run: | - uv run --no-sync qa test tests/unit --cov=. --cov-report=xml --log-cli-level=WARNING - - name: Upload coverage to Codecov - uses: Wandalen/wretry.action@v3 - with: - action: codecov/codecov-action@v3 - with: | - flags: unittests - fail_ci_if_error: true - token: 3e6d6cb5-fcdb-41ea-b134-f6c5856363e9 - attempt_limit: 5 - attempt_delay: 10000 - - security-tests: - runs-on: ubuntu-latest - strategy: - max-parallel: 5 - matrix: - python-version: [ 3.14, 3.12 ] - fail-fast: false - steps: - - uses: actions/checkout@v6 - with: - ref: ${{ inputs.BRANCH_REF }} - fetch-depth: 2 - - name: Configure uv environment - uses: ./.github/actions/setup-uv-local - with: - python-version: ${{ matrix.python-version }} - - name: Setup Python ${{ matrix.python-version }} and install dependencies - run: | - uv sync --frozen --no-dev --group test - - name: Run security tests - run: | - uv run --no-sync qa lint bandit gitlabform - - smoke-tests: - strategy: - max-parallel: 2 - matrix: - os: [ubuntu-latest, windows-latest, macos-latest] - python-version: [ 3.14, 3.12 ] - fail-fast: true - runs-on: ${{ matrix.os }} - steps: - - uses: actions/checkout@v6 - with: - ref: ${{ inputs.BRANCH_REF }} - fetch-depth: 2 - - name: Configure uv environment - uses: ./.github/actions/setup-uv-local - with: - python-version: ${{ matrix.python-version }} - - name: Setup Python ${{ matrix.python-version }} and install dependencies - run: | - uv sync --frozen --no-dev --group test - - name: Run smoke tests - run: | - uv run --no-sync gitlabform -V diff --git a/.github/workflows/tests-ultimate.yml b/.github/workflows/tests-ultimate.yml deleted file mode 100644 index 35552fac1..000000000 --- a/.github/workflows/tests-ultimate.yml +++ /dev/null @@ -1,59 +0,0 @@ -name: Tests Ultimate - -on: - workflow_call: - inputs: - BRANCH_REF: - type: string - required: true - environment: - description: "Environment to run tests in: 'Integrate Pull Request' for PRs (requires approval) or 'Main' for Main." - type: string - required: true - -permissions: - contents: read - -jobs: - acceptance-tests-ultimate: - environment: - name: ${{ inputs.environment }} - runs-on: ubuntu-latest - strategy: - max-parallel: 5 - matrix: - python-version: [3.14] - fail-fast : false - steps: - - uses: actions/checkout@v6 - with: - ref: ${{ inputs.BRANCH_REF }} - - name: Configure uv environment - uses: ./.github/actions/setup-uv-local - with: - python-version: ${{ matrix.python-version }} - - name: Setup Python ${{ matrix.python-version }} and install dependencies - # uv will automatically create and use a virtual environment for the specified Python version - # Only install dependencies needed for running tests, to save time and resources. - run: | - uv sync --frozen --no-dev --group test - - name: Start GitLab in Docker - env: - # requested via: https://handbook.gitlab.com/handbook/marketing/developer-relations/contributor-success/community-contributors-workflows/#contributing-to-the-gitlab-enterprise-edition-ee - # https://gitlab.com/gitlab-org/developer-relations/contributor-success/team-task/-/issues - GITLAB_EE_LICENSE: ${{ secrets.GITLAB_EE_ULTIMATE_LICENSE }} - run: | - uv run --no-sync gitlab-local up - - name: Run acceptance tests for ultimate features - run: | - uv run --no-sync qa test tests/acceptance/ultimate --cov=. --cov-report=xml --durations=0 --reruns 3 --reruns-delay 10 --log-cli-level=WARNING - - name: Upload coverage to Codecov - uses: Wandalen/wretry.action@v3 - with: - action: codecov/codecov-action@v3 - with: | - name: codecov-acceptance-test-ultimate - flags: integration - token: 3e6d6cb5-fcdb-41ea-b134-f6c5856363e9 - attempt_limit: 5 - attempt_delay: 10000 \ No newline at end of file diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 000000000..625612280 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,49 @@ +name: Tests +# This workflow is the canonical tests domain. Add future test jobs (integration, component, regression) here. + +on: + workflow_call: + inputs: + BRANCH_REF: + type: string + required: true + secrets: + CODECOV_TOKEN: + required: true + +permissions: + contents: read + +jobs: + unit: + name: "Unit (${{ matrix.python-version }})" + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.14", "3.12"] + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + ref: ${{ inputs.BRANCH_REF }} + fetch-depth: 2 + - name: Configure uv environment + uses: ./.github/actions/setup-uv-local + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: uv sync --frozen --no-dev --group test + - name: Run unit tests + run: uv run --no-sync qa test tests/unit --cov=. --cov-report=xml --log-cli-level=WARNING + - name: Upload coverage to Codecov + uses: Wandalen/wretry.action@v3 + with: + action: codecov/codecov-action@v3 + with: | + name: unit-tests-${{ matrix.python-version }} + flags: unittests + fail_ci_if_error: true + token: ${{ secrets.CODECOV_TOKEN }} + attempt_limit: 5 + attempt_delay: 10000 From 40b68523d2227bbfc4c4f6a918b61afbd4d7943f Mon Sep 17 00:00:00 2001 From: amimas Date: Sat, 6 Jun 2026 11:40:23 -0400 Subject: [PATCH 2/9] fix(ci): fix GH Actions expressions and clarify PR workflow names (#6) --- .github/workflows/build.yml | 1 - .github/workflows/pr-ci-workflow.yml | 2 +- .github/workflows/prs-entrypoint.yml | 4 ++-- .github/workflows/tests-acceptance.yml | 5 +++-- .github/workflows/tests.yml | 1 + 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ac2fdd8e3..4bd5108ce 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -39,7 +39,6 @@ jobs: docs: name: Docs runs-on: ubuntu-latest - needs: package steps: - name: Checkout repository uses: actions/checkout@v6 diff --git a/.github/workflows/pr-ci-workflow.yml b/.github/workflows/pr-ci-workflow.yml index 454eb9190..5f35e7b23 100644 --- a/.github/workflows/pr-ci-workflow.yml +++ b/.github/workflows/pr-ci-workflow.yml @@ -1,4 +1,4 @@ -name: Linter and tests (PRs) +name: "Build 路 Lint 路 Tests" on: workflow_call: diff --git a/.github/workflows/prs-entrypoint.yml b/.github/workflows/prs-entrypoint.yml index 2bf22e722..41a0a7fcc 100644 --- a/.github/workflows/prs-entrypoint.yml +++ b/.github/workflows/prs-entrypoint.yml @@ -1,4 +1,4 @@ -name: PR CI +name: "PR CI" permissions: actions: read @@ -18,7 +18,7 @@ on: jobs: pr_jobs: - name: PR CI + name: "Trigger" # github.event.pull_request object defined here: https://docs.github.com/en/rest/pulls/pulls?apiVersion=2026-03-10#get-a-pull-request if: ${{ (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == 'gitlabform/gitlabform') || (github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name != 'gitlabform/gitlabform') }} uses: ./.github/workflows/pr-ci-workflow.yml diff --git a/.github/workflows/tests-acceptance.yml b/.github/workflows/tests-acceptance.yml index 6b7f00b3f..c2edc4067 100644 --- a/.github/workflows/tests-acceptance.yml +++ b/.github/workflows/tests-acceptance.yml @@ -45,6 +45,7 @@ jobs: - name: Run Standard acceptance tests for ${{ matrix.flavor }} flavor run: uv run --no-sync qa test tests/acceptance/standard --cov=. --cov-report=xml --durations=0 --reruns 3 --reruns-delay 10 --log-cli-level=WARNING - name: Upload coverage + if: ${{ secrets.CODECOV_TOKEN != '' }} uses: Wandalen/wretry.action@v3 with: action: codecov/codecov-action@v3 @@ -57,7 +58,6 @@ jobs: acceptance-licensed: name: "GitLab ${{ matrix.tier == 'premium' && 'Premium' || 'Ultimate' }}" - needs: [acceptance-standard] environment: ${{ inputs.environment }} runs-on: ubuntu-latest strategy: @@ -76,11 +76,12 @@ jobs: run: uv sync --frozen --no-dev --group test - name: Start GitLab (${{ matrix.tier }}) in docker env: - GITLAB_EE_LICENSE: ${{ matrix.tier == 'premium' && secrets.GITLAB_EE_LICENSE || secrets.GITLAB_EE_ULTIMATE_LICENSE }} + GITLAB_EE_LICENSE: "${{ matrix.tier == 'premium' && secrets.GITLAB_EE_LICENSE || secrets.GITLAB_EE_ULTIMATE_LICENSE }}" run: uv run --no-sync gitlab-local up - name: Run acceptance Tests for ${{ matrix.tier }} features run: uv run --no-sync qa test tests/acceptance/${{ matrix.tier }} --cov=. --cov-report=xml --durations=0 --reruns 3 --reruns-delay 10 --log-cli-level=WARNING - name: Upload coverage + if: ${{ secrets.CODECOV_TOKEN != '' }} uses: Wandalen/wretry.action@v3 with: action: codecov/codecov-action@v3 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 625612280..e2f5fe101 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -37,6 +37,7 @@ jobs: - name: Run unit tests run: uv run --no-sync qa test tests/unit --cov=. --cov-report=xml --log-cli-level=WARNING - name: Upload coverage to Codecov + if: ${{ secrets.CODECOV_TOKEN != '' }} uses: Wandalen/wretry.action@v3 with: action: codecov/codecov-action@v3 From 477aa3f596f19e47053336268a6fcbfe29c0fadc Mon Sep 17 00:00:00 2001 From: amimas Date: Sat, 6 Jun 2026 16:18:01 -0400 Subject: [PATCH 3/9] ci: fix workflow syntax due to secrets checking (#7) --- .github/workflows/tests-acceptance.yml | 2 -- .github/workflows/tests.yml | 1 - 2 files changed, 3 deletions(-) diff --git a/.github/workflows/tests-acceptance.yml b/.github/workflows/tests-acceptance.yml index c2edc4067..924fa4ad1 100644 --- a/.github/workflows/tests-acceptance.yml +++ b/.github/workflows/tests-acceptance.yml @@ -45,7 +45,6 @@ jobs: - name: Run Standard acceptance tests for ${{ matrix.flavor }} flavor run: uv run --no-sync qa test tests/acceptance/standard --cov=. --cov-report=xml --durations=0 --reruns 3 --reruns-delay 10 --log-cli-level=WARNING - name: Upload coverage - if: ${{ secrets.CODECOV_TOKEN != '' }} uses: Wandalen/wretry.action@v3 with: action: codecov/codecov-action@v3 @@ -81,7 +80,6 @@ jobs: - name: Run acceptance Tests for ${{ matrix.tier }} features run: uv run --no-sync qa test tests/acceptance/${{ matrix.tier }} --cov=. --cov-report=xml --durations=0 --reruns 3 --reruns-delay 10 --log-cli-level=WARNING - name: Upload coverage - if: ${{ secrets.CODECOV_TOKEN != '' }} uses: Wandalen/wretry.action@v3 with: action: codecov/codecov-action@v3 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e2f5fe101..625612280 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -37,7 +37,6 @@ jobs: - name: Run unit tests run: uv run --no-sync qa test tests/unit --cov=. --cov-report=xml --log-cli-level=WARNING - name: Upload coverage to Codecov - if: ${{ secrets.CODECOV_TOKEN != '' }} uses: Wandalen/wretry.action@v3 with: action: codecov/codecov-action@v3 From 31ad8ba5c4904cea20b803bb35def35798ee07e4 Mon Sep 17 00:00:00 2001 From: amimas Date: Sat, 6 Jun 2026 17:07:44 -0400 Subject: [PATCH 4/9] ci: optimize release workflow (#8) - remove redundant jobs in release that also runs when commits pushed to main - add verification step to release workflow to check main branch workflow passed successfully - add option for maintainers to force release in case of issues with main branch workflow --- .github/workflows/_releases.yml | 62 +++++++++++++++++++++------------ docs/contrib/releases.md | 2 ++ 2 files changed, 41 insertions(+), 23 deletions(-) diff --git a/.github/workflows/_releases.yml b/.github/workflows/_releases.yml index 179d7a9b4..7c4e03e75 100644 --- a/.github/workflows/_releases.yml +++ b/.github/workflows/_releases.yml @@ -4,8 +4,16 @@ on: push: tags: - 'v*' - # Allow manual triggering of the workflow + # Allow manual triggering of the workflow. + # A maintainer can use the `force` input to bypass main workflow verification + # when a release must proceed due to unexpected issues with main workflow that prevent normal execution. workflow_dispatch: + inputs: + force: + description: 'Skip main workflow verification and force release' + required: false + default: false + type: boolean permissions: contents: write @@ -13,33 +21,41 @@ permissions: jobs: - static-analysis: - name: Lint - uses: ./.github/workflows/static-analysis.yml - with: - BRANCH_REF: ${{ github.ref }} - - tests: - name: Tests - uses: ./.github/workflows/tests.yml - with: - BRANCH_REF: ${{ github.ref }} - secrets: inherit + verify-main-workflow: + name: Verify main workflow + runs-on: ubuntu-latest + steps: + - name: Check main branch workflow completion + uses: actions/github-script@v7 + with: + script: | + const force = github.context.payload.inputs?.force === true || github.context.payload.inputs?.force === 'true'; + if (force) { + console.log('Force release enabled; skipping main workflow verification.'); + return; + } - tests-acceptance: - name: Acceptance Tests - uses: ./.github/workflows/tests-acceptance.yml - with: - BRANCH_REF: ${{ github.ref }} - environment: Main Tests - secrets: inherit + const runs = await github.rest.actions.listWorkflowRunsForRepo({ + owner: context.repo.owner, + repo: context.repo.repo, + event: 'push', + branch: 'main', + per_page: 50, + }); + const mainRun = runs.data.workflow_runs.find( + run => run.head_sha === context.sha && run.name === 'Main branch' + ); + if (!mainRun) { + throw new Error('No completed main workflow was found for this commit.'); + } + if (mainRun.status !== 'completed' || mainRun.conclusion !== 'success') { + throw new Error(`Main workflow did not pass. status=${mainRun.status} conclusion=${mainRun.conclusion}`); + } build-and-test-artifacts: name: Build & Test needs: - - static-analysis - - tests - - tests-acceptance + - verify-main-workflow uses: ./.github/workflows/build.yml with: BRANCH_REF: ${{ github.ref }} diff --git a/docs/contrib/releases.md b/docs/contrib/releases.md index c5afda5b2..72f951299 100644 --- a/docs/contrib/releases.md +++ b/docs/contrib/releases.md @@ -20,4 +20,6 @@ We try to follow the [PEP 440](https://peps.python.org/pep-0440/) versioning sch - Upload new version of gitlabform to [pypi package registry under gitlabform](https://pypi.org/project/gitlabform/). - A corresponding [GitHub release](https://github.com/gitlabform/gitlabform/releases) will be created that references the new tag. + The release workflow also verifies that the commit on `main` passed the normal main-branch checks before publishing. If a release must proceed despite issues, a maintainer can manually trigger the `Releases` workflow and enable `force: true` to bypass that verification. + 3. Edit the release in GitHub and copy the changelog entry into its description. From a17155fa4b57ccabe1dedc1f5030439a88732ad3 Mon Sep 17 00:00:00 2001 From: amimas Date: Sat, 6 Jun 2026 17:51:08 -0400 Subject: [PATCH 5/9] chore(ci): update workflow names and add config comments --- .github/workflows/_main.yml | 2 +- .github/workflows/pr-ci-workflow.yml | 11 +++- .github/workflows/prs-entrypoint.yml | 13 +++- .github/workflows/tests-unit.yml | 96 ++++++++++++++++++++++++++++ .github/workflows/tests.yml | 49 -------------- 5 files changed, 116 insertions(+), 55 deletions(-) create mode 100644 .github/workflows/tests-unit.yml delete mode 100644 .github/workflows/tests.yml diff --git a/.github/workflows/_main.yml b/.github/workflows/_main.yml index d36d8a077..1802419a3 100644 --- a/.github/workflows/_main.yml +++ b/.github/workflows/_main.yml @@ -32,7 +32,7 @@ jobs: tests: name: Tests - uses: ./.github/workflows/tests.yml + uses: ./.github/workflows/tests-unit.yml with: BRANCH_REF: ${{ github.ref }} secrets: inherit diff --git a/.github/workflows/pr-ci-workflow.yml b/.github/workflows/pr-ci-workflow.yml index 5f35e7b23..95593d1ca 100644 --- a/.github/workflows/pr-ci-workflow.yml +++ b/.github/workflows/pr-ci-workflow.yml @@ -1,4 +1,11 @@ -name: "Build 路 Lint 路 Tests" +name: "CI - Build 路 Lint 路 Tests" + +# Reusable workflow containing the actual CI jobs for pull request validation. +# This file is called by prs-entrypoint.yml and is intentionally separated so that +# event/routing logic stays in the entrypoint while the CI job set remains reusable. + +# This reusable workflow does not receive secrets automatically from the PR event. +# The caller workflow (prs-entrypoint.yml) must explicitly pass secrets when calling it. on: workflow_call: @@ -23,7 +30,7 @@ jobs: tests: name: Tests - uses: ./.github/workflows/tests.yml + uses: ./.github/workflows/tests-unit.yml with: BRANCH_REF: ${{ inputs.BRANCH_REF }} secrets: inherit diff --git a/.github/workflows/prs-entrypoint.yml b/.github/workflows/prs-entrypoint.yml index 41a0a7fcc..7289b62e8 100644 --- a/.github/workflows/prs-entrypoint.yml +++ b/.github/workflows/prs-entrypoint.yml @@ -1,4 +1,8 @@ -name: "PR CI" +name: "CI" + +# This workflow is the entrypoint for pull request validation. +# It selects the correct event path for same-repo vs fork PRs, +# then forwards the branch SHA and explicit secrets to the reusable workflow. permissions: actions: read @@ -9,17 +13,20 @@ concurrency: cancel-in-progress: true on: + # PRs from branches in this repository pull_request: branches: - main + # PRs from forked repositories pull_request_target: branches: - main jobs: pr_jobs: - name: "Trigger" - # github.event.pull_request object defined here: https://docs.github.com/en/rest/pulls/pulls?apiVersion=2026-03-10#get-a-pull-request + name: "Pipeline" + # Only run pull_request for same-repo PRs, and pull_request_target for fork PRs. + # This prevents duplicate workflow runs and keeps fork secret handling safe. if: ${{ (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == 'gitlabform/gitlabform') || (github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name != 'gitlabform/gitlabform') }} uses: ./.github/workflows/pr-ci-workflow.yml permissions: diff --git a/.github/workflows/tests-unit.yml b/.github/workflows/tests-unit.yml new file mode 100644 index 000000000..9c8e6b936 --- /dev/null +++ b/.github/workflows/tests-unit.yml @@ -0,0 +1,96 @@ +name: Unit Tests +# Reusable workflow for unit tests only. + +on: + workflow_call: + inputs: + BRANCH_REF: + type: string + required: true + secrets: + CODECOV_TOKEN: + required: true + +permissions: + contents: read + +jobs: + unit: + name: "Unit (${{ matrix.python-version }})" + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.14", "3.12"] + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + ref: ${{ inputs.BRANCH_REF }} + fetch-depth: 2 + - name: Configure uv environment + uses: ./.github/actions/setup-uv-local + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: uv sync --frozen --no-dev --group test + - name: Run unit tests + run: uv run --no-sync qa test tests/unit --cov=. --cov-report=xml --log-cli-level=WARNING + - name: Upload coverage to Codecov + uses: Wandalen/wretry.action@v3 + with: + action: codecov/codecov-action@v3 + with: | + name: unit-tests-${{ matrix.python-version }} + flags: unittests + fail_ci_if_error: true + token: ${{ secrets.CODECOV_TOKEN }} + attempt_limit: 5 + attempt_delay: 10000 +name: Unit tests + +on: + workflow_call: + inputs: + BRANCH_REF: + type: string + required: true + secrets: + CODECOV_TOKEN: + required: true + +permissions: + contents: read + +jobs: + unit-tests: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.14", "3.12"] + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + ref: ${{ inputs.BRANCH_REF }} + fetch-depth: 2 + - name: Configure uv environment + uses: ./.github/actions/setup-uv-local + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: uv sync --frozen --no-dev --group test + - name: Run unit tests + run: uv run --no-sync qa test tests/unit --cov=. --cov-report=xml --log-cli-level=WARNING + - name: Upload coverage to Codecov + uses: Wandalen/wretry.action@v3 + with: + action: codecov/codecov-action@v3 + with: | + name: unit-tests-${{ matrix.python-version }} + flags: unittests + fail_ci_if_error: true + token: ${{ secrets.CODECOV_TOKEN }} + attempt_limit: 5 + attempt_delay: 10000 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml deleted file mode 100644 index 625612280..000000000 --- a/.github/workflows/tests.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: Tests -# This workflow is the canonical tests domain. Add future test jobs (integration, component, regression) here. - -on: - workflow_call: - inputs: - BRANCH_REF: - type: string - required: true - secrets: - CODECOV_TOKEN: - required: true - -permissions: - contents: read - -jobs: - unit: - name: "Unit (${{ matrix.python-version }})" - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - python-version: ["3.14", "3.12"] - steps: - - name: Checkout repository - uses: actions/checkout@v6 - with: - ref: ${{ inputs.BRANCH_REF }} - fetch-depth: 2 - - name: Configure uv environment - uses: ./.github/actions/setup-uv-local - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: uv sync --frozen --no-dev --group test - - name: Run unit tests - run: uv run --no-sync qa test tests/unit --cov=. --cov-report=xml --log-cli-level=WARNING - - name: Upload coverage to Codecov - uses: Wandalen/wretry.action@v3 - with: - action: codecov/codecov-action@v3 - with: | - name: unit-tests-${{ matrix.python-version }} - flags: unittests - fail_ci_if_error: true - token: ${{ secrets.CODECOV_TOKEN }} - attempt_limit: 5 - attempt_delay: 10000 From 94cde7e13cebd3531201f12ab1858149a4e33f4b Mon Sep 17 00:00:00 2001 From: amimas Date: Sat, 6 Jun 2026 18:02:06 -0400 Subject: [PATCH 6/9] ci: fix ci workflow config syntax (#11) --- .github/workflows/tests-unit.yml | 47 -------------------------------- 1 file changed, 47 deletions(-) diff --git a/.github/workflows/tests-unit.yml b/.github/workflows/tests-unit.yml index 9c8e6b936..9009f1f9c 100644 --- a/.github/workflows/tests-unit.yml +++ b/.github/workflows/tests-unit.yml @@ -47,50 +47,3 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} attempt_limit: 5 attempt_delay: 10000 -name: Unit tests - -on: - workflow_call: - inputs: - BRANCH_REF: - type: string - required: true - secrets: - CODECOV_TOKEN: - required: true - -permissions: - contents: read - -jobs: - unit-tests: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - python-version: ["3.14", "3.12"] - steps: - - name: Checkout repository - uses: actions/checkout@v6 - with: - ref: ${{ inputs.BRANCH_REF }} - fetch-depth: 2 - - name: Configure uv environment - uses: ./.github/actions/setup-uv-local - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: uv sync --frozen --no-dev --group test - - name: Run unit tests - run: uv run --no-sync qa test tests/unit --cov=. --cov-report=xml --log-cli-level=WARNING - - name: Upload coverage to Codecov - uses: Wandalen/wretry.action@v3 - with: - action: codecov/codecov-action@v3 - with: | - name: unit-tests-${{ matrix.python-version }} - flags: unittests - fail_ci_if_error: true - token: ${{ secrets.CODECOV_TOKEN }} - attempt_limit: 5 - attempt_delay: 10000 From 3645f4661570ae9db425592eea1108b0b4e0510f Mon Sep 17 00:00:00 2001 From: amimas Date: Sat, 6 Jun 2026 23:05:52 -0400 Subject: [PATCH 7/9] ci: fix PR workflow auto-cancellation (#13) --- .github/workflows/build.yml | 2 +- .github/workflows/prs-entrypoint-forks.yml | 34 ++++++++++++++++++ .github/workflows/prs-entrypoint-main.yml | 34 ++++++++++++++++++ .github/workflows/prs-entrypoint.yml | 40 ---------------------- 4 files changed, 69 insertions(+), 41 deletions(-) create mode 100644 .github/workflows/prs-entrypoint-forks.yml create mode 100644 .github/workflows/prs-entrypoint-main.yml delete mode 100644 .github/workflows/prs-entrypoint.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4bd5108ce..e146d1025 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -60,7 +60,7 @@ jobs: path: site smoke-tests: - name: "Smoke (${{ matrix.os == 'ubuntu-latest' && 'Linux' || matrix.os == 'macos-latest' && 'macOS' || 'Windows' }}, ${{ matrix.python-version }})" + name: "Smoke test - (${{ matrix.os == 'ubuntu-latest' && 'Linux' || matrix.os == 'macos-latest' && 'macOS' || 'Windows' }}, ${{ matrix.python-version }})" strategy: fail-fast: false matrix: diff --git a/.github/workflows/prs-entrypoint-forks.yml b/.github/workflows/prs-entrypoint-forks.yml new file mode 100644 index 000000000..d665bb656 --- /dev/null +++ b/.github/workflows/prs-entrypoint-forks.yml @@ -0,0 +1,34 @@ +name: "CI - Fork PRs" + +# This workflow handles pull requests from forked repositories. +# It uses pull_request_target so the reusable CI workflow can receive explicit secrets. + +permissions: + actions: read + contents: read + pull-requests: write + security-events: write +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number }} + cancel-in-progress: true + +on: + pull_request_target: + branches: + - main + +jobs: + pr_jobs: + name: "Pipeline" + # github.event.pull_request object defined here: https://docs.github.com/en/rest/pulls/pulls?apiVersion=2026-03-10#get-a-pull-request + if: ${{ github.event.pull_request.head.repo.full_name != github.repository }} + uses: ./.github/workflows/pr-ci-workflow.yml + permissions: + actions: read + contents: read + packages: read + pull-requests: write + security-events: write + with: + BRANCH_REF: ${{ github.event.pull_request.head.sha }} + secrets: inherit diff --git a/.github/workflows/prs-entrypoint-main.yml b/.github/workflows/prs-entrypoint-main.yml new file mode 100644 index 000000000..2a2917c8e --- /dev/null +++ b/.github/workflows/prs-entrypoint-main.yml @@ -0,0 +1,34 @@ +name: "CI - PRs" + +# This workflow is the entrypoint for pull requests opened from host repository. +# Fork PRs are handled by prs-entrypoint-forks.yml using pull_request_target. +# It forwards the branch SHA and explicit secrets to the reusable workflow. + +permissions: + actions: read + contents: read + pull-requests: write +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number }} + cancel-in-progress: true + +on: + pull_request: + branches: + - main + +jobs: + pr_jobs: + name: "Pipeline" + # github.event.pull_request object defined here: https://docs.github.com/en/rest/pulls/pulls?apiVersion=2026-03-10#get-a-pull-request + if: ${{ github.event.pull_request.head.repo.full_name == github.repository }} + uses: ./.github/workflows/pr-ci-workflow.yml + permissions: + actions: read + contents: read + packages: read + pull-requests: write + security-events: write + with: + BRANCH_REF: ${{ github.event.pull_request.head.sha }} + secrets: inherit diff --git a/.github/workflows/prs-entrypoint.yml b/.github/workflows/prs-entrypoint.yml deleted file mode 100644 index 7289b62e8..000000000 --- a/.github/workflows/prs-entrypoint.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: "CI" - -# This workflow is the entrypoint for pull request validation. -# It selects the correct event path for same-repo vs fork PRs, -# then forwards the branch SHA and explicit secrets to the reusable workflow. - -permissions: - actions: read - contents: read - pull-requests: write -concurrency: - group: ${{github.workflow}}-${{ github.event.pull_request.number }} - cancel-in-progress: true - -on: - # PRs from branches in this repository - pull_request: - branches: - - main - # PRs from forked repositories - pull_request_target: - branches: - - main - -jobs: - pr_jobs: - name: "Pipeline" - # Only run pull_request for same-repo PRs, and pull_request_target for fork PRs. - # This prevents duplicate workflow runs and keeps fork secret handling safe. - if: ${{ (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == 'gitlabform/gitlabform') || (github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name != 'gitlabform/gitlabform') }} - uses: ./.github/workflows/pr-ci-workflow.yml - permissions: - actions: read - contents: read - packages: read - pull-requests: write - security-events: write - with: - BRANCH_REF: ${{ github.event.pull_request.head.sha }} - secrets: inherit \ No newline at end of file From 8285f6d56d93e0640920495362e5ed1039c9af89 Mon Sep 17 00:00:00 2001 From: amimas Date: Sun, 7 Jun 2026 14:40:37 -0400 Subject: [PATCH 8/9] =?UTF-8?q?fix(ci):=20release=20workflow=20main=20veri?= =?UTF-8?q?fication=20and=20rename=20build=20artifact=E2=80=A6=20(#14)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(ci): release workflow main verification and rename build artifact job * chore: add python as prefix in job name --- .github/workflows/_releases.yml | 18 ++++++++++-------- .github/workflows/build.yml | 2 +- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/.github/workflows/_releases.yml b/.github/workflows/_releases.yml index 7c4e03e75..c2cc08b5f 100644 --- a/.github/workflows/_releases.yml +++ b/.github/workflows/_releases.yml @@ -27,9 +27,11 @@ jobs: steps: - name: Check main branch workflow completion uses: actions/github-script@v7 + env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true with: script: | - const force = github.context.payload.inputs?.force === true || github.context.payload.inputs?.force === 'true'; + const force = context.payload.inputs?.force === true || context.payload.inputs?.force === 'true'; if (force) { console.log('Force release enabled; skipping main workflow verification.'); return; @@ -52,8 +54,8 @@ jobs: throw new Error(`Main workflow did not pass. status=${mainRun.status} conclusion=${mainRun.conclusion}`); } - build-and-test-artifacts: - name: Build & Test + build-release-artifacts: + name: Build release artifacts needs: - verify-main-workflow uses: ./.github/workflows/build.yml @@ -63,7 +65,7 @@ jobs: publish-docs: name: Docs needs: - - build-and-test-artifacts + - build-release-artifacts runs-on: ubuntu-latest steps: - name: Checkout repository @@ -85,7 +87,7 @@ jobs: publish-to-github: name: Publish Release needs: - - build-and-test-artifacts + - build-release-artifacts runs-on: ubuntu-latest steps: - name: Create release in GitHub @@ -99,7 +101,7 @@ jobs: publish-to-pypi: name: Publish PyPI needs: - - build-and-test-artifacts + - build-release-artifacts runs-on: ubuntu-latest environment: name: pypi @@ -120,7 +122,7 @@ jobs: run: | uv sync --frozen --no-dev --group release # TODO: In a separate PR, consume the build artifact (package-dist) uploaded - # in build-and-test-artifacts instead of rebuilding and verifying the package here. + # in build-release-artifacts instead of rebuilding and verifying the package here. - name: Build and verify the package run: | uv run --no-sync package build @@ -132,7 +134,7 @@ jobs: publish-to-ghcr: name: Publish GHCR needs: - - build-and-test-artifacts + - build-release-artifacts - publish-to-pypi runs-on: ubuntu-latest steps: diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e146d1025..d216aafd8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -60,7 +60,7 @@ jobs: path: site smoke-tests: - name: "Smoke test - (${{ matrix.os == 'ubuntu-latest' && 'Linux' || matrix.os == 'macos-latest' && 'macOS' || 'Windows' }}, ${{ matrix.python-version }})" + name: "Smoke test - (${{ matrix.os == 'ubuntu-latest' && 'Linux' || matrix.os == 'macos-latest' && 'macOS' || 'Windows' }}, Python ${{ matrix.python-version }})" strategy: fail-fast: false matrix: From 75f9ef4fca9080d300e21c28d4c5f592bf8e609f Mon Sep 17 00:00:00 2001 From: amimas Date: Sat, 6 Jun 2026 22:41:22 -0400 Subject: [PATCH 9/9] Update link format in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8df6818f1..83c1f5e56 100644 --- a/README.md +++ b/README.md @@ -15,4 +15,4 @@ ...and more using hierarchical configuration written in YAML. -Please see the project site for more information. +Please see [the project site](https://gitlabform.github.io/gitlabform/) for more information.