From 12b1df6ede446c76d8c7894dc06100deb4aba4cc Mon Sep 17 00:00:00 2001 From: Sluijsens Date: Fri, 31 Jul 2026 16:53:46 +0200 Subject: [PATCH 1/2] Replaces unsupported failure()/cancelled() with explicit needs..result checks in publish gates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Gitea Actions only implements always() from GitHub Actions' status-check functions; success(), failure(), and cancelled() aren't supported. publish-test/publish-production relied on !failure() && !cancelled() to let a run through when an unaffected gate job was skipped by the path filter while still blocking on an actual failure — rewritten to check needs..result explicitly instead, which Gitea does support. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01VhySVFARKAP89WqQ3Kff8M --- .gitea/workflows/continuous_integration.yaml | 31 +++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/.gitea/workflows/continuous_integration.yaml b/.gitea/workflows/continuous_integration.yaml index f8a0fc2..5639319 100644 --- a/.gitea/workflows/continuous_integration.yaml +++ b/.gitea/workflows/continuous_integration.yaml @@ -280,12 +280,21 @@ jobs: publish-test: needs: [changes, backend-build, backend-test, vulnerability-scan, frontend-build, frontend-test, frontend-lint] # always() bypasses the automatic skip-cascade from a gate job that was itself skipped (because - # its side of the repo didn't change) — !failure() && !cancelled() still blocks a run where a - # gate that DID run actually failed. The remaining changes.outputs check makes sure there's - # something to publish at all: a run that touched neither backend nor frontend has nothing new - # to deploy. + # its side of the repo didn't change). Gitea Actions only implements always() from the GitHub + # Actions status-check functions — success()/failure()/cancelled() are not supported — so each + # gate's outcome is checked explicitly via needs..result instead: a skipped gate is fine, + # but a gate that actually ran and failed or was cancelled still blocks the publish. The final + # changes.outputs check makes sure there's something to publish at all: a run that touched + # neither backend nor frontend has nothing new to deploy. if: | - always() && !failure() && !cancelled() && + always() && + needs.changes.result == 'success' && + needs.backend-build.result != 'failure' && needs.backend-build.result != 'cancelled' && + needs.backend-test.result != 'failure' && needs.backend-test.result != 'cancelled' && + needs.vulnerability-scan.result != 'failure' && needs.vulnerability-scan.result != 'cancelled' && + needs.frontend-build.result != 'failure' && needs.frontend-build.result != 'cancelled' && + needs.frontend-test.result != 'failure' && needs.frontend-test.result != 'cancelled' && + needs.frontend-lint.result != 'failure' && needs.frontend-lint.result != 'cancelled' && (needs.changes.outputs.backend == 'true' || needs.changes.outputs.frontend == 'true') runs-on: ubuntu-latest steps: @@ -349,9 +358,17 @@ jobs: # Vite build-time value: one dist/ bundle cannot be tagged as both 'test' and 'production' (FR-05). publish-production: needs: [changes, backend-build, backend-test, vulnerability-scan, frontend-build, frontend-test, frontend-lint] - # See publish-test's comment on the always()/!failure()/!cancelled() combination above. + # See publish-test's comment above on why this checks needs..result explicitly instead of + # failure()/cancelled(), which Gitea Actions doesn't support. if: | - always() && !failure() && !cancelled() && + always() && + needs.changes.result == 'success' && + needs.backend-build.result != 'failure' && needs.backend-build.result != 'cancelled' && + needs.backend-test.result != 'failure' && needs.backend-test.result != 'cancelled' && + needs.vulnerability-scan.result != 'failure' && needs.vulnerability-scan.result != 'cancelled' && + needs.frontend-build.result != 'failure' && needs.frontend-build.result != 'cancelled' && + needs.frontend-test.result != 'failure' && needs.frontend-test.result != 'cancelled' && + needs.frontend-lint.result != 'failure' && needs.frontend-lint.result != 'cancelled' && github.event_name == 'workflow_dispatch' && github.event.inputs.deploy_production == 'true' && (needs.changes.outputs.backend == 'true' || needs.changes.outputs.frontend == 'true') runs-on: ubuntu-latest From de065784445df28303f0e10320390cd1afb9d669 Mon Sep 17 00:00:00 2001 From: Sluijsens Date: Fri, 31 Jul 2026 20:17:55 +0200 Subject: [PATCH 2/2] Logs workflow_dispatch input values and skips publish-test on a manual run without deploy_test checked Gitea's UI has no reliable way to see a past run's dispatch input values, so the config job now echoes them into the log. publish-test previously always built and uploaded the test artifact regardless of the deploy_test/deploy_production selection, wasting a build on manual runs that were never going to deploy to test; it now mirrors publish-production by skipping when workflow_dispatch didn't check deploy_test. Push and pull_request runs are unaffected. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01VhySVFARKAP89WqQ3Kff8M --- .gitea/workflows/continuous_integration.yaml | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/.gitea/workflows/continuous_integration.yaml b/.gitea/workflows/continuous_integration.yaml index 5639319..7f3fcce 100644 --- a/.gitea/workflows/continuous_integration.yaml +++ b/.gitea/workflows/continuous_integration.yaml @@ -53,6 +53,13 @@ jobs: health_check_url_test: ${{ steps.set.outputs.health_check_url_test }} health_check_url_production: ${{ steps.set.outputs.health_check_url_production }} steps: + # Gitea has no reliable way to see a past run's workflow_dispatch input values from the UI, so + # this logs them explicitly — the only durable record of what was actually checked for a run. + - name: Log dispatch inputs + if: github.event_name == 'workflow_dispatch' + run: | + echo "Deploy to Test: ${{ github.event.inputs.deploy_test }}" + echo "Deploy to Production: ${{ github.event.inputs.deploy_production }}" - id: set run: | echo "artifact_name_test=${{ env.ARTIFACT_NAME_TEST }}" >> "$GITHUB_OUTPUT" @@ -283,9 +290,11 @@ jobs: # its side of the repo didn't change). Gitea Actions only implements always() from the GitHub # Actions status-check functions — success()/failure()/cancelled() are not supported — so each # gate's outcome is checked explicitly via needs..result instead: a skipped gate is fine, - # but a gate that actually ran and failed or was cancelled still blocks the publish. The final + # but a gate that actually ran and failed or was cancelled still blocks the publish. The # changes.outputs check makes sure there's something to publish at all: a run that touched - # neither backend nor frontend has nothing new to deploy. + # neither backend nor frontend has nothing new to deploy. The final line skips this build on a + # manual workflow_dispatch run that didn't check deploy_test — building and uploading a test + # artifact nobody is going to deploy is wasted work; push/PR runs are unaffected. if: | always() && needs.changes.result == 'success' && @@ -295,7 +304,8 @@ jobs: needs.frontend-build.result != 'failure' && needs.frontend-build.result != 'cancelled' && needs.frontend-test.result != 'failure' && needs.frontend-test.result != 'cancelled' && needs.frontend-lint.result != 'failure' && needs.frontend-lint.result != 'cancelled' && - (needs.changes.outputs.backend == 'true' || needs.changes.outputs.frontend == 'true') + (needs.changes.outputs.backend == 'true' || needs.changes.outputs.frontend == 'true') && + (github.event_name != 'workflow_dispatch' || github.event.inputs.deploy_test == 'true') runs-on: ubuntu-latest steps: - uses: actions/checkout@v4