Use single Gitea CI/CD pipeline
CI - Build & Verify / Build, Typecheck & Hygiene (push) Failing after 11s
CI - Build & Verify / Deploy to award.noveria.net (push) Has been skipped

This commit is contained in:
AzuTear
2026-06-26 18:35:08 +02:00
parent 6a6eb412ae
commit 3499ad0df5
2 changed files with 181 additions and 204 deletions
+181
View File
@@ -10,6 +10,13 @@ on:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:
inputs:
no_cache:
description: 'Build Docker images without cache'
required: false
default: false
type: boolean
jobs:
verify:
@@ -63,3 +70,177 @@ jobs:
-w /workspace/frontend \
node:24-alpine \
sh -lc 'npm ci && npm run build'
deploy:
name: Deploy to award.noveria.net
runs-on: ubuntu-latest
needs: verify
timeout-minutes: 30
if: |
github.event_name == 'workflow_dispatch' ||
(github.event_name == 'push' && gitea.ref == 'refs/heads/main')
env:
DEPLOY_PATH: /home/projekte_bao/vtuber-awards
DEPLOY_APP_PATH: /home/projekte_bao/vtuber-awards/app
DB_NETWORK: vtuber-awards_internal
LIVE_URL: https://award.noveria.net
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Resolve deploy metadata
id: meta
run: |
set -euo pipefail
echo "sha=$(git rev-parse HEAD)" >> "$GITEA_OUTPUT"
echo "short_sha=$(git rev-parse --short HEAD)" >> "$GITEA_OUTPUT"
git log -1 --oneline
- name: Verify production host layout
run: |
set -euo pipefail
docker run --rm \
-v "${DEPLOY_PATH}:/deploy:ro" \
alpine:latest \
sh -lc '
test -f /deploy/compose.yaml
test -f /deploy/deploy/backend.Dockerfile
test -f /deploy/deploy/web.Dockerfile
test -d /deploy/app
'
- name: Sync code to production app directory
run: |
set -euo pipefail
docker run --rm \
-v "${{ gitea.workspace }}:/src:ro" \
-v "${DEPLOY_APP_PATH}:/dest" \
alpine:latest \
sh -lc '
set -e
dest_owner="$(stat -c "%u:%g" /dest)"
find /dest -mindepth 1 -maxdepth 1 ! -name .git -exec rm -rf {} +
cd /src
find . -mindepth 1 -maxdepth 1 ! -name .git -exec cp -R {} /dest/ \;
chown -R "$dest_owner" /dest
'
- name: Build Docker images
run: |
set -euo pipefail
no_cache="${{ github.event_name == 'workflow_dispatch' && inputs.no_cache || false }}"
build_args=""
if [ "$no_cache" = "true" ]; then
build_args="--no-cache"
fi
docker run --rm \
-v "${DEPLOY_PATH}:/workspace/vtube-awards" \
-v /var/run/docker.sock:/var/run/docker.sock \
-w /workspace/vtube-awards \
docker:cli \
sh -lc "docker compose config >/dev/null && docker compose build ${build_args} api web"
- name: Backup database before migrations
run: |
set -euo pipefail
docker run --rm \
-v "${DEPLOY_PATH}:/workspace/vtube-awards" \
-v /var/run/docker.sock:/var/run/docker.sock \
-w /workspace/vtube-awards \
docker:cli \
sh -lc '
set -e
mkdir -p backups
stamp="$(date -u +%Y%m%dT%H%M%SZ)"
backup="backups/predeploy-${{ steps.meta.outputs.short_sha }}-${stamp}.dump"
docker compose exec -T postgres sh -lc '"'"'pg_dump -U "$POSTGRES_USER" -d "$POSTGRES_DB" -Fc'"'"' > "$backup"
test -s "$backup"
find backups -name "predeploy-*.dump" -mtime +14 -delete
echo "Database backup written to $backup"
'
- name: Apply EF Core migrations
run: |
set -euo pipefail
docker run --rm \
-e DEPLOY_APP_PATH="${DEPLOY_APP_PATH}" \
-e DB_NETWORK="${DB_NETWORK}" \
-v "${DEPLOY_PATH}:/workspace/vtube-awards" \
-v /var/run/docker.sock:/var/run/docker.sock \
-w /workspace/vtube-awards \
docker:cli \
sh -lc '
set -e
db="$(docker compose exec -T postgres printenv POSTGRES_DB)"
user="$(docker compose exec -T postgres printenv POSTGRES_USER)"
password="$(docker compose exec -T postgres printenv POSTGRES_PASSWORD)"
docker run --rm \
--network "${DB_NETWORK}" \
-v "${DEPLOY_APP_PATH}/Backend:/src/Backend" \
-w /src/Backend \
-e "VTSA_POSTGRES=Host=postgres;Port=5432;Database=${db};Username=${user};Password=${password}" \
mcr.microsoft.com/dotnet/sdk:8.0-alpine \
sh -lc "dotnet tool install --global dotnet-ef >/dev/null && export PATH=\$PATH:/root/.dotnet/tools && dotnet restore >/dev/null && dotnet ef database update"
'
- name: Restart production services
run: |
set -euo pipefail
docker run --rm \
-v "${DEPLOY_PATH}:/workspace/vtube-awards" \
-v /var/run/docker.sock:/var/run/docker.sock \
-w /workspace/vtube-awards \
docker:cli \
sh -lc 'docker compose up -d --wait --force-recreate api web'
- name: Verify live health
run: |
set -euo pipefail
retry() {
label="$1"
url="$2"
attempts=10
wait=3
for attempt in $(seq 1 "$attempts"); do
if body="$(curl -fsS --max-time 15 "$url")"; then
echo "$label OK on attempt $attempt"
echo "$body"
return 0
fi
echo "$label failed on attempt $attempt/$attempts; waiting ${wait}s"
sleep "$wait"
done
echo "$label failed after $attempts attempts"
return 1
}
retry "API health" "${LIVE_URL}/api/health"
db_body="$(curl -fsS --max-time 15 "${LIVE_URL}/api/health/database")"
echo "$db_body"
echo "$db_body" | grep -q '"canConnect":true'
echo "$db_body" | grep -q '"pendingMigrations":\[\]'
- name: Verify frontend assets
run: |
set -euo pipefail
index="$(curl -fsS --max-time 15 "${LIVE_URL}/")"
js_asset="$(printf "%s" "$index" | grep -Eo '/assets/index-[^"]+\.js' | head -n 1)"
css_asset="$(printf "%s" "$index" | grep -Eo '/assets/index-[^"]+\.css' | head -n 1)"
test -n "$js_asset"
test -n "$css_asset"
curl -fsS --max-time 20 "${LIVE_URL}${js_asset}" >/dev/null
curl -fsS --max-time 20 "${LIVE_URL}${css_asset}" >/dev/null
echo "Frontend assets served: ${js_asset}, ${css_asset}"
- name: Show production containers
if: always()
run: |
docker run --rm \
-v "${DEPLOY_PATH}:/workspace/vtube-awards" \
-v /var/run/docker.sock:/var/run/docker.sock \
-w /workspace/vtube-awards \
docker:cli \
sh -lc 'docker compose ps'
-204
View File
@@ -1,204 +0,0 @@
name: Deploy Production
run-name: Deploy VTubeAwards by @${{ gitea.actor }}
concurrency:
group: vtubeawards-production
cancel-in-progress: false
on:
workflow_run:
workflows: ["CI - Build & Verify"]
types: [completed]
branches: [main]
workflow_dispatch:
inputs:
git_ref:
description: 'Git ref to deploy'
required: false
default: 'main'
type: string
no_cache:
description: 'Build Docker images without cache'
required: false
default: false
type: boolean
jobs:
deploy:
name: Deploy to award.noveria.net
runs-on: ubuntu-latest
timeout-minutes: 30
if: |
github.event_name == 'workflow_dispatch' ||
(github.event_name == 'workflow_run' &&
github.event.workflow_run.conclusion == 'success' &&
!contains(github.event.workflow_run.head_commit.message, '[skip ci]'))
env:
DEPLOY_PATH: /home/projekte_bao/vtuber-awards
DEPLOY_APP_PATH: /home/projekte_bao/vtuber-awards/app
COMPOSE_PROJECT: vtuber-awards
DB_NETWORK: vtuber-awards_internal
LIVE_URL: https://award.noveria.net
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.event_name == 'workflow_dispatch' && inputs.git_ref || 'main' }}
fetch-depth: 0
- name: Resolve deploy metadata
id: meta
run: |
set -euo pipefail
echo "sha=$(git rev-parse HEAD)" >> "$GITEA_OUTPUT"
echo "short_sha=$(git rev-parse --short HEAD)" >> "$GITEA_OUTPUT"
echo "Deploying $(git rev-parse --short HEAD)"
git log -1 --oneline
- name: Verify production host layout
run: |
set -euo pipefail
docker run --rm \
-v "${DEPLOY_PATH}:/deploy:ro" \
alpine:latest \
sh -lc '
test -f /deploy/compose.yaml
test -f /deploy/deploy/backend.Dockerfile
test -f /deploy/deploy/web.Dockerfile
test -d /deploy/app
'
- name: Sync code to production app directory
run: |
set -euo pipefail
docker run --rm \
-v "${{ gitea.workspace }}:/src:ro" \
-v "${DEPLOY_APP_PATH}:/dest" \
alpine:latest \
sh -lc '
set -e
dest_owner="$(stat -c "%u:%g" /dest)"
find /dest -mindepth 1 -maxdepth 1 ! -name .git -exec rm -rf {} +
cd /src
find . -mindepth 1 -maxdepth 1 ! -name .git -exec cp -R {} /dest/ \;
chown -R "$dest_owner" /dest
'
- name: Build Docker images
run: |
set -euo pipefail
no_cache="${{ github.event_name == 'workflow_dispatch' && inputs.no_cache || false }}"
build_args=""
if [ "$no_cache" = "true" ]; then
build_args="--no-cache"
fi
docker run --rm \
-v "${DEPLOY_PATH}:/workspace/vtube-awards" \
-v /var/run/docker.sock:/var/run/docker.sock \
-w /workspace/vtube-awards \
docker:cli \
sh -lc "docker compose config >/dev/null && docker compose build ${build_args} api web"
- name: Backup database before migrations
run: |
set -euo pipefail
docker run --rm \
-v "${DEPLOY_PATH}:/workspace/vtube-awards" \
-v /var/run/docker.sock:/var/run/docker.sock \
-w /workspace/vtube-awards \
docker:cli \
sh -lc '
set -e
mkdir -p backups
stamp="$(date -u +%Y%m%dT%H%M%SZ)"
backup="backups/predeploy-${{ steps.meta.outputs.short_sha }}-${stamp}.dump"
docker compose exec -T postgres sh -lc '"'"'pg_dump -U "$POSTGRES_USER" -d "$POSTGRES_DB" -Fc'"'"' > "$backup"
test -s "$backup"
find backups -name "predeploy-*.dump" -mtime +14 -delete
echo "Database backup written to $backup"
'
- name: Apply EF Core migrations
run: |
set -euo pipefail
docker run --rm \
-e DEPLOY_APP_PATH="${DEPLOY_APP_PATH}" \
-e DB_NETWORK="${DB_NETWORK}" \
-v "${DEPLOY_PATH}:/workspace/vtube-awards" \
-v /var/run/docker.sock:/var/run/docker.sock \
-w /workspace/vtube-awards \
docker:cli \
sh -lc '
set -e
db="$(docker compose exec -T postgres printenv POSTGRES_DB)"
user="$(docker compose exec -T postgres printenv POSTGRES_USER)"
password="$(docker compose exec -T postgres printenv POSTGRES_PASSWORD)"
docker run --rm \
--network "${DB_NETWORK}" \
-v "${DEPLOY_APP_PATH}/Backend:/src/Backend" \
-w /src/Backend \
-e "VTSA_POSTGRES=Host=postgres;Port=5432;Database=${db};Username=${user};Password=${password}" \
mcr.microsoft.com/dotnet/sdk:8.0-alpine \
sh -lc "dotnet tool install --global dotnet-ef >/dev/null && export PATH=\$PATH:/root/.dotnet/tools && dotnet restore >/dev/null && dotnet ef database update"
'
- name: Restart production services
run: |
set -euo pipefail
docker run --rm \
-v "${DEPLOY_PATH}:/workspace/vtube-awards" \
-v /var/run/docker.sock:/var/run/docker.sock \
-w /workspace/vtube-awards \
docker:cli \
sh -lc 'docker compose up -d --wait --force-recreate api web'
- name: Verify live health
run: |
set -euo pipefail
retry() {
label="$1"
url="$2"
attempts=10
wait=3
for attempt in $(seq 1 "$attempts"); do
if body="$(curl -fsS --max-time 15 "$url")"; then
echo "$label OK on attempt $attempt"
echo "$body"
return 0
fi
echo "$label failed on attempt $attempt/$attempts; waiting ${wait}s"
sleep "$wait"
done
echo "$label failed after $attempts attempts"
return 1
}
retry "API health" "${LIVE_URL}/api/health"
db_body="$(curl -fsS --max-time 15 "${LIVE_URL}/api/health/database")"
echo "$db_body"
echo "$db_body" | grep -q '"canConnect":true'
echo "$db_body" | grep -q '"pendingMigrations":\[\]'
- name: Verify frontend assets
run: |
set -euo pipefail
index="$(curl -fsS --max-time 15 "${LIVE_URL}/")"
js_asset="$(printf "%s" "$index" | grep -Eo '/assets/index-[^"]+\.js' | head -n 1)"
css_asset="$(printf "%s" "$index" | grep -Eo '/assets/index-[^"]+\.css' | head -n 1)"
test -n "$js_asset"
test -n "$css_asset"
curl -fsS --max-time 20 "${LIVE_URL}${js_asset}" >/dev/null
curl -fsS --max-time 20 "${LIVE_URL}${css_asset}" >/dev/null
echo "Frontend assets served: ${js_asset}, ${css_asset}"
- name: Show production containers
if: always()
run: |
docker run --rm \
-v "${DEPLOY_PATH}:/workspace/vtube-awards" \
-v /var/run/docker.sock:/var/run/docker.sock \
-w /workspace/vtube-awards \
docker:cli \
sh -lc 'docker compose ps'