e4ad440c15
docker compose up --force-recreate only works when Compose owns the container. If the container was started outside Compose (e.g. manually via docker restart), Compose can't recreate it and errors with "container name already in use". Explicitly stopping and removing it first handles both cases cleanly. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
124 lines
4.4 KiB
YAML
124 lines
4.4 KiB
YAML
name: CI/CD
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
pull_request:
|
|
branches: [main]
|
|
|
|
concurrency:
|
|
group: drsousan-cicd-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
# HOW THIS WORKS
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
# Runner labels:
|
|
# ubuntu-latest → container runner ← CI dotnet build runs here
|
|
# self-hosted → host runner ← docker build/push/deploy runs here
|
|
#
|
|
# Local Nexus:
|
|
# Docker registry → mirror.soroushasadi.com (group: pull | host: push)
|
|
# NuGet → 171.22.25.73:8081/repository/nuget-group/
|
|
#
|
|
# Required Gitea secrets:
|
|
# ENV_FILE → contents of .env
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
jobs:
|
|
|
|
# ── CI: compile-check (runs on every push / PR) ──────────────────────────────
|
|
ci:
|
|
name: "CI · dotnet build"
|
|
runs-on: ubuntu-latest
|
|
container:
|
|
image: mirror.soroushasadi.com/dotnet/sdk:10.0
|
|
options: --add-host=gitea:host-gateway
|
|
steps:
|
|
- name: Checkout
|
|
env:
|
|
TOKEN: ${{ github.token }}
|
|
REF: ${{ github.ref }}
|
|
run: |
|
|
git init
|
|
git remote add origin "${{ github.server_url }}/${{ github.repository }}.git"
|
|
git config http.extraheader "Authorization: Bearer ${TOKEN}"
|
|
git fetch --depth=1 origin "${REF}"
|
|
git checkout FETCH_HEAD
|
|
|
|
- name: Restore
|
|
working-directory: DrSousan.Api
|
|
env:
|
|
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
|
run: dotnet restore DrSousan.Api.csproj
|
|
|
|
- name: Build
|
|
working-directory: DrSousan.Api
|
|
run: dotnet build DrSousan.Api.csproj --no-restore -c Release
|
|
|
|
# ── CD: build image → deploy locally (push to main only) ───────────────────
|
|
deploy:
|
|
name: "Deploy · drsousan"
|
|
runs-on: self-hosted
|
|
env:
|
|
PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin
|
|
REGISTRY: mirror.soroushasadi.com
|
|
IMAGE: mirror.soroushasadi.com/drsousan/api
|
|
needs: [ci]
|
|
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
timeout-minutes: 20
|
|
|
|
steps:
|
|
- name: Checkout
|
|
env:
|
|
TOKEN: ${{ github.token }}
|
|
REF: ${{ github.ref }}
|
|
run: |
|
|
git init
|
|
git remote add origin "${{ github.server_url }}/${{ github.repository }}.git"
|
|
git config http.extraheader "Authorization: Bearer ${TOKEN}"
|
|
git fetch --depth=1 origin "${REF}"
|
|
git checkout FETCH_HEAD
|
|
|
|
- name: Write .env
|
|
run: printf '%s' "$ENV_FILE" > .env
|
|
env:
|
|
ENV_FILE: ${{ secrets.ENV_FILE }}
|
|
|
|
- name: Build image
|
|
run: docker compose build api
|
|
env:
|
|
DOCKER_BUILDKIT: 1
|
|
|
|
- name: Deploy
|
|
run: |
|
|
docker stop drsousan_api 2>/dev/null || true
|
|
docker rm drsousan_api 2>/dev/null || true
|
|
docker compose up -d --no-deps api
|
|
|
|
- name: Wait for healthy
|
|
run: |
|
|
for i in $(seq 1 24); do
|
|
STATUS=$(docker inspect --format='{{.State.Health.Status}}' drsousan_api 2>/dev/null || echo "missing")
|
|
echo " [$i/24] $STATUS"
|
|
[ "$STATUS" = "healthy" ] && echo "✅ drsousan_api healthy" && exit 0
|
|
sleep 5
|
|
done
|
|
echo "❌ timed out"
|
|
docker compose logs --tail=60 api
|
|
exit 1
|
|
|
|
- name: Show containers
|
|
if: always()
|
|
run: docker compose ps
|
|
|
|
- name: Prune old drsousan images
|
|
if: success()
|
|
# Only remove untagged (dangling) drsousan images — never touches other projects
|
|
run: |
|
|
docker images --format '{{.Repository}}:{{.Tag}} {{.ID}}' \
|
|
| grep '^mirror\.soroushasadi\.com/drsousan/' \
|
|
| grep '<none>' \
|
|
| awk '{print $2}' \
|
|
| xargs -r docker rmi || true
|