diff --git a/.github/workflows/deploy-backend.yml b/.github/workflows/deploy-backend.yml index 0a67128..31c3d61 100644 --- a/.github/workflows/deploy-backend.yml +++ b/.github/workflows/deploy-backend.yml @@ -51,6 +51,9 @@ name: Deploy Backend to EC2 +permissions: + contents: read + on: push: branches: ['dev', 'main'] @@ -259,9 +262,9 @@ jobs: # force_smoke_failure test path. deployed_sha: ${{ (inputs.force_smoke_failure == true && 'forced-failure-sentinel') || github.sha }} min_passed: ${{ inputs.force_smoke_failure && 999 || 1 }} - # provider-smoke creates datasets/use cases with the test account, so - # prod deploys run only the read-only api and consumer suites. - skip_provider: ${{ github.ref_name == 'main' }} + # main: only the readonly allowlist ever runs against prod. + # dev: stage 1 of 2 -- functional-tests runs only if this passes. + suite: ${{ github.ref_name == 'main' && 'readonly' || 'smoke' }} secrets: # This job can't see environment-scoped secrets, and the repo-level # HOME_URL_DEV is the dev site -- passing it on main smoke-tested dev @@ -277,14 +280,56 @@ jobs: # and the job fails its preflight. KEYCLOAK_CLIENT_SECRET: ${{ secrets.KEYCLOAK_CLIENT_SECRET }} + functional-tests: + name: Functional Tests + needs: smoke-tests + # dev only, stage 2: runs only once smoke has passed. Functional tests + # write (datasets, use cases), so they never run against prod. + if: github.ref_name != 'main' + uses: CivicDataLab/CivicDataSpace-test/.github/workflows/run-smoke.yml@CI + with: + suite: functional + api_base_url: ${{ vars.DEV_API_BASE_URL }} + # Provider functional is flaky against dev, so it runs report-only in + # provider-functional-report instead of gating the deploy. + skip_provider: true + secrets: + HOME_URL_DEV: ${{ secrets.HOME_URL_DEV }} + TEST_EMAIL_1: ${{ secrets.TEST_EMAIL_1 }} + TEST_PASSWORD_1: ${{ secrets.TEST_PASSWORD_1 }} + TEST_EMAIL_2: ${{ secrets.TEST_EMAIL_2 }} + TEST_PASSWORD_2: ${{ secrets.TEST_PASSWORD_2 }} + KEYCLOAK_CLIENT_SECRET: ${{ secrets.KEYCLOAK_CLIENT_SECRET }} + + provider-functional-report: + name: Provider Functional (report-only) + needs: smoke-tests + # Report-only: rollback and finalize do not list this job in needs, so + # its result never rolls back or blocks a dev deploy. Promote it to a + # gate once the provider suite is stable against dev. + if: github.ref_name != 'main' + uses: CivicDataLab/CivicDataSpace-test/.github/workflows/run-smoke.yml@CI + with: + suite: functional + only_provider: true + secrets: + HOME_URL_DEV: ${{ secrets.HOME_URL_DEV }} + TEST_EMAIL_1: ${{ secrets.TEST_EMAIL_1 }} + TEST_PASSWORD_1: ${{ secrets.TEST_PASSWORD_1 }} + TEST_EMAIL_2: ${{ secrets.TEST_EMAIL_2 }} + TEST_PASSWORD_2: ${{ secrets.TEST_PASSWORD_2 }} + rollback-on-smoke-failure: name: Rollback (smoke tests failed) # `deploy` must be in needs: for needs.deploy.result to resolve here. - needs: [deploy, smoke-tests] - # failure()/success() builtins rather than needs.smoke-tests.result -- - # both are false on cancellation, which is the behaviour we want; - # if: always() would ignore cancellation entirely. - if: failure() && needs.deploy.result == 'success' + # provider-functional-report is listed so this waits for every test to + # finish, but its result is deliberately not checked (report-only). + needs: [deploy, smoke-tests, functional-tests, provider-functional-report] + # Explicit gating results, not failure(): failure() would also count the + # report-only provider job. !cancelled() keeps cancellation a no-op. + if: >- + ${{ !cancelled() && needs.deploy.result == 'success' && + (needs.smoke-tests.result == 'failure' || needs.functional-tests.result == 'failure') }} runs-on: ubuntu-latest environment: ${{ github.ref_name == 'main' && 'production' || 'development' }} # 50m: must comfortably exceed the Restore previous image step's own @@ -331,8 +376,15 @@ jobs: finalize-deploy: name: Finalize Deploy - needs: [deploy, smoke-tests] - if: success() + # provider-functional-report is listed so this waits for every test to + # finish, but its result is deliberately not checked (report-only). + needs: [deploy, smoke-tests, functional-tests, provider-functional-report] + # Explicit gating results, not success()/!failure(): on main the functional + # jobs are skipped by design, and the provider job must not block. + if: >- + ${{ !cancelled() && needs.deploy.result == 'success' && + needs.smoke-tests.result == 'success' && + (needs.functional-tests.result == 'success' || needs.functional-tests.result == 'skipped') }} runs-on: ubuntu-latest environment: ${{ github.ref_name == 'main' && 'production' || 'development' }} timeout-minutes: 10 diff --git a/.github/workflows/pr-gate.yml b/.github/workflows/pr-gate.yml new file mode 100644 index 0000000..778681e --- /dev/null +++ b/.github/workflows/pr-gate.yml @@ -0,0 +1,51 @@ +# Full test suite against dev for every PR into main. Required by branch +# protection on main, so a PR merges (and prod deploys) only once it's green. +# dev already runs the PR's code (it was merged to dev first); deployed_sha +# makes the suite fail if dev is running anything else. +name: PR Gate + +permissions: {} + +on: + pull_request: + branches: [main] + +permissions: + contents: read + pull-requests: read + +concurrency: + group: pr-gate-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + full-suite: + name: Full Suite (dev) + uses: CivicDataLab/CivicDataSpace-test/.github/workflows/run-smoke.yml@CI + with: + suite: full + # Provider is flaky against dev: it reports in provider-report below, which + # branch protection does not require. + skip_provider: true + api_base_url: ${{ vars.DEV_API_BASE_URL }} + deployed_sha: ${{ github.event.pull_request.head.sha }} + secrets: + HOME_URL_DEV: ${{ secrets.HOME_URL_DEV }} + TEST_EMAIL_1: ${{ secrets.TEST_EMAIL_1 }} + TEST_PASSWORD_1: ${{ secrets.TEST_PASSWORD_1 }} + TEST_EMAIL_2: ${{ secrets.TEST_EMAIL_2 }} + TEST_PASSWORD_2: ${{ secrets.TEST_PASSWORD_2 }} + KEYCLOAK_CLIENT_SECRET: ${{ secrets.KEYCLOAK_CLIENT_SECRET }} + + provider-report: + name: Provider Full Suite (report-only) + uses: CivicDataLab/CivicDataSpace-test/.github/workflows/run-smoke.yml@CI + with: + suite: full + only_provider: true + secrets: + HOME_URL_DEV: ${{ secrets.HOME_URL_DEV }} + TEST_EMAIL_1: ${{ secrets.TEST_EMAIL_1 }} + TEST_PASSWORD_1: ${{ secrets.TEST_PASSWORD_1 }} + TEST_EMAIL_2: ${{ secrets.TEST_EMAIL_2 }} + TEST_PASSWORD_2: ${{ secrets.TEST_PASSWORD_2 }}