fix(ci): use docker:// runner mode with pre-built SDK images

Switch CI jobs to container: image: overrides so jobs run inside official
SDK containers (dotnet/sdk:10.0, node:20-alpine) instead of the bare
runner container. This bypasses blocked CDN downloads for dotnet/node.

Deploy job stays on self-hosted:host where Docker CLI is available.
Update workflow comments to explain the required runner label config:
  ubuntu-latest:docker://node:20-alpine (CI jobs)
  self-hosted:host (deploy)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-05-28 11:27:49 +03:30
parent dcddcf77d6
commit 6c868f5f30
+15 -18
View File
@@ -12,16 +12,26 @@ concurrency:
cancel-in-progress: true cancel-in-progress: true
# ───────────────────────────────────────────────────────────────────────────── # ─────────────────────────────────────────────────────────────────────────────
# CI — runs on every push AND every PR # HOW THIS WORKS
# Uses pre-built container images so nothing is downloaded from blocked CDNs.
# ───────────────────────────────────────────────────────────────────────────── # ─────────────────────────────────────────────────────────────────────────────
# Runner labels (in gitea docker-compose):
# ubuntu-latest:docker://node:20-alpine ← CI jobs run in real Docker containers
# self-hosted:host ← deploy runs directly on the server
#
# With docker:// labels:
# - container: image: overrides the base image for the job ✅
# - services: creates sidecar containers on the same network ✅
# - workspace is properly mounted into the container ✅
# - No need for actions/setup-dotnet or actions/setup-node ✅
# ─────────────────────────────────────────────────────────────────────────────
jobs: jobs:
# ── Main API ──────────────────────────────────────────────────────────────── # ── Main API ────────────────────────────────────────────────────────────────
api-build: api-build:
name: "CI · API (dotnet build + test)" name: "CI · API (dotnet build + test)"
runs-on: ubuntu-latest runs-on: ubuntu-latest
# Use official .NET SDK image — no setup-dotnet download needed # .NET SDK baked into the image — no internet download needed
container: container:
image: mcr.microsoft.com/dotnet/sdk:10.0 image: mcr.microsoft.com/dotnet/sdk:10.0
services: services:
@@ -139,8 +149,7 @@ jobs:
# ───────────────────────────────────────────────────────────────────────────── # ─────────────────────────────────────────────────────────────────────────────
# DEPLOY — only on push to main, only if ALL CI jobs pass. # DEPLOY — only on push to main, only if ALL CI jobs pass.
# MUST run on self-hosted: needs Docker CLI on the actual production server. # self-hosted:host — runs directly on your server where Docker is installed.
# ubuntu-latest containers do not have Docker inside them.
# ───────────────────────────────────────────────────────────────────────────── # ─────────────────────────────────────────────────────────────────────────────
deploy: deploy:
name: "Deploy · all services" name: "Deploy · all services"
@@ -158,24 +167,17 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
# Write .env from Gitea secret
# Set it at: Gitea repo → Settings → Secrets → Actions → Add Secret
# Name: ENV_FILE
# Value: your full .env file content
- name: Write .env - name: Write .env
run: printf '%s' "$ENV_FILE" > .env run: printf '%s' "$ENV_FILE" > .env
env: env:
ENV_FILE: ${{ secrets.ENV_FILE }} ENV_FILE: ${{ secrets.ENV_FILE }}
# ── Build main services ─────────────────────────────────────────────────
- name: Build main images (api, web, website, finder) - name: Build main images (api, web, website, finder)
run: | run: docker compose build --parallel api web website finder
docker compose build --parallel api web website finder
env: env:
DOCKER_BUILDKIT: 1 DOCKER_BUILDKIT: 1
COMPOSE_DOCKER_CLI_BUILD: 1 COMPOSE_DOCKER_CLI_BUILD: 1
# ── Build admin services (separate compose file) ────────────────────────
- name: Build admin images (admin-api, admin-web) - name: Build admin images (admin-api, admin-web)
run: | run: |
docker compose \ docker compose \
@@ -186,7 +188,6 @@ jobs:
DOCKER_BUILDKIT: 1 DOCKER_BUILDKIT: 1
COMPOSE_DOCKER_CLI_BUILD: 1 COMPOSE_DOCKER_CLI_BUILD: 1
# ── Start / restart main services ───────────────────────────────────────
- name: Start main services - name: Start main services
run: | run: |
docker compose up -d \ docker compose up -d \
@@ -194,7 +195,6 @@ jobs:
--no-deps \ --no-deps \
postgres redis api web website finder postgres redis api web website finder
# ── Start / restart admin services ──────────────────────────────────────
- name: Start admin services - name: Start admin services
run: | run: |
docker compose \ docker compose \
@@ -204,10 +204,8 @@ jobs:
--no-deps \ --no-deps \
admin-api admin-web admin-api admin-web
# ── Health checks ────────────────────────────────────────────────────────
- name: Wait for main API healthy - name: Wait for main API healthy
run: | run: |
echo "Waiting for meezi-api..."
for i in $(seq 1 24); do for i in $(seq 1 24); do
STATUS=$(docker inspect --format='{{.State.Health.Status}}' meezi-api 2>/dev/null || echo "missing") STATUS=$(docker inspect --format='{{.State.Health.Status}}' meezi-api 2>/dev/null || echo "missing")
echo " [$i/24] $STATUS" echo " [$i/24] $STATUS"
@@ -218,7 +216,6 @@ jobs:
- name: Wait for admin API healthy - name: Wait for admin API healthy
run: | run: |
echo "Waiting for meezi-admin-api..."
for i in $(seq 1 24); do for i in $(seq 1 24); do
STATUS=$(docker inspect --format='{{.State.Health.Status}}' meezi-admin-api 2>/dev/null || echo "missing") STATUS=$(docker inspect --format='{{.State.Health.Status}}' meezi-admin-api 2>/dev/null || echo "missing")
echo " [$i/24] $STATUS" echo " [$i/24] $STATUS"