Files
vending-machine-game/.gitea/workflows/ci-cd.yml
T
RyanandClaude Sonnet 5.5 9564880783
CI/CD / test (push) Successful in 21s
CI/CD / publish (push) Successful in 15s
CI/CD / deploy (push) Successful in 4s
Don't fail the publish job when the package is already linked
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-26 11:39:34 +03:30

141 lines
5.4 KiB
YAML

# Gitea Actions. While .gitea/workflows exists Gitea ignores .github/workflows (GitHub Pages only).
#
# Every push and pull request: install, test and build.
# Push to main: build the Docker image, push it to the Gitea registry, then start it with
# docker compose through the runner's Docker socket (the runner lives on the deployment server).
#
# Repository settings → Actions → Secrets:
# REGISTRY_USERNAME Gitea user that publishes images
# REGISTRY_TOKEN its personal access token with package Read and Write
# (the built-in GITEA_TOKEN cannot publish packages)
# Repository settings → Actions → Variables (optional):
# NPM_REGISTRY npm mirror used by the runner and the server's Docker build when
# registry.npmjs.org is unreachable, e.g. from servers in Iran
# HEALTH_URL URL checked after deploy, e.g. https://robotmarkets.ir/
name: CI/CD
on:
push:
branches: [main]
pull_request:
workflow_dispatch:
# Deploys run one at a time, in push order.
concurrency:
group: ci-cd-${{ github.ref }}
cancel-in-progress: false
# Fail within minutes instead of hanging when the registry is slow or blocked.
env:
NPM_CONFIG_REGISTRY: ${{ vars.NPM_REGISTRY || 'https://registry.npmjs.org/' }}
NPM_CONFIG_FETCH_RETRIES: '3'
NPM_CONFIG_FETCH_TIMEOUT: '60000'
jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 15
container:
image: node:22-bookworm
steps:
- uses: actions/checkout@v4
- name: Install
run: npm ci --no-audit --no-fund
- name: Test
run: node --test tests/*.test.js
- name: Build
run: npm run build
publish:
needs: test
if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
timeout-minutes: 30
outputs:
image: ${{ steps.push.outputs.image }}
steps:
- uses: actions/checkout@v4
- name: Build, push and link the image
id: push
shell: bash
env:
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
SOURCE_REPOSITORY: ${{ gitea.repository }}
COMMIT_SHA: ${{ gitea.sha }}
RUN_ID: ${{ gitea.run_id }}
run: |
set -euo pipefail
REGISTRY='git.my-rm.com'
OWNER="${SOURCE_REPOSITORY%%/*}"
REPO="${SOURCE_REPOSITORY#*/}"
IMAGE="$REGISTRY/${OWNER,,}/${REPO,,}:build-$(date -u +%Y%m%d-%H%M%S)-${RUN_ID}-${COMMIT_SHA:0:7}"
# Keep this job's registry credentials away from the host's own logins.
DOCKER_CONFIG="$(mktemp -d)"
export DOCKER_CONFIG
trap 'rm -rf "$DOCKER_CONFIG"' EXIT
printf '%s' "$REGISTRY_TOKEN" | docker login "$REGISTRY" --username "$REGISTRY_USERNAME" --password-stdin
docker build \
--build-arg "NPM_REGISTRY=$NPM_CONFIG_REGISTRY" \
--label "org.opencontainers.image.source=https://$REGISTRY/$SOURCE_REPOSITORY" \
--label "org.opencontainers.image.revision=$COMMIT_SHA" \
--tag "$IMAGE" .
docker push "$IMAGE"
# Show the package under this repository's Packages tab. Gitea answers 400 when the package
# is already linked (every run after the first), and the image is published either way.
status="$(curl --silent --show-error --output /dev/null --write-out '%{http_code}' --request POST \
--header "Authorization: token $REGISTRY_TOKEN" \
"https://$REGISTRY/api/v1/packages/$OWNER/container/${REPO,,}/-/link/$REPO")"
case "$status" in
201) echo "Linked the package to $REPO" ;;
*) echo "Package link request returned HTTP $status (already linked is normal); continuing" ;;
esac
echo "image=$IMAGE" >> "$GITEA_OUTPUT"
echo "Published and linked: $IMAGE"
# The runner shares the server's Docker daemon, so deploying is a local docker compose call:
# no SSH, and the daemon pulls with the credentials of this job's temporary docker login.
deploy:
needs: publish
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- name: Deploy the published image
shell: bash
env:
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
IMAGE: ${{ needs.publish.outputs.image }}
run: |
set -euo pipefail
DOCKER_CONFIG="$(mktemp -d)"
export DOCKER_CONFIG
trap 'rm -rf "$DOCKER_CONFIG"' EXIT
printf '%s' "$REGISTRY_TOKEN" | docker login git.my-rm.com --username "$REGISTRY_USERNAME" --password-stdin
docker compose -p vending-machine-game -f deploy/docker-compose.yml pull
docker compose -p vending-machine-game -f deploy/docker-compose.yml up -d --remove-orphans
docker compose -p vending-machine-game -f deploy/docker-compose.yml ps
docker image prune -f
- name: Check the site
if: vars.HEALTH_URL != ''
shell: bash
env:
HEALTH_URL: ${{ vars.HEALTH_URL }}
run: |
for attempt in $(seq 1 20); do
if curl -fsS -o /dev/null "$HEALTH_URL"; then
echo "Site is up: $HEALTH_URL"
exit 0
fi
sleep 6
done
echo "Site did not answer at $HEALTH_URL" >&2
exit 1