Merge pull request 'Shortens deploy label, adds manual test-deploy toggle, skips unaffected backend/frontend gates' (#4) from feature/gitea-workflow-optimizations into master
Continuous Integration / config (push) Successful in 10s
Continuous Integration / changes (push) Successful in 21s
Continuous Integration / backend-build (push) Successful in 4m54s
Continuous Integration / vulnerability-scan (push) Successful in 4m39s
Continuous Integration / frontend-prepare (push) Successful in 1m46s
Continuous Integration / backend-test (push) Successful in 5m47s
Continuous Integration / frontend-build (push) Successful in 2m13s
Continuous Integration / frontend-test (push) Successful in 4m32s
Continuous Integration / frontend-lint (push) Successful in 2m10s
Continuous Integration / publish-test (push) Canceled after 0s
Continuous Integration / publish-production (push) Canceled after 0s
Continuous Integration / deploy-test (push) Canceled after 0s
Continuous Integration / deploy-production (push) Canceled after 0s

Reviewed-on: #4
This commit was merged in pull request #4.
This commit is contained in:
2026-07-31 16:00:53 +02:00
+76 -10
View File
@@ -3,8 +3,12 @@ name: Continuous Integration
on:
workflow_dispatch:
inputs:
deploy_test:
description: 'Deploy to Test'
type: boolean
default: false
deploy_production:
description: 'Also deploy to production after a successful build/test (in addition to the automatic test deploy)'
description: 'Deploy to Production'
type: boolean
default: false
pull_request:
@@ -62,8 +66,47 @@ jobs:
echo "health_check_url_test=${{ env.HEALTH_CHECK_URL_TEST }}" >> "$GITHUB_OUTPUT"
echo "health_check_url_production=${{ env.HEALTH_CHECK_URL_PRODUCTION }}" >> "$GITHUB_OUTPUT"
# Path-based skip: keeps the pipeline short when a change only touches one side of the repo.
# fetch-depth: 0 gives paths-filter full local git history to diff against, rather than relying
# on its GitHub-API fallback for missing history, which targets GitHub's REST API and cannot be
# assumed to work against a Gitea remote. Changes under the workflow files themselves count as
# both backend and frontend, so a workflow edit is never left partially untested. A manual
# workflow_dispatch run has no `before` commit to diff against and is always an explicit request
# to run everything, so it force-reports both sides as changed rather than trusting the filter.
changes:
runs-on: ubuntu-latest
outputs:
backend: ${{ github.event_name == 'workflow_dispatch' || steps.filter.outputs.backend == 'true' }}
frontend: ${{ github.event_name == 'workflow_dispatch' || steps.filter.outputs.frontend == 'true' }}
steps:
# Skipped on workflow_dispatch: there's no `before` commit to diff against, and a manual run
# always force-reports both sides changed anyway (see the outputs above), so the diff would be
# wasted work.
- uses: actions/checkout@v4
if: github.event_name != 'workflow_dispatch'
with:
fetch-depth: 0
- uses: dorny/paths-filter@v3
if: github.event_name != 'workflow_dispatch'
id: filter
with:
filters: |
workflow: &workflow
- '.gitea/workflows/**'
backend:
- *workflow
- 'src/**'
- 'tests/**'
- '*.sln'
- '**/*.csproj'
frontend:
- *workflow
- 'frontend/**'
# --- Gate 1: backend build ---
backend-build:
needs: changes
if: needs.changes.outputs.backend == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@@ -75,7 +118,8 @@ jobs:
# --- Gate 2: backend tests ---
backend-test:
needs: backend-build
needs: [changes, backend-build]
if: needs.changes.outputs.backend == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@@ -90,6 +134,8 @@ jobs:
# step greps its own output and fails deliberately (FR-22, D-12, OPEN-03 — closed by pinning
# Microsoft.OpenApi and System.Security.Cryptography.Xml in the relevant .csproj files).
vulnerability-scan:
needs: changes
if: needs.changes.outputs.backend == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@@ -106,6 +152,8 @@ jobs:
fi
frontend-prepare:
needs: changes
if: needs.changes.outputs.frontend == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@@ -135,7 +183,8 @@ jobs:
# that environment's Vite variables set and copies the result into wwwroot/admin before
# `dotnet publish` runs — see that step's comment for why this isn't an MSBuild target instead.
frontend-build:
needs: frontend-prepare
needs: [changes, frontend-prepare]
if: needs.changes.outputs.frontend == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@@ -163,7 +212,8 @@ jobs:
# --- Gate 5: frontend tests ---
frontend-test:
needs: frontend-prepare
needs: [changes, frontend-prepare]
if: needs.changes.outputs.frontend == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@@ -191,7 +241,8 @@ jobs:
# --- Gate 6: frontend lint / format-check ---
frontend-lint:
needs: frontend-prepare
needs: [changes, frontend-prepare]
if: needs.changes.outputs.frontend == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@@ -227,7 +278,15 @@ jobs:
# gate cannot see the actual runtime CSP configuration; it only catches drift between the frontend
# build and what this variable claims the CSP allows.
publish-test:
needs: [backend-build, backend-test, vulnerability-scan, frontend-build, frontend-test, frontend-lint]
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.
if: |
always() && !failure() && !cancelled() &&
(needs.changes.outputs.backend == 'true' || needs.changes.outputs.frontend == 'true')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@@ -289,8 +348,12 @@ jobs:
# a distinct job rather than a parameterized reuse of publish-test, because VITE_APP_ENV is a
# Vite build-time value: one dist/ bundle cannot be tagged as both 'test' and 'production' (FR-05).
publish-production:
needs: [backend-build, backend-test, vulnerability-scan, frontend-build, frontend-test, frontend-lint]
if: github.event_name == 'workflow_dispatch' && github.event.inputs.deploy_production == 'true'
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.
if: |
always() && !failure() && !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
steps:
- uses: actions/checkout@v4
@@ -342,10 +405,13 @@ jobs:
path: ${{ env.ARTIFACT_NAME_PRODUCTION }}
retention-days: 1
# Automatic test deploy on push to master, or on any workflow_dispatch run (FR-03, D-01, D-09).
# Automatic test deploy on every push to master (unchanged — a merge should reach test without
# extra steps). A manual workflow_dispatch run only deploys to test when deploy_test is checked,
# since a dispatch run is often just re-running CI/production, not a new test build (FR-03, D-01,
# D-09).
deploy-test:
needs: [publish-test, config]
if: github.event_name == 'workflow_dispatch' || (github.event_name == 'push' && github.ref == 'refs/heads/master')
if: (github.event_name == 'workflow_dispatch' && github.event.inputs.deploy_test == 'true') || (github.event_name == 'push' && github.ref == 'refs/heads/master')
uses: ./.gitea/workflows/deploy-scp.yaml
secrets: inherit
with: