From 578479b7900a0f37ee008fc799ef2a9ebaad1f57 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Tue, 19 May 2026 02:15:19 +0200 Subject: [PATCH 01/21] ci(workflows): consolidate PR checks and add /e2e command runner Refines test workflows for renovate automerge by collapsing the fragmented PR pipeline into three required jobs (lint, test, build) and gating e2e behind explicit `/e2e` PR comments. - ci-pr.yaml: single PR workflow with paths-filter-driven lint-go, lint-generated, lint-charts, lint-license, lint-reuse, test, build; checksOK aggregator treats success+skipped as pass for stable required-status semantics. - ci-e2e-info.yaml: posts a per-PR info comment listing relevant suites with `/e2e ` examples; auto-comments `/e2e all` on renovate PRs so the automerge chain runs unattended. - ci-e2e-runner.yaml: issue_comment-triggered matrix runner; supports `/e2e all`, `/e2e skip`, and explicit suite lists; posts per-suite and aggregate `e2eOK` commit statuses on the PR head SHA. - paths-filters-{ci,e2e}.yaml: split path rules into editable config files so suite scoping changes don't require workflow edits. - ci-e2e-test-nightly.yaml: bump admin cluster to v1.34.3. - Drop superseded workflows: ci-e2e-test, unit-tests, pr-docker-build, helm-lint, kustomize-lint, check-changes-crd-api, license, ci-reuse. Signed-off-by: abhijith-darshan --- .github/paths-filters-ci.yaml | 29 +++ .github/paths-filters-e2e.yaml | 55 +++++ .github/workflows/check-changes-crd-api.yaml | 50 ----- .github/workflows/ci-e2e-info.yaml | 129 ++++++++++++ .github/workflows/ci-e2e-runner.yaml | 210 +++++++++++++++++++ .github/workflows/ci-e2e-test-nightly.yaml | 2 +- .github/workflows/ci-e2e-test.yaml | 105 ---------- .github/workflows/ci-pr.yaml | 167 +++++++++++++++ .github/workflows/ci-reuse.yaml | 11 - .github/workflows/helm-lint.yaml | 43 ---- .github/workflows/kustomize-lint.yaml | 32 --- .github/workflows/license.yaml | 14 -- .github/workflows/pr-docker-build.yaml | 69 ------ .github/workflows/unit-tests.yml | 58 ----- 14 files changed, 591 insertions(+), 383 deletions(-) create mode 100644 .github/paths-filters-ci.yaml create mode 100644 .github/paths-filters-e2e.yaml delete mode 100644 .github/workflows/check-changes-crd-api.yaml create mode 100644 .github/workflows/ci-e2e-info.yaml create mode 100644 .github/workflows/ci-e2e-runner.yaml delete mode 100644 .github/workflows/ci-e2e-test.yaml create mode 100644 .github/workflows/ci-pr.yaml delete mode 100644 .github/workflows/ci-reuse.yaml delete mode 100644 .github/workflows/helm-lint.yaml delete mode 100644 .github/workflows/kustomize-lint.yaml delete mode 100644 .github/workflows/license.yaml delete mode 100644 .github/workflows/pr-docker-build.yaml delete mode 100644 .github/workflows/unit-tests.yml diff --git a/.github/paths-filters-ci.yaml b/.github/paths-filters-ci.yaml new file mode 100644 index 000000000..805cd324c --- /dev/null +++ b/.github/paths-filters-ci.yaml @@ -0,0 +1,29 @@ +# Path filters consumed by .github/workflows/ci-pr.yaml via dorny/paths-filter. +# Edit this file to change which paths trigger which lint/test/build jobs — +# no workflow YAML changes required. + +go: + - 'internal/**' + - 'cmd/**' + - 'pkg/**' + - 'api/**' + - 'go.mod' + - 'go.sum' + - '.golangci.yaml' + +api: + - 'api/**' + - 'charts/manager/crds/**' + +charts: + - 'charts/**' + +docker: + - 'Dockerfile*' + - 'Makefile' + - 'cmd/**' + - 'internal/**' + - 'pkg/**' + - 'api/**' + - 'go.mod' + - 'go.sum' diff --git a/.github/paths-filters-e2e.yaml b/.github/paths-filters-e2e.yaml new file mode 100644 index 000000000..010c54c91 --- /dev/null +++ b/.github/paths-filters-e2e.yaml @@ -0,0 +1,55 @@ +# Path filters consumed by .github/workflows/ci-e2e-info.yaml via dorny/paths-filter. +# Each top-level key (other than `shared`) is an e2e suite name. The info workflow +# uses these to suggest `/e2e ` comments on a PR. +# Edit this file to change suite scoping — no workflow YAML changes required. + +# Shared changes affect every suite — info workflow recommends `/e2e all`. +shared: + - 'go.mod' + - 'go.sum' + - 'Dockerfile*' + - 'cmd/greenhouse/**' + - 'internal/helm/**' + - 'internal/flux/**' + - 'internal/clientutil/**' + - 'charts/**' + - 'e2e/shared/**' + +organization: + - 'internal/controller/organization/**' + - 'api/v1alpha1/organization*.go' + - 'e2e/organization/**' + +cluster: + - 'internal/controller/cluster/**' + - 'api/v1alpha1/cluster*.go' + - 'api/v1alpha2/cluster*.go' + - 'e2e/cluster/**' + +catalog: + - 'internal/controller/catalog/**' + - 'internal/flux/**' + - 'api/v1alpha1/catalog*.go' + - 'e2e/catalog/**' + +plugin: + - 'internal/controller/plugin/**' + - 'internal/controller/pluginpreset/**' + - 'internal/ocimirror/**' + - 'api/v1alpha1/plugin*.go' + - 'e2e/plugin/**' + +plugindefinition: + - 'internal/controller/plugindefinition/**' + - 'api/v1alpha1/plugindefinition*.go' + - 'e2e/plugindefinition/**' + +clusterplugindefinition: + - 'internal/controller/clusterplugindefinition/**' + - 'api/v1alpha1/clusterplugindefinition*.go' + - 'e2e/clusterplugindefinition/**' + +teamrolebinding: + - 'internal/controller/teamrolebinding/**' + - 'api/v1alpha1/teamrole*.go' + - 'e2e/teamrolebinding/**' \ No newline at end of file diff --git a/.github/workflows/check-changes-crd-api.yaml b/.github/workflows/check-changes-crd-api.yaml deleted file mode 100644 index 4f3d97bcd..000000000 --- a/.github/workflows/check-changes-crd-api.yaml +++ /dev/null @@ -1,50 +0,0 @@ -# Run it locally with act (https://github.com/nektos/act) -# 1. Install act: -# `brew install act` -# 2. Create a .secret file with the following content: -# `GITHUB_TOKEN=your_github_token` -# PULL REQUEST -# 1. Create a act_pull_request.json file in case of a pull request with the following content: -# `{"pull_request": {"number": , "head": {"ref": "", "sha": "PR commit sha"}, "base": {"ref": "main"}}, "repository": {"name": "juno", "owner": {"login": "cloudoperators"}}}` -# 2. Run the following command: -# `act pull_request -j run-pipeline -e act_pull_request.json -W .github/workflows/ci-npm-packages.yaml` - -name: Detect CRD API Changes and Generate Docs, Types and Specs - -on: - pull_request: - types: [opened, synchronize, reopened] - paths: - - "api/meta/**" - - "api/v1alpha1/**" - - "api/v1alpha2/**" -jobs: - generate-docs-types-specs: - runs-on: [ ubuntu-latest ] - steps: - - name: Generate GitHub App Token - id: github-app-token - uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 - with: - app-id: ${{ secrets.CLOUDOPERATOR_APP_ID }} - private-key: ${{ secrets.CLOUDOPERATOR_APP_PRIVATE_KEY }} - permission-contents: write - - - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - repository: ${{ github.event.pull_request.head.repo.full_name }} - ref: ${{ github.event.pull_request.head.ref }} - token: ${{ steps.github-app-token.outputs.token }} - - - name: Generate manifests, documentation and types - run: | - chmod o+rw docs/reference/api/openapi.yaml - make generate-all - - - name: Apply Changes - uses: EndBug/add-and-commit@290ea2c423ad77ca9c62ae0f5b224379612c0321 # v10.0.0 - with: - author_name: cloud-operator-bot[bot] - author_email: 224791424+cloud-operator-bot[bot]@users.noreply.github.com - message: "Automatic generation of CRD API Docs" diff --git a/.github/workflows/ci-e2e-info.yaml b/.github/workflows/ci-e2e-info.yaml new file mode 100644 index 000000000..62fdfd86e --- /dev/null +++ b/.github/workflows/ci-e2e-info.yaml @@ -0,0 +1,129 @@ +name: CI E2E Info + +on: + pull_request: + types: [opened, synchronize, reopened] + +permissions: + contents: read + pull-requests: write + +jobs: + detect: + name: Detect relevant suites + runs-on: [ubuntu-latest] + outputs: + changes: ${{ steps.filter.outputs.changes }} + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Filter suites by changed paths + id: filter + uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 + with: + filters: .github/paths-filters-e2e.yaml + + comment: + name: Post info comment + needs: [detect] + if: github.event.pull_request.user.login != 'renovate[bot]' + runs-on: [ubuntu-latest] + steps: + - name: Compose and post comment + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v8.0.0 + env: + CHANGES: ${{ needs.detect.outputs.changes }} + with: + script: | + const marker = ''; + const allSuites = [ + 'organization', 'cluster', 'catalog', 'plugin', + 'plugindefinition', 'clusterplugindefinition', + 'teamrolebinding' + ]; + const matched = JSON.parse(process.env.CHANGES || '[]'); + const sharedHit = matched.includes('shared'); + const suites = matched.filter(s => s !== 'shared'); + + const codeBlock = (s) => '```\n' + s + '\n```'; + const detailsForSuite = (s) => + `
\n/e2e ${s}\n\n` + + `Runs the \`${s}\` e2e suite against the PR head.\n\n` + + `Comment on this PR:\n\n${codeBlock('/e2e ' + s)}\n` + + `
\n`; + + let body = `${marker}\n## E2E\n\n`; + + if (sharedHit) { + body += `Shared code changed — the full suite is recommended. Comment on this PR:\n\n`; + body += codeBlock('/e2e all') + '\n\n'; + body += `
\nRun individual suites\n\n`; + body += allSuites.map(detailsForSuite).join('\n'); + body += `
\n`; + } else if (suites.length === 0) { + body += `No e2e-relevant changes detected. To satisfy \`e2eOK\` without running any suite, comment:\n\n`; + body += codeBlock('/e2e skip') + '\n\n'; + body += `
\nRun a suite anyway\n\n`; + body += allSuites.map(detailsForSuite).join('\n'); + body += `\n${codeBlock('/e2e all')}\n`; + body += `
\n`; + } else { + body += `Relevant suites detected: ${suites.map(s => `\`${s}\``).join(', ')}.\n\n`; + body += `Comment on this PR:\n\n`; + body += codeBlock('/e2e ' + suites.join(' ')) + '\n\n'; + body += `_or run the full set:_\n\n${codeBlock('/e2e all')}\n\n`; + body += `
\nRun individual suites\n\n`; + body += suites.map(detailsForSuite).join('\n'); + body += `
\n`; + } + + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + }); + const existing = comments.find(c => c.body && c.body.startsWith(marker)); + if (existing) { + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: existing.id, + body, + }); + } else { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body, + }); + } + + renovate-autorun: + name: Auto-trigger /e2e all for renovate + needs: [detect] + if: github.event.pull_request.user.login == 'renovate[bot]' + runs-on: [ubuntu-latest] + steps: + - name: Post /e2e all comment + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v8.0.0 + with: + script: | + const marker = ''; + const body = `${marker}\n/e2e all`; + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + }); + const existing = comments.find(c => c.body && c.body.startsWith(marker)); + if (existing) { + core.info('Renovate autorun comment already posted; skipping.'); + return; + } + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body, + }); \ No newline at end of file diff --git a/.github/workflows/ci-e2e-runner.yaml b/.github/workflows/ci-e2e-runner.yaml new file mode 100644 index 000000000..5e9dd4a94 --- /dev/null +++ b/.github/workflows/ci-e2e-runner.yaml @@ -0,0 +1,210 @@ +name: CI E2E Runner + +on: + issue_comment: + types: [created] + +permissions: + contents: read + pull-requests: read + statuses: write + +jobs: + parse: + name: Parse /e2e command + if: ${{ github.event.issue.pull_request && startsWith(github.event.comment.body, '/e2e') }} + runs-on: [ubuntu-latest] + outputs: + sha: ${{ steps.pr.outputs.sha }} + suites: ${{ steps.cmd.outputs.suites }} + mode: ${{ steps.cmd.outputs.mode }} + steps: + - name: Resolve PR head SHA + id: pr + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v8.0.0 + with: + script: | + const { owner, repo, number } = context.issue; + const pr = await github.rest.pulls.get({ owner, repo, pull_number: number }); + core.setOutput('sha', pr.data.head.sha); + + - name: Parse comment body + id: cmd + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v8.0.0 + env: + BODY: ${{ github.event.comment.body }} + with: + script: | + const allSuites = [ + 'organization', 'cluster', 'catalog', 'plugin', + 'plugindefinition', 'clusterplugindefinition', + 'teamrolebinding' + ]; + const body = (process.env.BODY || '').trim(); + const firstLine = body.split('\n')[0].trim(); + const tokens = firstLine.split(/\s+/); + if (tokens[0] !== '/e2e') { + core.setFailed(`unexpected command: ${firstLine}`); + return; + } + const args = tokens.slice(1).filter(Boolean); + if (args.length === 0) { + core.setFailed('no suites provided. Use `/e2e all`, `/e2e skip`, or `/e2e [...]`.'); + return; + } + if (args.includes('skip')) { + if (args.length !== 1) { + core.setFailed('`/e2e skip` cannot be combined with other suites.'); + return; + } + core.setOutput('mode', 'skip'); + core.setOutput('suites', '[]'); + return; + } + let suites; + if (args.includes('all')) { + if (args.length !== 1) { + core.setFailed('`/e2e all` cannot be combined with other suites.'); + return; + } + suites = allSuites; + } else { + const unknown = args.filter(a => !allSuites.includes(a)); + if (unknown.length > 0) { + core.setFailed(`unknown suite(s): ${unknown.join(', ')}. Available: ${allSuites.join(', ')}, all, skip.`); + return; + } + suites = [...new Set(args)]; + } + core.setOutput('mode', 'run'); + core.setOutput('suites', JSON.stringify(suites)); + + - name: Acknowledge command + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v8.0.0 + with: + script: | + await github.rest.reactions.createForIssueComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: context.payload.comment.id, + content: 'rocket', + }); + + e2e: + name: E2E ${{ matrix.suite }} + needs: [parse] + if: needs.parse.outputs.mode == 'run' + runs-on: [ubuntu-latest] + strategy: + fail-fast: false + matrix: + suite: ${{ fromJson(needs.parse.outputs.suites) }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - name: Set status pending (${{ matrix.suite }}) + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v8.0.0 + with: + script: | + await github.rest.repos.createCommitStatus({ + owner: context.repo.owner, + repo: context.repo.repo, + sha: '${{ needs.parse.outputs.sha }}', + state: 'pending', + context: 'e2e / ${{ matrix.suite }}', + target_url: `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`, + }); + + - name: Checkout PR head + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + ref: ${{ needs.parse.outputs.sha }} + + - name: Run ${{ matrix.suite }} e2e + id: e2e + continue-on-error: true + uses: cloudoperators/common/workflows/e2e@17d3d6b80851574fcd8c7b6e5c4fe6aee2922359 + env: + RUNTIME_ENV: "CI" + GH_APP_ID: ${{ secrets.CLOUDOPERATOR_APP_ID }} + GH_APP_PRIVATE_KEY: ${{ secrets.CLOUDOPERATOR_APP_PRIVATE_KEY }} + GH_APP_INSTALLATION_ID: ${{ secrets.CLOUDOPERATOR_APP_INSTALLATION_ID }} + with: + admin-k8s-version: "v1.34.3" + remote-k8s-version: "v1.32.11" + scenario: ${{ matrix.suite }} + admin-config: ${{ github.workspace }}/e2e/greenhouse-admin-cluster.yaml + remote-config: ${{ github.workspace }}/dev-env/greenhouse-remote-cluster.yaml + + - name: Show e2e logs + if: always() + continue-on-error: true + env: + SCENARIO: ${{ matrix.suite }} + E2E_RESULT_DIR: ${{ github.workspace }}/bin + run: make show-e2e-logs + + - name: Set status final (${{ matrix.suite }}) + if: always() + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v8.0.0 + env: + OUTCOME: ${{ steps.e2e.outcome }} + with: + script: | + const ok = process.env.OUTCOME === 'success'; + await github.rest.repos.createCommitStatus({ + owner: context.repo.owner, + repo: context.repo.repo, + sha: '${{ needs.parse.outputs.sha }}', + state: ok ? 'success' : 'failure', + context: 'e2e / ${{ matrix.suite }}', + target_url: `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`, + }); + + - name: Fail step on e2e failure + if: steps.e2e.outcome == 'failure' + run: exit 1 + + e2eOK: + name: e2eOK + needs: [parse, e2e] + if: always() && needs.parse.result == 'success' + runs-on: [ubuntu-latest] + steps: + - name: Compute aggregate state + id: agg + env: + MODE: ${{ needs.parse.outputs.mode }} + E2E_RESULT: ${{ needs.e2e.result }} + run: | + if [ "$MODE" = "skip" ]; then + echo "state=success" >> "$GITHUB_OUTPUT" + echo "description=skipped via /e2e skip" >> "$GITHUB_OUTPUT" + exit 0 + fi + case "$E2E_RESULT" in + success) echo "state=success" >> "$GITHUB_OUTPUT"; echo "description=all suites passed" >> "$GITHUB_OUTPUT" ;; + skipped) echo "state=success" >> "$GITHUB_OUTPUT"; echo "description=no suites scheduled" >> "$GITHUB_OUTPUT" ;; + *) echo "state=failure" >> "$GITHUB_OUTPUT"; echo "description=one or more suites failed" >> "$GITHUB_OUTPUT" ;; + esac + + - name: Post e2eOK commit status + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v8.0.0 + env: + STATE: ${{ steps.agg.outputs.state }} + DESCRIPTION: ${{ steps.agg.outputs.description }} + with: + script: | + await github.rest.repos.createCommitStatus({ + owner: context.repo.owner, + repo: context.repo.repo, + sha: '${{ needs.parse.outputs.sha }}', + state: process.env.STATE, + context: 'e2eOK', + description: process.env.DESCRIPTION, + target_url: `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`, + }); + + - name: Fail job on aggregate failure + if: steps.agg.outputs.state != 'success' + run: exit 1 \ No newline at end of file diff --git a/.github/workflows/ci-e2e-test-nightly.yaml b/.github/workflows/ci-e2e-test-nightly.yaml index 2bd0ee1ee..09f3f401d 100644 --- a/.github/workflows/ci-e2e-test-nightly.yaml +++ b/.github/workflows/ci-e2e-test-nightly.yaml @@ -46,7 +46,7 @@ jobs: GH_APP_PRIVATE_KEY: ${{ secrets.CLOUDOPERATOR_APP_PRIVATE_KEY }} GH_APP_INSTALLATION_ID: ${{ secrets.CLOUDOPERATOR_APP_INSTALLATION_ID }} with: - admin-k8s-version: "v1.33.7" + admin-k8s-version: "v1.34.3" remote-k8s-version: ${{ matrix.k8s-version }} scenario: ${{ matrix.e2es }} admin-config: ${{ github.workspace }}/e2e/greenhouse-admin-cluster.yaml diff --git a/.github/workflows/ci-e2e-test.yaml b/.github/workflows/ci-e2e-test.yaml deleted file mode 100644 index 339065398..000000000 --- a/.github/workflows/ci-e2e-test.yaml +++ /dev/null @@ -1,105 +0,0 @@ -name: E2E Workflow -on: - workflow_dispatch: - pull_request: - paths: - - 'internal/**' - - 'pkg/**' - - 'e2e/**' - - 'cmd/**' - - 'Dockerfile*' - - 'go.mod' - - 'go.sum' - -jobs: - - init: - outputs: - tests: ${{ steps.e2es.outputs.result }} - runs-on: [ ubuntu-latest ] - name: "Prepare E2E Scenarios" - steps: - - name: "Checkout" - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - # find all e2e scenarios in the e2e directory and generate an array of scenario names - - name: "E2E Detection" - id: e2es - run: | - scenarios=$(find ${GITHUB_WORKSPACE}/e2e -type f -name 'e2e_test.go' -not -path "*/authz/*" -exec dirname {} \; | xargs -n 1 basename | jq -R -s -c 'split("\n")[:-1]') - echo $scenarios - echo "result=$scenarios" >> $GITHUB_OUTPUT - - e2e: - name: "Run ${{ matrix.e2es }}(${{ matrix.k8s-version}}) E2E" - if: needs.init.outputs.tests != '[]' - needs: [ init ] - runs-on: [ ubuntu-latest ] - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - strategy: - fail-fast: false - matrix: - k8s-version: [ "v1.32.11" ] - e2es: ${{fromJson(needs.init.outputs.tests)}} - steps: - # run the e2e tests using composite common/workflows/e2e action - - name: "E2E" - id: e2e - continue-on-error: true - uses: cloudoperators/common/workflows/e2e@f87e1e6b7889707a9c10d1a8a9f9f481eb311c06 # main - env: - RUNTIME_ENV: "CI" - GH_APP_ID: ${{ secrets.CLOUDOPERATOR_APP_ID }} - GH_APP_PRIVATE_KEY: ${{ secrets.CLOUDOPERATOR_APP_PRIVATE_KEY }} - GH_APP_INSTALLATION_ID: ${{ secrets.CLOUDOPERATOR_APP_INSTALLATION_ID }} - with: - admin-k8s-version: "v1.33.7" - remote-k8s-version: ${{ matrix.k8s-version }} - scenario: ${{ matrix.e2es }} - admin-config: ${{ github.workspace }}/e2e/greenhouse-admin-cluster.yaml - remote-config: ${{ github.workspace }}/dev-env/greenhouse-remote-cluster.yaml - - - name: "Display Logs" - continue-on-error: true - env: - SCENARIO: ${{ matrix.e2es }} - E2E_RESULT_DIR: ${{github.workspace}}/bin - run: | - make show-e2e-logs - - - name: "E2E Failure?" - if: ${{ steps.e2e.outcome == 'failure' }} - run: | - exit 1 - - lint: - runs-on: [ ubuntu-latest ] - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up Go - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 - with: - go-version-file: 'go.mod' - token: ${{ secrets.GITHUB_TOKEN }} - - name: golangci-lint - run: make lint - - # check if the e2e tests passed for all scenarios - checksOK: - name: "E2E Check" - needs: [ e2e ] - runs-on: [ ubuntu-latest ] - if: always() - steps: - - name: "Check if e2e passed" - run: | - if [ "${{ needs.e2e.result }}" == "success" ]; then - echo "✅ E2E passed 🎉" - else - echo "❌ E2E failed 😭" - exit 1 - fi \ No newline at end of file diff --git a/.github/workflows/ci-pr.yaml b/.github/workflows/ci-pr.yaml new file mode 100644 index 000000000..edae32471 --- /dev/null +++ b/.github/workflows/ci-pr.yaml @@ -0,0 +1,167 @@ +name: CI PR + +on: + pull_request: + types: [opened, synchronize, reopened] + +permissions: + contents: read + statuses: write + +jobs: + changes: + name: Detect changes + runs-on: [ubuntu-latest] + outputs: + go: ${{ steps.filter.outputs.go }} + api: ${{ steps.filter.outputs.api }} + charts: ${{ steps.filter.outputs.charts }} + docker: ${{ steps.filter.outputs.docker }} + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Filter changed paths + id: filter + uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 + with: + filters: .github/paths-filters-ci.yaml + + lint-go: + name: Lint (go) + needs: [changes] + runs-on: [ubuntu-latest] + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Set up Go + if: needs.changes.outputs.go == 'true' + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + with: + go-version-file: 'go.mod' + + - name: golangci-lint + if: needs.changes.outputs.go == 'true' + run: make lint + + lint-generated: + name: Lint (generated) + needs: [changes] + runs-on: [ubuntu-latest] + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Set up Go + if: needs.changes.outputs.api == 'true' + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + with: + go-version-file: 'go.mod' + + - name: Check generated files are up to date + if: needs.changes.outputs.api == 'true' + run: | + make generate-all + if ! git diff --quiet; then + echo "::error title=Generated files out of date::Run 'make generate-all' locally and commit the result before pushing again." + git --no-pager diff + exit 1 + fi + + lint-charts: + name: Lint (charts) + needs: [changes] + runs-on: [ubuntu-latest] + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + + - name: Set up Helm + if: needs.changes.outputs.charts == 'true' + uses: azure/setup-helm@dda3372f752e03dde6b3237bc9431cdc2f7a02a2 # v5.0.0 + with: + version: "v3.19.2" + + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + if: needs.changes.outputs.charts == 'true' + with: + python-version: 3.14 + check-latest: true + + - name: Set up chart-testing + if: needs.changes.outputs.charts == 'true' + uses: helm/chart-testing-action@6ec842c01de15ebb84c8627d2744a0c2f2755c9f # v2.8.0 + + - name: Run chart-testing (lint) + if: needs.changes.outputs.charts == 'true' + run: | + changed=$(ct list-changed --config ct.yaml --target-branch ${{ github.event.repository.default_branch }}) + if [[ -n "$changed" ]]; then + ct lint --config ct.yaml --target-branch ${{ github.event.repository.default_branch }} + fi + + lint-license: + name: Lint (license) + uses: cloudoperators/common/.github/workflows/shared-license.yaml@17d3d6b80851574fcd8c7b6e5c4fe6aee2922359 + with: + apply_header: false + + lint-reuse: + name: Lint (reuse) + uses: cloudoperators/common/.github/workflows/shared-reuse.yaml@17d3d6b80851574fcd8c7b6e5c4fe6aee2922359 + + test: + name: Test + needs: [changes] + runs-on: [ubuntu-latest] + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Set up Go + if: needs.changes.outputs.go == 'true' + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + with: + go-version-file: 'go.mod' + + - name: Unit tests + if: needs.changes.outputs.go == 'true' + run: make test + + build: + name: Build + needs: [changes] + runs-on: [ubuntu-latest] + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Build Docker image + if: needs.changes.outputs.docker == 'true' + run: make docker-build + + checksOK: + name: checksOK + needs: [lint-go, lint-generated, lint-charts, lint-license, lint-reuse, test, build] + if: always() + runs-on: [ubuntu-latest] + steps: + - name: PR checks + run: | + fail=0 + for r in \ + "${{ needs.lint-go.result }}" \ + "${{ needs.lint-generated.result }}" \ + "${{ needs.lint-charts.result }}" \ + "${{ needs.lint-license.result }}" \ + "${{ needs.lint-reuse.result }}" \ + "${{ needs.test.result }}" \ + "${{ needs.build.result }}"; do + if [ "$r" != "success" ] && [ "$r" != "skipped" ]; then + fail=1 + fi + done + if [ "$fail" -eq 1 ]; then + echo "::error ::One or more checks failed (lint-go=${{ needs.lint-go.result }}, lint-generated=${{ needs.lint-generated.result }}, lint-charts=${{ needs.lint-charts.result }}, lint-license=${{ needs.lint-license.result }}, lint-reuse=${{ needs.lint-reuse.result }}, test=${{ needs.test.result }}, build=${{ needs.build.result }})" + exit 1 + fi + echo "All checks passed" diff --git a/.github/workflows/ci-reuse.yaml b/.github/workflows/ci-reuse.yaml deleted file mode 100644 index a3e8102cb..000000000 --- a/.github/workflows/ci-reuse.yaml +++ /dev/null @@ -1,11 +0,0 @@ -# SPDX-FileCopyrightText: 2022 Free Software Foundation Europe e.V. -# -# SPDX-License-Identifier: CC0-1.0 - -name: REUSE Compliance Check - -on: [pull_request] - -jobs: - reuse: - uses: cloudoperators/common/.github/workflows/shared-reuse.yaml@f87e1e6b7889707a9c10d1a8a9f9f481eb311c06 # main diff --git a/.github/workflows/helm-lint.yaml b/.github/workflows/helm-lint.yaml deleted file mode 100644 index b5f2f0bf8..000000000 --- a/.github/workflows/helm-lint.yaml +++ /dev/null @@ -1,43 +0,0 @@ -name: "Helm lint" -on: - pull_request: - paths: - - 'charts/**' - -env: - REGISTRY: ghcr.io - -jobs: - helm-lint: - runs-on: [ ubuntu-latest ] - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - fetch-depth: 0 - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up Helm - uses: azure/setup-helm@dda3372f752e03dde6b3237bc9431cdc2f7a02a2 # v5.0.0 - with: - version: "v3.19.2" - - - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 - with: - python-version: 3.14 - check-latest: true - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up chart-testing - uses: helm/chart-testing-action@6ec842c01de15ebb84c8627d2744a0c2f2755c9f # v2.8.0 - - - name: Run chart-testing (list-changed) - id: list-changed - run: | - changed=$(ct list-changed --config ct.yaml --target-branch ${{ github.event.repository.default_branch }}) - if [[ -n "$changed" ]]; then - echo "changed=true" >> "$GITHUB_OUTPUT" - fi - - - name: Run chart-testing (lint) - if: steps.list-changed.outputs.changed == 'true' - run: ct lint --config ct.yaml --target-branch ${{ github.event.repository.default_branch }} diff --git a/.github/workflows/kustomize-lint.yaml b/.github/workflows/kustomize-lint.yaml deleted file mode 100644 index 1a0c8f512..000000000 --- a/.github/workflows/kustomize-lint.yaml +++ /dev/null @@ -1,32 +0,0 @@ -name: 'Kustomize lint' -on: - pull_request: - paths: - - 'charts/**' - -jobs: - kustomize: - runs-on: [ ubuntu-latest ] - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up Go - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 - with: - go-version-file: 'go.mod' - token: ${{ secrets.GITHUB_TOKEN }} - - name: Setup Controller-gen - run: make controller-gen - - name: Setup Kustomize - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - curl --request GET \ - --url "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" \ - --header "Authorization: Bearer $GH_TOKEN" - - name: Setup API documentation generator - run: docker pull ghcr.io/srfrnk/crd-api-doc-gen:latest - - name: lint - run: make kustomize-build-crds diff --git a/.github/workflows/license.yaml b/.github/workflows/license.yaml deleted file mode 100644 index ef977a9fe..000000000 --- a/.github/workflows/license.yaml +++ /dev/null @@ -1,14 +0,0 @@ -name: Check & Fix License Header -on: - pull_request: - types: [opened, synchronize, reopened] - -jobs: - license: - name: Apply Licence Header - permissions: - contents: write # Only used when `apply_header: true` else the permission is `read` see: https://github.com/cloudoperators/common/blob/8f15c13b6f4c1631c7e6f6dff5c3300452e9b5b6/.github/workflows/shared-license.yaml#L21-L22 - uses: cloudoperators/common/.github/workflows/shared-license.yaml@17d3d6b80851574fcd8c7b6e5c4fe6aee2922359 - secrets: - app_id: ${{ secrets.CLOUDOPERATOR_APP_ID }} - private_key: ${{ secrets.CLOUDOPERATOR_APP_PRIVATE_KEY }} diff --git a/.github/workflows/pr-docker-build.yaml b/.github/workflows/pr-docker-build.yaml deleted file mode 100644 index 289b69334..000000000 --- a/.github/workflows/pr-docker-build.yaml +++ /dev/null @@ -1,69 +0,0 @@ -name: Test build Docker images on PR - -on: - pull_request: - types: [ opened, synchronize, reopened ] - paths: - - Makefile - - Dockerfile - -env: - REGISTRY: ghcr.io - -permissions: - contents: read - packages: read - id-token: write - -jobs: - build: - name: Build - runs-on: [ ubuntu-latest ] - permissions: - contents: read - packages: write - # This is used to complete the identity challenge - # with sigstore/fulcio when running outside of PRs. - id-token: write - steps: - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - - name: Set up QEMU - uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 - with: - driver-opts: | - image=moby/buildkit:latest - - - name: Extract Docker metadata - id: meta - uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0 - with: - images: ${{ env.REGISTRY }}/${{ github.repository_owner }}/greenhouse - tags: | - type=semver,pattern={{version}},prefix=v - type=semver,pattern={{major}}.{{minor}},prefix=v - type=semver,pattern={{major}},enable=${{ !startsWith(github.ref, 'refs/tags/v0.') }},prefix=v - type=ref,event=branch - type=ref,event=tag - type=ref,event=pr - type=schedule - type=raw,value=${{ github.sha }} - type=sha,enable=true,format=short,prefix= - type=edge,branch=master - - - name: Build Docker image - id: build-and-push - uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0 - with: - context: . - push: false - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - provenance: false - platforms: | - linux/amd64 - linux/arm64 diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml deleted file mode 100644 index 49d3e29cc..000000000 --- a/.github/workflows/unit-tests.yml +++ /dev/null @@ -1,58 +0,0 @@ -name: "Unit tests" -on: - pull_request: - paths: - - 'internal/**' - - 'cmd/**' - - 'Dockerfile*' - - 'go.mod' - - 'go.sum' - - '.golangci.yaml' - -jobs: - lint: - runs-on: [ ubuntu-latest ] - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up Go - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 - with: - go-version-file: 'go.mod' - token: ${{ secrets.GITHUB_TOKEN }} - - name: golangci-lint - run: make lint - - test: - runs-on: [ ubuntu-latest ] - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up Go - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 - with: - go-version-file: 'go.mod' - token: ${{ secrets.GITHUB_TOKEN }} - - name: test - run: make test - - build: - runs-on: [ ubuntu-latest ] - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up Go - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 - with: - go-version-file: 'go.mod' - token: ${{ secrets.GITHUB_TOKEN }} - - name: build - run: make build From 8b49602759be406be64555af1ff635205c51f8fd Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Tue, 19 May 2026 02:28:09 +0200 Subject: [PATCH 02/21] ci(workflows): auto-skip when no e2e suites match and trim info comment - ci-e2e-info.yaml: auto-comment `/e2e skip` when paths-filter detects no relevant suites; auto-comment `/e2e all` (or `/e2e skip`) on renovate PRs so the automerge chain runs without human input - info comment: drop the "no matches" branch (handled by auto-skip), drop hardcoded suite list, flatten nested
into a single collapsible with copyable `/e2e ` blocks per matched suite Signed-off-by: abhijith-darshan --- .github/workflows/ci-e2e-info.yaml | 98 ++++++++++++++++++------------ 1 file changed, 60 insertions(+), 38 deletions(-) diff --git a/.github/workflows/ci-e2e-info.yaml b/.github/workflows/ci-e2e-info.yaml index 62fdfd86e..acdd28be1 100644 --- a/.github/workflows/ci-e2e-info.yaml +++ b/.github/workflows/ci-e2e-info.yaml @@ -14,19 +14,32 @@ jobs: runs-on: [ubuntu-latest] outputs: changes: ${{ steps.filter.outputs.changes }} + has-suites: ${{ steps.classify.outputs.has-suites }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - name: Filter suites by changed paths + - name: Filter e2e suites id: filter uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 with: filters: .github/paths-filters-e2e.yaml - comment: - name: Post info comment + - name: Check e2e suites + id: classify + env: + CHANGES: ${{ steps.filter.outputs.changes }} + run: | + # has-suites = true if the changes array is non-empty + if [ "${CHANGES:-[]}" = "[]" ]; then + echo "has-suites=false" >> "$GITHUB_OUTPUT" + else + echo "has-suites=true" >> "$GITHUB_OUTPUT" + fi + + info-comment: + name: Info comment needs: [detect] - if: github.event.pull_request.user.login != 'renovate[bot]' + if: needs.detect.outputs.has-suites == 'true' && github.event.pull_request.user.login != 'renovate[bot]' runs-on: [ubuntu-latest] steps: - name: Compose and post comment @@ -36,45 +49,26 @@ jobs: with: script: | const marker = ''; - const allSuites = [ - 'organization', 'cluster', 'catalog', 'plugin', - 'plugindefinition', 'clusterplugindefinition', - 'teamrolebinding' - ]; const matched = JSON.parse(process.env.CHANGES || '[]'); const sharedHit = matched.includes('shared'); const suites = matched.filter(s => s !== 'shared'); const codeBlock = (s) => '```\n' + s + '\n```'; - const detailsForSuite = (s) => - `
\n/e2e ${s}\n\n` + - `Runs the \`${s}\` e2e suite against the PR head.\n\n` + - `Comment on this PR:\n\n${codeBlock('/e2e ' + s)}\n` + - `
\n`; + const suiteBlock = (list) => + `
\nRun individual suites\n\n` + + [...list, 'all'].map(s => codeBlock('/e2e ' + s)).join('\n\n') + + `\n
\n`; let body = `${marker}\n## E2E\n\n`; if (sharedHit) { body += `Shared code changed — the full suite is recommended. Comment on this PR:\n\n`; - body += codeBlock('/e2e all') + '\n\n'; - body += `
\nRun individual suites\n\n`; - body += allSuites.map(detailsForSuite).join('\n'); - body += `
\n`; - } else if (suites.length === 0) { - body += `No e2e-relevant changes detected. To satisfy \`e2eOK\` without running any suite, comment:\n\n`; - body += codeBlock('/e2e skip') + '\n\n'; - body += `
\nRun a suite anyway\n\n`; - body += allSuites.map(detailsForSuite).join('\n'); - body += `\n${codeBlock('/e2e all')}\n`; - body += `
\n`; + body += codeBlock('/e2e all') + '\n'; } else { body += `Relevant suites detected: ${suites.map(s => `\`${s}\``).join(', ')}.\n\n`; body += `Comment on this PR:\n\n`; body += codeBlock('/e2e ' + suites.join(' ')) + '\n\n'; - body += `_or run the full set:_\n\n${codeBlock('/e2e all')}\n\n`; - body += `
\nRun individual suites\n\n`; - body += suites.map(detailsForSuite).join('\n'); - body += `
\n`; + body += suiteBlock(suites); } const { data: comments } = await github.rest.issues.listComments({ @@ -99,31 +93,59 @@ jobs: }); } - renovate-autorun: - name: Auto-trigger /e2e all for renovate + auto-skip-e2e: + name: Auto-skip e2e + needs: [detect] + if: needs.detect.outputs.has-suites == 'false' && github.event.pull_request.user.login != 'renovate[bot]' + runs-on: [ubuntu-latest] + steps: + - name: Post /e2e skip comment + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v8.0.0 + with: + script: | + const marker = ''; + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + }); + if (comments.find(c => c.body && c.body.startsWith(marker))) { + core.info('Auto-skip comment already posted; nothing to do.'); + return; + } + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body: `${marker}\n/e2e skip`, + }); + + auto-run-e2e-renovate: + name: Auto-run e2e for renovate needs: [detect] if: github.event.pull_request.user.login == 'renovate[bot]' runs-on: [ubuntu-latest] steps: - - name: Post /e2e all comment + - name: Post /e2e command comment uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v8.0.0 + env: + HAS_SUITES: ${{ needs.detect.outputs.has-suites }} with: script: | const marker = ''; - const body = `${marker}\n/e2e all`; const { data: comments } = await github.rest.issues.listComments({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, }); - const existing = comments.find(c => c.body && c.body.startsWith(marker)); - if (existing) { - core.info('Renovate autorun comment already posted; skipping.'); + if (comments.find(c => c.body && c.body.startsWith(marker))) { + core.info('Renovate autorun comment already posted; nothing to do.'); return; } + const cmd = process.env.HAS_SUITES === 'true' ? '/e2e all' : '/e2e skip'; await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, - body, - }); \ No newline at end of file + body: `${marker}\n${cmd}`, + }); From 668dbe7aae3cd4719556a961450930d7598c6222 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Tue, 19 May 2026 02:35:12 +0200 Subject: [PATCH 03/21] (chore): test e2e on code change Signed-off-by: abhijith-darshan --- internal/controller/plugin/oci_mirror.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/controller/plugin/oci_mirror.go b/internal/controller/plugin/oci_mirror.go index be82eede9..1a3627169 100644 --- a/internal/controller/plugin/oci_mirror.go +++ b/internal/controller/plugin/oci_mirror.go @@ -132,7 +132,7 @@ func buildPostRenderer(mirror *ocimirror.ImageMirror, manifestSets ...string) *h } // ensureImageReplication pre-replicates container images referenced in renderedManifests. -// On failure it flags HelmReleaseCreatedCondition with ImageReplicationFailedReason and keeps +// On failure, it flags HelmReleaseCreatedCondition with ImageReplicationFailedReason and keeps // the previous status.ImageReplication list intact, so transient errors dont wipe history. func ensureImageReplication(ctx context.Context, mirror *ocimirror.ImageMirror, plugin *greenhousev1alpha1.Plugin, manifestSets ...string) error { replicated, err := mirror.ReplicateOCIArtifacts(ctx, plugin.Status.ImageReplication, manifestSets...) From 7a4051e15da9d64fe8a8a0ef57838c39975934ee Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Tue, 19 May 2026 02:45:12 +0200 Subject: [PATCH 04/21] ci(workflows): grant contents:write to lint-license and tighten info comment - ci-pr.yaml: lint-license now grants contents:write at job level so the called shared workflow's apply-license-header job (gated by apply_header=false at runtime) passes static permission validation - ci-e2e-info.yaml: flatten the info comment into a header line, one collapsible per matched suite, and a single worked example; drop the redundant nested details block Signed-off-by: abhijith-darshan --- .github/workflows/ci-e2e-info.yaml | 17 +++++++---------- .github/workflows/ci-pr.yaml | 2 ++ 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci-e2e-info.yaml b/.github/workflows/ci-e2e-info.yaml index acdd28be1..dce4e44fc 100644 --- a/.github/workflows/ci-e2e-info.yaml +++ b/.github/workflows/ci-e2e-info.yaml @@ -54,21 +54,18 @@ jobs: const suites = matched.filter(s => s !== 'shared'); const codeBlock = (s) => '```\n' + s + '\n```'; - const suiteBlock = (list) => - `
\nRun individual suites\n\n` + - [...list, 'all'].map(s => codeBlock('/e2e ' + s)).join('\n\n') + - `\n
\n`; let body = `${marker}\n## E2E\n\n`; if (sharedHit) { - body += `Shared code changed — the full suite is recommended. Comment on this PR:\n\n`; - body += codeBlock('/e2e all') + '\n'; + body += `Shared code changed — full E2E run is recommended.\n\n`; + body += codeBlock('/e2e all'); } else { - body += `Relevant suites detected: ${suites.map(s => `\`${s}\``).join(', ')}.\n\n`; - body += `Comment on this PR:\n\n`; - body += codeBlock('/e2e ' + suites.join(' ')) + '\n\n'; - body += suiteBlock(suites); + body += `E2E suites were found for the changes — [${suites.map(s => `\`${s}\``).join(', ')}]\n\n`; + body += suites.map(s => `
/e2e ${s}
`).join('\n') + '\n\n'; + body += `Comment in the PR to trigger an E2E test, for example:\n\n`; + body += codeBlock('/e2e ' + suites.join(' ')) + '\n'; + body += `_or run the full set:_ \`/e2e all\`\n`; } const { data: comments } = await github.rest.issues.listComments({ diff --git a/.github/workflows/ci-pr.yaml b/.github/workflows/ci-pr.yaml index edae32471..cc990397a 100644 --- a/.github/workflows/ci-pr.yaml +++ b/.github/workflows/ci-pr.yaml @@ -101,6 +101,8 @@ jobs: lint-license: name: Lint (license) + permissions: + contents: write uses: cloudoperators/common/.github/workflows/shared-license.yaml@17d3d6b80851574fcd8c7b6e5c4fe6aee2922359 with: apply_header: false From d54ddb6ad36a81ab9edb1497cd01c8b533d902d0 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Tue, 19 May 2026 02:50:43 +0200 Subject: [PATCH 05/21] ci(workflows): rename info step and always recreate the info comment - ci-e2e-info.yaml: rename the github-script step to "E2E Info" so the Actions UI surfaces something meaningful - delete any previous bot info comment and always create a fresh one, so the comment can't get buried in long review threads (it'll show up at the bottom on every push) Signed-off-by: abhijith-darshan --- .github/workflows/ci-e2e-info.yaml | 31 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci-e2e-info.yaml b/.github/workflows/ci-e2e-info.yaml index dce4e44fc..16ec793af 100644 --- a/.github/workflows/ci-e2e-info.yaml +++ b/.github/workflows/ci-e2e-info.yaml @@ -42,7 +42,7 @@ jobs: if: needs.detect.outputs.has-suites == 'true' && github.event.pull_request.user.login != 'renovate[bot]' runs-on: [ubuntu-latest] steps: - - name: Compose and post comment + - name: E2E Info uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v8.0.0 env: CHANGES: ${{ needs.detect.outputs.changes }} @@ -73,22 +73,21 @@ jobs: repo: context.repo.repo, issue_number: context.issue.number, }); - const existing = comments.find(c => c.body && c.body.startsWith(marker)); - if (existing) { - await github.rest.issues.updateComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: existing.id, - body, - }); - } else { - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.issue.number, - body, - }); + for (const c of comments) { + if (c.body && c.body.startsWith(marker)) { + await github.rest.issues.deleteComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: c.id, + }); + } } + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body, + }); auto-skip-e2e: name: Auto-skip e2e From c448e2c72fa56b6e5031112027ab08f68b955bbf Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Tue, 19 May 2026 03:05:05 +0200 Subject: [PATCH 06/21] ci(workflows): simplify info comment and restore CC0 SPDX tag Collapse detected suites into a single expandable block and tag ci-pr.yaml as CC0-1.0 so the unused-license check passes. Signed-off-by: abhijith-darshan --- .github/workflows/ci-e2e-info.yaml | 14 +++++--------- .github/workflows/ci-pr.yaml | 3 +++ 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci-e2e-info.yaml b/.github/workflows/ci-e2e-info.yaml index 16ec793af..7ef0593c3 100644 --- a/.github/workflows/ci-e2e-info.yaml +++ b/.github/workflows/ci-e2e-info.yaml @@ -53,19 +53,15 @@ jobs: const sharedHit = matched.includes('shared'); const suites = matched.filter(s => s !== 'shared'); - const codeBlock = (s) => '```\n' + s + '\n```'; - - let body = `${marker}\n## E2E\n\n`; + let body = `${marker}\n## E2E Info\n\n`; if (sharedHit) { body += `Shared code changed — full E2E run is recommended.\n\n`; - body += codeBlock('/e2e all'); + body += `Comment \`/e2e all\` to run all suites.\n`; } else { - body += `E2E suites were found for the changes — [${suites.map(s => `\`${s}\``).join(', ')}]\n\n`; - body += suites.map(s => `
/e2e ${s}
`).join('\n') + '\n\n'; - body += `Comment in the PR to trigger an E2E test, for example:\n\n`; - body += codeBlock('/e2e ' + suites.join(' ')) + '\n'; - body += `_or run the full set:_ \`/e2e all\`\n`; + const suiteList = suites.map(s => `- \`${s}\``).join('\n'); + body += `
Detected E2E Suites\n\n${suiteList}\n\n
\n\n`; + body += `Comment \`/e2e all\` to run all suites.\n`; } const { data: comments } = await github.rest.issues.listComments({ diff --git a/.github/workflows/ci-pr.yaml b/.github/workflows/ci-pr.yaml index cc990397a..c4e8241c5 100644 --- a/.github/workflows/ci-pr.yaml +++ b/.github/workflows/ci-pr.yaml @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: 2026 SAP SE or an SAP affiliate company and Greenhouse contributors +# SPDX-License-Identifier: CC0-1.0 + name: CI PR on: From f0962f60a2ffea8f3bcbfd24f88ab09befd41937 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Tue, 19 May 2026 03:07:40 +0200 Subject: [PATCH 07/21] ci(workflows): list detected suites as runnable commands Render the detected suites inside the collapsible as a code block of /e2e lines so they can be copied directly into a comment. Signed-off-by: abhijith-darshan --- .github/workflows/ci-e2e-info.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-e2e-info.yaml b/.github/workflows/ci-e2e-info.yaml index 7ef0593c3..31d136674 100644 --- a/.github/workflows/ci-e2e-info.yaml +++ b/.github/workflows/ci-e2e-info.yaml @@ -59,8 +59,8 @@ jobs: body += `Shared code changed — full E2E run is recommended.\n\n`; body += `Comment \`/e2e all\` to run all suites.\n`; } else { - const suiteList = suites.map(s => `- \`${s}\``).join('\n'); - body += `
Detected E2E Suites\n\n${suiteList}\n\n
\n\n`; + const suiteList = suites.map(s => `/e2e ${s}`).join('\n'); + body += `
Detected E2E Suites\n\n\`\`\`\n${suiteList}\n\`\`\`\n\n
\n\n`; body += `Comment \`/e2e all\` to run all suites.\n`; } From ef2dfc6d4725c89e4a9558877867f28c6e438d20 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Tue, 19 May 2026 03:17:32 +0200 Subject: [PATCH 08/21] (chore): use existing workflow name Signed-off-by: abhijith-darshan --- .github/workflows/ci-e2e-runner.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-e2e-runner.yaml b/.github/workflows/ci-e2e-runner.yaml index 5e9dd4a94..9c463410e 100644 --- a/.github/workflows/ci-e2e-runner.yaml +++ b/.github/workflows/ci-e2e-runner.yaml @@ -1,4 +1,4 @@ -name: CI E2E Runner +name: E2E Workflow on: issue_comment: From 73f2acff828ffb9971daaca301774b53cb043ebe Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Tue, 19 May 2026 03:19:22 +0200 Subject: [PATCH 09/21] (chore): rename file to old name Signed-off-by: abhijith-darshan --- .github/workflows/{ci-e2e-runner.yaml => ci-e2e-test.yaml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{ci-e2e-runner.yaml => ci-e2e-test.yaml} (100%) diff --git a/.github/workflows/ci-e2e-runner.yaml b/.github/workflows/ci-e2e-test.yaml similarity index 100% rename from .github/workflows/ci-e2e-runner.yaml rename to .github/workflows/ci-e2e-test.yaml From 2d84b911a2dd9c46053d57fb46d2effdf8467874 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Tue, 19 May 2026 04:11:04 +0200 Subject: [PATCH 10/21] (chore): reduce ci pr jobs and restore ci-e2e-test Signed-off-by: abhijith-darshan --- .github/paths-filters-ci.yaml | 12 +- .github/workflows/ci-e2e-test.yaml | 242 ++++++++--------------------- .github/workflows/ci-pr.yaml | 72 +++------ 3 files changed, 90 insertions(+), 236 deletions(-) diff --git a/.github/paths-filters-ci.yaml b/.github/paths-filters-ci.yaml index 805cd324c..f446cc280 100644 --- a/.github/paths-filters-ci.yaml +++ b/.github/paths-filters-ci.yaml @@ -10,6 +10,8 @@ go: - 'go.mod' - 'go.sum' - '.golangci.yaml' + - 'Dockerfile*' + - 'Makefile' api: - 'api/**' @@ -17,13 +19,3 @@ api: charts: - 'charts/**' - -docker: - - 'Dockerfile*' - - 'Makefile' - - 'cmd/**' - - 'internal/**' - - 'pkg/**' - - 'api/**' - - 'go.mod' - - 'go.sum' diff --git a/.github/workflows/ci-e2e-test.yaml b/.github/workflows/ci-e2e-test.yaml index 9c463410e..a9add0b87 100644 --- a/.github/workflows/ci-e2e-test.yaml +++ b/.github/workflows/ci-e2e-test.yaml @@ -1,210 +1,96 @@ name: E2E Workflow - on: - issue_comment: - types: [created] - -permissions: - contents: read - pull-requests: read - statuses: write + workflow_dispatch: jobs: - parse: - name: Parse /e2e command - if: ${{ github.event.issue.pull_request && startsWith(github.event.comment.body, '/e2e') }} - runs-on: [ubuntu-latest] + + init: outputs: - sha: ${{ steps.pr.outputs.sha }} - suites: ${{ steps.cmd.outputs.suites }} - mode: ${{ steps.cmd.outputs.mode }} + tests: ${{ steps.e2es.outputs.result }} + runs-on: [ ubuntu-latest ] + name: "Prepare E2E Scenarios" steps: - - name: Resolve PR head SHA - id: pr - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v8.0.0 - with: - script: | - const { owner, repo, number } = context.issue; - const pr = await github.rest.pulls.get({ owner, repo, pull_number: number }); - core.setOutput('sha', pr.data.head.sha); - - - name: Parse comment body - id: cmd - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v8.0.0 - env: - BODY: ${{ github.event.comment.body }} - with: - script: | - const allSuites = [ - 'organization', 'cluster', 'catalog', 'plugin', - 'plugindefinition', 'clusterplugindefinition', - 'teamrolebinding' - ]; - const body = (process.env.BODY || '').trim(); - const firstLine = body.split('\n')[0].trim(); - const tokens = firstLine.split(/\s+/); - if (tokens[0] !== '/e2e') { - core.setFailed(`unexpected command: ${firstLine}`); - return; - } - const args = tokens.slice(1).filter(Boolean); - if (args.length === 0) { - core.setFailed('no suites provided. Use `/e2e all`, `/e2e skip`, or `/e2e [...]`.'); - return; - } - if (args.includes('skip')) { - if (args.length !== 1) { - core.setFailed('`/e2e skip` cannot be combined with other suites.'); - return; - } - core.setOutput('mode', 'skip'); - core.setOutput('suites', '[]'); - return; - } - let suites; - if (args.includes('all')) { - if (args.length !== 1) { - core.setFailed('`/e2e all` cannot be combined with other suites.'); - return; - } - suites = allSuites; - } else { - const unknown = args.filter(a => !allSuites.includes(a)); - if (unknown.length > 0) { - core.setFailed(`unknown suite(s): ${unknown.join(', ')}. Available: ${allSuites.join(', ')}, all, skip.`); - return; - } - suites = [...new Set(args)]; - } - core.setOutput('mode', 'run'); - core.setOutput('suites', JSON.stringify(suites)); + - name: "Checkout" + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - name: Acknowledge command - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v8.0.0 - with: - script: | - await github.rest.reactions.createForIssueComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: context.payload.comment.id, - content: 'rocket', - }); + # find all e2e scenarios in the e2e directory and generate an array of scenario names + - name: "E2E Detection" + id: e2es + run: | + scenarios=$(find ${GITHUB_WORKSPACE}/e2e -type f -name 'e2e_test.go' -not -path "*/authz/*" -exec dirname {} \; | xargs -n 1 basename | jq -R -s -c 'split("\n")[:-1]') + echo $scenarios + echo "result=$scenarios" >> $GITHUB_OUTPUT e2e: - name: E2E ${{ matrix.suite }} - needs: [parse] - if: needs.parse.outputs.mode == 'run' - runs-on: [ubuntu-latest] + name: "Run ${{ matrix.e2es }}(${{ matrix.k8s-version}}) E2E" + if: needs.init.outputs.tests != '[]' + needs: [ init ] + runs-on: [ ubuntu-latest ] + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} strategy: fail-fast: false matrix: - suite: ${{ fromJson(needs.parse.outputs.suites) }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + k8s-version: [ "v1.32.11" ] + e2es: ${{fromJson(needs.init.outputs.tests)}} steps: - - name: Set status pending (${{ matrix.suite }}) - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v8.0.0 - with: - script: | - await github.rest.repos.createCommitStatus({ - owner: context.repo.owner, - repo: context.repo.repo, - sha: '${{ needs.parse.outputs.sha }}', - state: 'pending', - context: 'e2e / ${{ matrix.suite }}', - target_url: `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`, - }); - - - name: Checkout PR head - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - ref: ${{ needs.parse.outputs.sha }} - - - name: Run ${{ matrix.suite }} e2e + # run the e2e tests using composite common/workflows/e2e action + - name: "E2E" id: e2e continue-on-error: true - uses: cloudoperators/common/workflows/e2e@17d3d6b80851574fcd8c7b6e5c4fe6aee2922359 + uses: cloudoperators/common/workflows/e2e@17d3d6b80851574fcd8c7b6e5c4fe6aee2922359 # main env: RUNTIME_ENV: "CI" GH_APP_ID: ${{ secrets.CLOUDOPERATOR_APP_ID }} GH_APP_PRIVATE_KEY: ${{ secrets.CLOUDOPERATOR_APP_PRIVATE_KEY }} GH_APP_INSTALLATION_ID: ${{ secrets.CLOUDOPERATOR_APP_INSTALLATION_ID }} with: - admin-k8s-version: "v1.34.3" - remote-k8s-version: "v1.32.11" - scenario: ${{ matrix.suite }} + admin-k8s-version: "v1.33.7" + remote-k8s-version: ${{ matrix.k8s-version }} + scenario: ${{ matrix.e2es }} admin-config: ${{ github.workspace }}/e2e/greenhouse-admin-cluster.yaml remote-config: ${{ github.workspace }}/dev-env/greenhouse-remote-cluster.yaml - - name: Show e2e logs - if: always() + - name: "Display Logs" continue-on-error: true env: - SCENARIO: ${{ matrix.suite }} - E2E_RESULT_DIR: ${{ github.workspace }}/bin - run: make show-e2e-logs - - - name: Set status final (${{ matrix.suite }}) - if: always() - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v8.0.0 - env: - OUTCOME: ${{ steps.e2e.outcome }} - with: - script: | - const ok = process.env.OUTCOME === 'success'; - await github.rest.repos.createCommitStatus({ - owner: context.repo.owner, - repo: context.repo.repo, - sha: '${{ needs.parse.outputs.sha }}', - state: ok ? 'success' : 'failure', - context: 'e2e / ${{ matrix.suite }}', - target_url: `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`, - }); + SCENARIO: ${{ matrix.e2es }} + E2E_RESULT_DIR: ${{github.workspace}}/bin + run: | + make show-e2e-logs - - name: Fail step on e2e failure - if: steps.e2e.outcome == 'failure' - run: exit 1 + - name: "E2E Failure?" + if: ${{ steps.e2e.outcome == 'failure' }} + run: | + exit 1 - e2eOK: - name: e2eOK - needs: [parse, e2e] - if: always() && needs.parse.result == 'success' - runs-on: [ubuntu-latest] + lint: + runs-on: [ ubuntu-latest ] steps: - - name: Compute aggregate state - id: agg - env: - MODE: ${{ needs.parse.outputs.mode }} - E2E_RESULT: ${{ needs.e2e.result }} - run: | - if [ "$MODE" = "skip" ]; then - echo "state=success" >> "$GITHUB_OUTPUT" - echo "description=skipped via /e2e skip" >> "$GITHUB_OUTPUT" - exit 0 - fi - case "$E2E_RESULT" in - success) echo "state=success" >> "$GITHUB_OUTPUT"; echo "description=all suites passed" >> "$GITHUB_OUTPUT" ;; - skipped) echo "state=success" >> "$GITHUB_OUTPUT"; echo "description=no suites scheduled" >> "$GITHUB_OUTPUT" ;; - *) echo "state=failure" >> "$GITHUB_OUTPUT"; echo "description=one or more suites failed" >> "$GITHUB_OUTPUT" ;; - esac + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + token: ${{ secrets.GITHUB_TOKEN }} - - name: Post e2eOK commit status - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v8.0.0 - env: - STATE: ${{ steps.agg.outputs.state }} - DESCRIPTION: ${{ steps.agg.outputs.description }} + - name: Set up Go + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: - script: | - await github.rest.repos.createCommitStatus({ - owner: context.repo.owner, - repo: context.repo.repo, - sha: '${{ needs.parse.outputs.sha }}', - state: process.env.STATE, - context: 'e2eOK', - description: process.env.DESCRIPTION, - target_url: `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`, - }); + go-version-file: 'go.mod' + token: ${{ secrets.GITHUB_TOKEN }} + - name: golangci-lint + run: make lint - - name: Fail job on aggregate failure - if: steps.agg.outputs.state != 'success' - run: exit 1 \ No newline at end of file + # check if the e2e tests passed for all scenarios + checksOK: + name: "E2E Check" + needs: [ e2e ] + runs-on: [ ubuntu-latest ] + if: always() + steps: + - name: "Check if e2e passed" + run: | + if [ "${{ needs.e2e.result }}" == "success" ]; then + echo "✅ E2E passed 🎉" + else + echo "❌ E2E failed 😭" + exit 1 + fi \ No newline at end of file diff --git a/.github/workflows/ci-pr.yaml b/.github/workflows/ci-pr.yaml index c4e8241c5..6fda954fa 100644 --- a/.github/workflows/ci-pr.yaml +++ b/.github/workflows/ci-pr.yaml @@ -19,7 +19,6 @@ jobs: go: ${{ steps.filter.outputs.go }} api: ${{ steps.filter.outputs.api }} charts: ${{ steps.filter.outputs.charts }} - docker: ${{ steps.filter.outputs.docker }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -29,32 +28,16 @@ jobs: with: filters: .github/paths-filters-ci.yaml - lint-go: - name: Lint (go) + lint: + name: Lint needs: [changes] runs-on: [ubuntu-latest] steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - - name: Set up Go - if: needs.changes.outputs.go == 'true' - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: - go-version-file: 'go.mod' - - - name: golangci-lint - if: needs.changes.outputs.go == 'true' - run: make lint - - lint-generated: - name: Lint (generated) - needs: [changes] - runs-on: [ubuntu-latest] - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + fetch-depth: 0 - name: Set up Go - if: needs.changes.outputs.api == 'true' uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: 'go.mod' @@ -69,15 +52,6 @@ jobs: exit 1 fi - lint-charts: - name: Lint (charts) - needs: [changes] - runs-on: [ubuntu-latest] - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - fetch-depth: 0 - - name: Set up Helm if: needs.changes.outputs.charts == 'true' uses: azure/setup-helm@dda3372f752e03dde6b3237bc9431cdc2f7a02a2 # v5.0.0 @@ -102,17 +76,9 @@ jobs: ct lint --config ct.yaml --target-branch ${{ github.event.repository.default_branch }} fi - lint-license: - name: Lint (license) - permissions: - contents: write - uses: cloudoperators/common/.github/workflows/shared-license.yaml@17d3d6b80851574fcd8c7b6e5c4fe6aee2922359 - with: - apply_header: false - - lint-reuse: - name: Lint (reuse) - uses: cloudoperators/common/.github/workflows/shared-reuse.yaml@17d3d6b80851574fcd8c7b6e5c4fe6aee2922359 + - name: golangci-lint + if: needs.changes.outputs.go == 'true' + run: make lint test: name: Test @@ -133,6 +99,18 @@ jobs: if: needs.changes.outputs.go == 'true' run: make test + license-check: + name: License check + permissions: + contents: write + uses: cloudoperators/common/.github/workflows/shared-license.yaml@17d3d6b80851574fcd8c7b6e5c4fe6aee2922359 + with: + apply_header: false + + reuse-check: + name: Reuse check + uses: cloudoperators/common/.github/workflows/shared-reuse.yaml@17d3d6b80851574fcd8c7b6e5c4fe6aee2922359 + build: name: Build needs: [changes] @@ -141,12 +119,12 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Build Docker image - if: needs.changes.outputs.docker == 'true' + if: needs.changes.outputs.go == 'true' run: make docker-build checksOK: name: checksOK - needs: [lint-go, lint-generated, lint-charts, lint-license, lint-reuse, test, build] + needs: [lint, test, license-check, reuse-check, build] if: always() runs-on: [ubuntu-latest] steps: @@ -154,19 +132,17 @@ jobs: run: | fail=0 for r in \ - "${{ needs.lint-go.result }}" \ - "${{ needs.lint-generated.result }}" \ - "${{ needs.lint-charts.result }}" \ - "${{ needs.lint-license.result }}" \ - "${{ needs.lint-reuse.result }}" \ + "${{ needs.lint.result }}" \ "${{ needs.test.result }}" \ + "${{ needs.license-check.result }}" \ + "${{ needs.reuse-check.result }}" \ "${{ needs.build.result }}"; do if [ "$r" != "success" ] && [ "$r" != "skipped" ]; then fail=1 fi done if [ "$fail" -eq 1 ]; then - echo "::error ::One or more checks failed (lint-go=${{ needs.lint-go.result }}, lint-generated=${{ needs.lint-generated.result }}, lint-charts=${{ needs.lint-charts.result }}, lint-license=${{ needs.lint-license.result }}, lint-reuse=${{ needs.lint-reuse.result }}, test=${{ needs.test.result }}, build=${{ needs.build.result }})" + echo "::error ::One or more checks failed (lint=${{ needs.lint.result }}, test=${{ needs.test.result }}, license-check=${{ needs.license-check.result }}, reuse-check=${{ needs.reuse-check.result }}, build=${{ needs.build.result }})" exit 1 fi echo "All checks passed" From 307b9445c5da62094181ef8fc9d9d16ec892867d Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Tue, 19 May 2026 04:17:41 +0200 Subject: [PATCH 11/21] (chore): add comment driven E2E flow diagram Signed-off-by: abhijith-darshan --- e2e/README.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/e2e/README.md b/e2e/README.md index 3e2483c96..d43f2c5b9 100644 --- a/e2e/README.md +++ b/e2e/README.md @@ -272,3 +272,29 @@ ready or not ready. be ready or not ready. > Note: You can only use `WaitUntilResourceReadyOrNotReady` for resources that use the `lifecycle.Reconcile` interface. + +### Comment-driven E2E flow + +```mermaid +flowchart TD + PR([PR opened / synchronized]) --> Author{Author?} + Author -->|renovate| AutoAll[Auto-post /e2e all] + Author -->|contributor| Detect[ci-e2e-info: detect changed paths] + Detect -->|matches paths-filters-e2e.yaml| Info["Post E2E Info comment
lists detected suites as /e2e suite lines"] + Detect -->|no match| AutoSkip[Auto-post /e2e skip] + + Info -.copy & paste.-> Comment + AutoSkip --> Comment + AutoAll --> Comment + + Comment(["/e2e tokens comment created"]) --> Parse[ci-e2e-test: parse comment body] + Parse -->|/e2e skip| Skip[run=false] + Parse -->|/e2e all| All["run=true
checkout PR head
find e2e -name e2e_test.go"] + Parse -->|/e2e suiteA /e2e suiteB| Run["run=true
use explicit suites"] + + Skip --> OK[Aggregate e2eOK status] + All --> Matrix + Run --> Matrix["Matrix job per suite
cloudoperators/common/workflows/e2e
fail-fast: cancels siblings on first failure"] + Matrix --> OK + OK --> Required([e2eOK = required check on PR]) +``` From 2acb56d95c24b1e4358bd8a99676c92bf25d3bbf Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Mon, 25 May 2026 22:27:54 +0200 Subject: [PATCH 12/21] ci(workflows): replace pull_request e2e with comment-driven runner MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add reactions:write permission to ci-e2e-run.yaml and delete the old pull_request-triggered ci-e2e-test.yaml — replaced by the comment-driven ci-e2e-run.yaml. Signed-off-by: abhijith-darshan --- .github/workflows/ci-e2e-run.yaml | 1 + .github/workflows/ci-e2e-test.yaml | 96 ------------------------------ 2 files changed, 1 insertion(+), 96 deletions(-) delete mode 100644 .github/workflows/ci-e2e-test.yaml diff --git a/.github/workflows/ci-e2e-run.yaml b/.github/workflows/ci-e2e-run.yaml index 8ad86751d..1c5e59ff5 100644 --- a/.github/workflows/ci-e2e-run.yaml +++ b/.github/workflows/ci-e2e-run.yaml @@ -8,6 +8,7 @@ permissions: contents: read pull-requests: read statuses: write + reactions: write jobs: parse: diff --git a/.github/workflows/ci-e2e-test.yaml b/.github/workflows/ci-e2e-test.yaml deleted file mode 100644 index a9add0b87..000000000 --- a/.github/workflows/ci-e2e-test.yaml +++ /dev/null @@ -1,96 +0,0 @@ -name: E2E Workflow -on: - workflow_dispatch: - -jobs: - - init: - outputs: - tests: ${{ steps.e2es.outputs.result }} - runs-on: [ ubuntu-latest ] - name: "Prepare E2E Scenarios" - steps: - - name: "Checkout" - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - # find all e2e scenarios in the e2e directory and generate an array of scenario names - - name: "E2E Detection" - id: e2es - run: | - scenarios=$(find ${GITHUB_WORKSPACE}/e2e -type f -name 'e2e_test.go' -not -path "*/authz/*" -exec dirname {} \; | xargs -n 1 basename | jq -R -s -c 'split("\n")[:-1]') - echo $scenarios - echo "result=$scenarios" >> $GITHUB_OUTPUT - - e2e: - name: "Run ${{ matrix.e2es }}(${{ matrix.k8s-version}}) E2E" - if: needs.init.outputs.tests != '[]' - needs: [ init ] - runs-on: [ ubuntu-latest ] - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - strategy: - fail-fast: false - matrix: - k8s-version: [ "v1.32.11" ] - e2es: ${{fromJson(needs.init.outputs.tests)}} - steps: - # run the e2e tests using composite common/workflows/e2e action - - name: "E2E" - id: e2e - continue-on-error: true - uses: cloudoperators/common/workflows/e2e@17d3d6b80851574fcd8c7b6e5c4fe6aee2922359 # main - env: - RUNTIME_ENV: "CI" - GH_APP_ID: ${{ secrets.CLOUDOPERATOR_APP_ID }} - GH_APP_PRIVATE_KEY: ${{ secrets.CLOUDOPERATOR_APP_PRIVATE_KEY }} - GH_APP_INSTALLATION_ID: ${{ secrets.CLOUDOPERATOR_APP_INSTALLATION_ID }} - with: - admin-k8s-version: "v1.33.7" - remote-k8s-version: ${{ matrix.k8s-version }} - scenario: ${{ matrix.e2es }} - admin-config: ${{ github.workspace }}/e2e/greenhouse-admin-cluster.yaml - remote-config: ${{ github.workspace }}/dev-env/greenhouse-remote-cluster.yaml - - - name: "Display Logs" - continue-on-error: true - env: - SCENARIO: ${{ matrix.e2es }} - E2E_RESULT_DIR: ${{github.workspace}}/bin - run: | - make show-e2e-logs - - - name: "E2E Failure?" - if: ${{ steps.e2e.outcome == 'failure' }} - run: | - exit 1 - - lint: - runs-on: [ ubuntu-latest ] - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up Go - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 - with: - go-version-file: 'go.mod' - token: ${{ secrets.GITHUB_TOKEN }} - - name: golangci-lint - run: make lint - - # check if the e2e tests passed for all scenarios - checksOK: - name: "E2E Check" - needs: [ e2e ] - runs-on: [ ubuntu-latest ] - if: always() - steps: - - name: "Check if e2e passed" - run: | - if [ "${{ needs.e2e.result }}" == "success" ]; then - echo "✅ E2E passed 🎉" - else - echo "❌ E2E failed 😭" - exit 1 - fi \ No newline at end of file From 2e8b1a464b3a72835e9f0860fa6dbb8f63c7974d Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Mon, 25 May 2026 22:31:36 +0200 Subject: [PATCH 13/21] ci(workflows): fix reactions permission for e2e runner MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use issues:write instead of reactions:write — GitHub reactions API requires issues=write scope. Signed-off-by: abhijith-darshan --- .github/workflows/ci-e2e-run.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-e2e-run.yaml b/.github/workflows/ci-e2e-run.yaml index 1c5e59ff5..35ed5f388 100644 --- a/.github/workflows/ci-e2e-run.yaml +++ b/.github/workflows/ci-e2e-run.yaml @@ -8,7 +8,7 @@ permissions: contents: read pull-requests: read statuses: write - reactions: write + issues: write jobs: parse: From d4dffab5e3f032dcaa551e391a7c2415b71e40ac Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Mon, 25 May 2026 22:34:29 +0200 Subject: [PATCH 14/21] ci(workflows): drop acknowledge step from e2e runner MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the rocket reaction step — it requires issues:write which the GITHUB_TOKEN cannot be granted from a PR branch workflow. Signed-off-by: abhijith-darshan --- .github/workflows/ci-e2e-run.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci-e2e-run.yaml b/.github/workflows/ci-e2e-run.yaml index 35ed5f388..8ad86751d 100644 --- a/.github/workflows/ci-e2e-run.yaml +++ b/.github/workflows/ci-e2e-run.yaml @@ -8,7 +8,6 @@ permissions: contents: read pull-requests: read statuses: write - issues: write jobs: parse: From 4c20e79d7132e3b6b7a0ce5ffaaea8da1eb169d4 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Tue, 26 May 2026 15:04:31 +0200 Subject: [PATCH 15/21] ci(workflows): pin common e2e action to f87e1e6 (main) Updates both ci-e2e-run and ci-e2e-test-nightly to pin cloudoperators/common/workflows/e2e to the latest main SHA which includes the new optional ref input. Signed-off-by: abhijith-darshan --- .github/workflows/ci-e2e-run.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-e2e-run.yaml b/.github/workflows/ci-e2e-run.yaml index 8ad86751d..dd0311d07 100644 --- a/.github/workflows/ci-e2e-run.yaml +++ b/.github/workflows/ci-e2e-run.yaml @@ -117,7 +117,7 @@ jobs: steps: - name: Run ${{ matrix.suite }} e2e id: e2e - uses: cloudoperators/common/workflows/e2e@f87e1e6b7889707a9c10d1a8a9f9f481eb311c06 + uses: cloudoperators/common/workflows/e2e@f87e1e6b7889707a9c10d1a8a9f9f481eb311c06 # main env: RUNTIME_ENV: "CI" GH_APP_ID: ${{ secrets.CLOUDOPERATOR_APP_ID }} From 733b8b235644b3d07f900b61b72dc75ed37a1dbf Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Fri, 29 May 2026 13:22:04 +0200 Subject: [PATCH 16/21] ci(workflows): add per-suite commit status to e2e runner Posts pending/success/failure commit status for each matrix suite so the PR checks page shows individual suite results with a spinning indicator while running, alongside the aggregate e2eOK gate. Signed-off-by: abhijith-darshan --- .github/workflows/ci-e2e-run.yaml | 32 +++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/.github/workflows/ci-e2e-run.yaml b/.github/workflows/ci-e2e-run.yaml index dd0311d07..46cea79e9 100644 --- a/.github/workflows/ci-e2e-run.yaml +++ b/.github/workflows/ci-e2e-run.yaml @@ -115,6 +115,20 @@ jobs: matrix: suite: ${{ fromJson(needs.parse.outputs.suites) }} steps: + - name: Set suite status pending + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + await github.rest.repos.createCommitStatus({ + owner: context.repo.owner, + repo: context.repo.repo, + sha: '${{ needs.parse.outputs.sha }}', + state: 'pending', + context: 'e2e / ${{ matrix.suite }}', + description: 'running', + target_url: `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`, + }); + - name: Run ${{ matrix.suite }} e2e id: e2e uses: cloudoperators/common/workflows/e2e@f87e1e6b7889707a9c10d1a8a9f9f481eb311c06 # main @@ -139,6 +153,24 @@ jobs: E2E_RESULT_DIR: ${{ github.workspace }}/bin run: make show-e2e-logs + - name: Set suite status + if: always() + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + E2E_RESULT: ${{ steps.e2e.conclusion }} + with: + script: | + const ok = process.env.E2E_RESULT === 'success'; + await github.rest.repos.createCommitStatus({ + owner: context.repo.owner, + repo: context.repo.repo, + sha: '${{ needs.parse.outputs.sha }}', + state: ok ? 'success' : 'failure', + context: 'e2e / ${{ matrix.suite }}', + description: ok ? 'passed' : 'failed', + target_url: `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`, + }); + e2eOK: name: e2eOK needs: [parse, e2e] From 73c015c2de192d0324bdbe4f564bc605d94d86ea Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Fri, 29 May 2026 13:44:00 +0200 Subject: [PATCH 17/21] (chore): undo test changes Signed-off-by: abhijith-darshan --- internal/controller/plugin/oci_mirror.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/controller/plugin/oci_mirror.go b/internal/controller/plugin/oci_mirror.go index 1a3627169..be82eede9 100644 --- a/internal/controller/plugin/oci_mirror.go +++ b/internal/controller/plugin/oci_mirror.go @@ -132,7 +132,7 @@ func buildPostRenderer(mirror *ocimirror.ImageMirror, manifestSets ...string) *h } // ensureImageReplication pre-replicates container images referenced in renderedManifests. -// On failure, it flags HelmReleaseCreatedCondition with ImageReplicationFailedReason and keeps +// On failure it flags HelmReleaseCreatedCondition with ImageReplicationFailedReason and keeps // the previous status.ImageReplication list intact, so transient errors dont wipe history. func ensureImageReplication(ctx context.Context, mirror *ocimirror.ImageMirror, plugin *greenhousev1alpha1.Plugin, manifestSets ...string) error { replicated, err := mirror.ReplicateOCIArtifacts(ctx, plugin.Status.ImageReplication, manifestSets...) From ce721b62b68fbc9cd0d120a8bfc0d6790c726368 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Fri, 29 May 2026 13:59:52 +0200 Subject: [PATCH 18/21] ci(workflows): remove markers from auto-skip and renovate e2e comments HTML markers prepended to the comment body prevented ci-e2e-run from triggering since it checks startsWith('/e2e'). Auto-posted comments now contain only the bare /e2e command. Signed-off-by: abhijith-darshan --- .github/workflows/ci-e2e-info.yaml | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/.github/workflows/ci-e2e-info.yaml b/.github/workflows/ci-e2e-info.yaml index 31d136674..28c1ffcee 100644 --- a/.github/workflows/ci-e2e-info.yaml +++ b/.github/workflows/ci-e2e-info.yaml @@ -95,21 +95,11 @@ jobs: uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v8.0.0 with: script: | - const marker = ''; - const { data: comments } = await github.rest.issues.listComments({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.issue.number, - }); - if (comments.find(c => c.body && c.body.startsWith(marker))) { - core.info('Auto-skip comment already posted; nothing to do.'); - return; - } await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, - body: `${marker}\n/e2e skip`, + body: '/e2e skip', }); auto-run-e2e-renovate: @@ -124,20 +114,10 @@ jobs: HAS_SUITES: ${{ needs.detect.outputs.has-suites }} with: script: | - const marker = ''; - const { data: comments } = await github.rest.issues.listComments({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.issue.number, - }); - if (comments.find(c => c.body && c.body.startsWith(marker))) { - core.info('Renovate autorun comment already posted; nothing to do.'); - return; - } const cmd = process.env.HAS_SUITES === 'true' ? '/e2e all' : '/e2e skip'; await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, - body: `${marker}\n${cmd}`, + body: cmd, }); From ba0b71d2599f1a0798ef370b320286cc8b9829c8 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Fri, 29 May 2026 14:11:04 +0200 Subject: [PATCH 19/21] ci(workflows): use GitHub App token for auto e2e comments Auto-skip and renovate auto-run jobs now post comments via a GitHub App token so the comment triggers ci-e2e-run (GITHUB_TOKEN comments cannot trigger other workflows). Steps are gated on !github.event.pull_request.head.repo.fork to avoid secret access failures on forked PRs. Signed-off-by: abhijith-darshan --- .github/workflows/ci-e2e-info.yaml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/.github/workflows/ci-e2e-info.yaml b/.github/workflows/ci-e2e-info.yaml index 28c1ffcee..8bc38ab23 100644 --- a/.github/workflows/ci-e2e-info.yaml +++ b/.github/workflows/ci-e2e-info.yaml @@ -91,9 +91,20 @@ jobs: if: needs.detect.outputs.has-suites == 'false' && github.event.pull_request.user.login != 'renovate[bot]' runs-on: [ubuntu-latest] steps: + - name: Generate GitHub App token + id: app-token + if: ${{ !github.event.pull_request.head.repo.fork }} + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 + with: + app-id: ${{ secrets.CLOUDOPERATOR_APP_ID }} + private-key: ${{ secrets.CLOUDOPERATOR_APP_PRIVATE_KEY }} + permission-issues: write + - name: Post /e2e skip comment + if: ${{ !github.event.pull_request.head.repo.fork }} uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v8.0.0 with: + github-token: ${{ steps.app-token.outputs.token }} script: | await github.rest.issues.createComment({ owner: context.repo.owner, @@ -108,11 +119,22 @@ jobs: if: github.event.pull_request.user.login == 'renovate[bot]' runs-on: [ubuntu-latest] steps: + - name: Generate GitHub App token + id: app-token + if: ${{ !github.event.pull_request.head.repo.fork }} + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 + with: + app-id: ${{ secrets.CLOUDOPERATOR_APP_ID }} + private-key: ${{ secrets.CLOUDOPERATOR_APP_PRIVATE_KEY }} + permission-issues: write + - name: Post /e2e command comment + if: ${{ !github.event.pull_request.head.repo.fork }} uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v8.0.0 env: HAS_SUITES: ${{ needs.detect.outputs.has-suites }} with: + github-token: ${{ steps.app-token.outputs.token }} script: | const cmd = process.env.HAS_SUITES === 'true' ? '/e2e all' : '/e2e skip'; await github.rest.issues.createComment({ From 87bbf41a27bcf20f2e455dec8e878b487a8d8b65 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Fri, 29 May 2026 14:17:13 +0200 Subject: [PATCH 20/21] ci(workflows): fix app token permission for PR comments Switch from permission-issues to permission-pull-requests for the GitHub App token used to post auto-skip and renovate e2e comments. Signed-off-by: abhijith-darshan --- .github/workflows/ci-e2e-info.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-e2e-info.yaml b/.github/workflows/ci-e2e-info.yaml index 8bc38ab23..546f7c0a2 100644 --- a/.github/workflows/ci-e2e-info.yaml +++ b/.github/workflows/ci-e2e-info.yaml @@ -98,7 +98,7 @@ jobs: with: app-id: ${{ secrets.CLOUDOPERATOR_APP_ID }} private-key: ${{ secrets.CLOUDOPERATOR_APP_PRIVATE_KEY }} - permission-issues: write + permission-pull-requests: write - name: Post /e2e skip comment if: ${{ !github.event.pull_request.head.repo.fork }} @@ -126,7 +126,7 @@ jobs: with: app-id: ${{ secrets.CLOUDOPERATOR_APP_ID }} private-key: ${{ secrets.CLOUDOPERATOR_APP_PRIVATE_KEY }} - permission-issues: write + permission-pull-requests: write - name: Post /e2e command comment if: ${{ !github.event.pull_request.head.repo.fork }} From 7112633e3e7bc5495c5918788b62392a9e3156da Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Fri, 29 May 2026 14:37:10 +0200 Subject: [PATCH 21/21] ci(workflows): allow cloud-operator-bot to trigger e2e runner The bot posts auto-skip and renovate e2e comments via GitHub App token. Allow it through the collaborator check by matching the actor name directly. Signed-off-by: abhijith-darshan --- .github/workflows/ci-e2e-run.yaml | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci-e2e-run.yaml b/.github/workflows/ci-e2e-run.yaml index 46cea79e9..9bb20e42a 100644 --- a/.github/workflows/ci-e2e-run.yaml +++ b/.github/workflows/ci-e2e-run.yaml @@ -19,10 +19,13 @@ jobs: suites: ${{ steps.suites.outputs.suites }} run: ${{ steps.cmd.outputs.run }} steps: - - name: Check commenter is a collaborator + - name: Check Collaborator Permission uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: script: | + if (context.actor === 'cloud-operator-bot[bot]') { + return; + } try { const { data } = await github.rest.repos.getCollaboratorPermissionLevel({ owner: context.repo.owner, @@ -37,7 +40,7 @@ jobs: core.setFailed(`e2e trigger denied — @${context.actor} is not a maintainer.`); } - - name: Resolve PR head SHA + - name: Resolve PR SHA id: pr uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: @@ -46,7 +49,7 @@ jobs: const pr = await github.rest.pulls.get({ owner, repo, pull_number: number }); core.setOutput('sha', pr.data.head.sha); - - name: Parse comment body + - name: Parse Command id: cmd uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: @@ -85,13 +88,13 @@ jobs: core.setOutput('discover', 'false'); core.setOutput('explicit', JSON.stringify([...new Set(tokens)])); - - name: Checkout PR head (for suite discovery) + - name: Checkout PR if: steps.cmd.outputs.discover == 'true' uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ steps.pr.outputs.sha }} - - name: Resolve suite list + - name: Resolve Suites id: suites env: DISCOVER: ${{ steps.cmd.outputs.discover }} @@ -115,7 +118,7 @@ jobs: matrix: suite: ${{ fromJson(needs.parse.outputs.suites) }} steps: - - name: Set suite status pending + - name: "Set e2e/${{ matrix.suite }} Pending" uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: script: | @@ -153,7 +156,7 @@ jobs: E2E_RESULT_DIR: ${{ github.workspace }}/bin run: make show-e2e-logs - - name: Set suite status + - name: "Set e2e/${{ matrix.suite }} Status" if: always() uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: