Files
hamkadr/.gitea/workflows/ci-cd.yml
T
soroush.asadi 36612b6bf0
CI/CD / CI · dotnet build (push) Failing after 42s
CI/CD / Deploy · hamkadr (push) Has been skipped
CI/Docker NuGet: Nexus nuget-group primary + Liara fallback
Both the CI restore (/tmp/nuget.ci.config) and the Docker image build
(nuget.docker.config) now use https://mirror.soroushasadi.com/repository/
nuget-group/ as the primary source with Liara as fallback, so a single
mirror returning 500 no longer breaks restore.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 19:44:57 +03:30

174 lines
7.3 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
name: CI/CD
on:
push:
branches: [main]
pull_request:
branches: [main]
concurrency:
group: hamkadr-cicd-${{ github.ref }}
cancel-in-progress: true
# ─────────────────────────────────────────────────────────────────────────────
# Runner labels (act_runner):
# ubuntu-latest:docker://node:20-alpine ← CI jobs run in real Docker containers
# self-hosted:host ← deploy runs directly on the server
# All images/packages via Nexus at mirror.soroushasadi.com.
# Required Gitea secret: ENV_FILE (contents of .env)
# ─────────────────────────────────────────────────────────────────────────────
jobs:
# ── CI: compile-check (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: Write NuGet config (Nexus group primary; Liara fallback)
# Nexus nuget-group is the primary mirror; Liara is kept as a fallback so a
# single mirror outage (e.g. a 500 on the service index) doesn't break restore.
run: |
cat > /tmp/nuget.ci.config << 'EOF'
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<add key="nexus"
value="https://mirror.soroushasadi.com/repository/nuget-group/index.json"
protocolVersion="3" />
<add key="liara"
value="https://package-mirror.liara.ir/repository/nuget/index.json"
protocolVersion="3" />
</packageSources>
<config>
<add key="http_retry_count" value="6" />
<add key="http_retry_delay_milliseconds" value="1000" />
</config>
</configuration>
EOF
- name: Restore
# NuGetAudit=false: the audit pings api.nuget.org for CVE data, which is
# filtered in Iran (100s timeout + NU1900 noise). The mirror has the packages.
run: dotnet restore src/JobsMedical.Web/JobsMedical.Web.csproj --configfile /tmp/nuget.ci.config -p:NuGetAudit=false
env:
DOTNET_CLI_TELEMETRY_OPTOUT: 1
- name: Build
run: dotnet build src/JobsMedical.Web/JobsMedical.Web.csproj --no-restore -c Release -p:NuGetAudit=false
# ── CD: build image → deploy on the server (push to main only) ────────────────
deploy:
name: "Deploy · hamkadr"
runs-on: self-hosted
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
needs: [ci]
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
timeout-minutes: 30
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: Tag current image for rollback (before rebuild)
run: |
if docker image inspect mirror.soroushasadi.com/hamkadr/api:latest >/dev/null 2>&1; then
docker tag mirror.soroushasadi.com/hamkadr/api:latest mirror.soroushasadi.com/hamkadr/api:rollback
echo "Tagged previous image → :rollback"
else
echo "No previous image — first deploy."
fi
- name: Back up database (if running)
run: |
set -a; . ./.env; set +a
if docker ps -q --filter name='^hamkadr_db$' | grep -q .; then
BACKUP_DIR="/opt/hamkadr-backups"; mkdir -p "$BACKUP_DIR"
STAMP=$(date +%Y%m%d-%H%M%S)
docker exec hamkadr_db pg_dump -U "${POSTGRES_USER:-hamkadr}" "${POSTGRES_DB:-hamkadr}" \
> "$BACKUP_DIR/hamkadr-$STAMP.sql" \
&& echo "✅ DB backed up → $BACKUP_DIR/hamkadr-$STAMP.sql" \
|| echo "⚠️ backup failed (non-fatal)"
ls -t "$BACKUP_DIR"/*.sql 2>/dev/null | tail -n +11 | xargs -r rm
else
echo "️ hamkadr_db not running — first deploy, nothing to back up."
fi
- name: Build image
run: docker compose build api
env:
DOCKER_BUILDKIT: 1
COMPOSE_DOCKER_CLI_BUILD: 1
- name: Start database
run: docker compose up -d --no-deps db
- name: Deploy app (stop + rm + up — reliable across docker versions)
run: |
docker stop hamkadr_api 2>/dev/null || true
docker rm hamkadr_api 2>/dev/null || true
docker compose up -d --no-deps api
- name: Wait for healthy
run: |
echo "Waiting for hamkadr_api (up to 3 min)..."
for i in $(seq 1 36); do
HEALTH=$(docker inspect --format='{{.State.Health.Status}}' hamkadr_api 2>/dev/null || echo "missing")
STATE=$(docker inspect --format='{{.State.Status}}' hamkadr_api 2>/dev/null || echo "missing")
RESTARTS=$(docker inspect --format='{{.RestartCount}}' hamkadr_api 2>/dev/null || echo "0")
echo " [$i/36] state=$STATE health=$HEALTH restarts=$RESTARTS"
[ "$HEALTH" = "healthy" ] && echo "✅ hamkadr_api healthy" && exit 0
if [ "$STATE" = "exited" ] || [ "$STATE" = "dead" ]; then
echo "❌ hamkadr_api crashed — logs:"; docker logs hamkadr_api 2>&1 | tail -120; exit 1
fi
if [ "$RESTARTS" -gt 1 ]; then
echo "❌ hamkadr_api crash-loop — logs:"; docker logs hamkadr_api 2>&1 | tail -120; exit 1
fi
[ "$i" = "36" ] && echo "❌ timeout" && docker logs hamkadr_api 2>&1 | tail -80 && exit 1
sleep 5
done
- name: Show containers
if: always()
run: docker compose ps
- name: Prune old hamkadr images
if: success()
# Only untagged (<none>) hamkadr images — never touches other projects.
run: |
docker images --format '{{.Repository}}:{{.Tag}} {{.ID}}' \
| grep '^mirror\.soroushasadi\.com/hamkadr/' \
| grep '<none>' \
| awk '{print $2}' \
| xargs -r docker rmi || true