name: CI/CD on: push: { branches: [main] } pull_request: { branches: [main] } concurrency: group: hamkadr-cicd-${{ github.ref }} cancel-in-progress: true jobs: # ---------------------------------------------------------------- CI build: name: "CI — dotnet build (Release)" 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: Write NuGet config (Nexus) run: | cat > /tmp/nuget.ci.config << 'EOF' EOF - name: Restore run: dotnet restore src/JobsMedical.Web/JobsMedical.Web.csproj --configfile /tmp/nuget.ci.config env: { DOTNET_CLI_TELEMETRY_OPTOUT: 1 } - name: Build run: dotnet build src/JobsMedical.Web/JobsMedical.Web.csproj --no-restore -c Release # ---------------------------------------------------------------- Deploy deploy: name: "Deploy — hamkadr (compose)" runs-on: self-hosted needs: [build] if: github.event_name == 'push' && github.ref == 'refs/heads/main' timeout-minutes: 40 env: # act host runner starts with a minimal PATH — extend so docker/snap resolve. PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin DOCKER_BUILDKIT: 1 COMPOSE_DOCKER_CLI_BUILD: 1 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: Back up database (if running) run: | set -a; . ./.env; set +a if docker ps -a --format '{{.Names}}' | grep -q '^hamkadr-db$'; then mkdir -p /opt/hamkadr-backups TS=$(date +%Y%m%d-%H%M%S) echo "Backing up DB → /opt/hamkadr-backups/hamkadr-$TS.sql" docker exec hamkadr-db pg_dump -U "$POSTGRES_USER" "$POSTGRES_DB" \ > "/opt/hamkadr-backups/hamkadr-$TS.sql" || echo "WARN: backup skipped (db not ready yet)" else echo "No existing db container — first deploy, nothing to back up." fi - name: Tag current image for rollback run: | if docker image inspect hamkadr-app:latest >/dev/null 2>&1; then docker tag hamkadr-app:latest hamkadr-app:rollback echo "Tagged hamkadr-app:rollback" fi - name: Build app image run: docker compose -f docker-compose.prod.yml build app - name: Start database run: docker compose -f docker-compose.prod.yml up -d --no-deps db - name: Recreate app (stop + rm + up — reliable across docker versions) run: | docker stop hamkadr-app 2>/dev/null || true docker rm hamkadr-app 2>/dev/null || true docker compose -f docker-compose.prod.yml up -d --no-deps app - name: Wait for app healthy run: | set -a; . ./.env; set +a for i in $(seq 1 24); do if curl -fsS "http://127.0.0.1:${APP_PORT}/healthz" >/dev/null 2>&1; then echo "OK — hamkadr-app healthy on 127.0.0.1:${APP_PORT}"; exit 0 fi echo " [$i/24] not ready yet…" sleep 5 done echo "TIMEOUT — dumping logs"; docker logs --tail=60 hamkadr-app; exit 1 - name: Prune dangling images if: success() run: docker image prune -f