The Gitea runner hung for over 30 minutes on `npm ci` because it cannot reach registry.npmjs.org. The registry is now configurable through the NPM_REGISTRY repository variable, passed on to the server's Docker build, and npm gives up after bounded retries. Jobs also have time limits (test 15 min, deploy 30 min) so the runner is never held forever. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
104 lines
4.2 KiB
YAML
104 lines
4.2 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: then deploy to the server over SSH and start it with docker compose.
|
|
#
|
|
# Repository settings → Actions → Secrets:
|
|
# DEPLOY_HOST server address (deploy is skipped while this is empty)
|
|
# DEPLOY_SSH_KEY private key whose public key is in the server user's ~/.ssh/authorized_keys
|
|
# DEPLOY_USER SSH user, default root; it must be able to run docker
|
|
# DEPLOY_PORT SSH port, default 22
|
|
# DEPLOY_KNOWN_HOSTS optional output of `ssh-keyscan <host>`; otherwise the host key is scanned on each run
|
|
# 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
|
|
# DEPLOY_PATH directory on the server, default vending-machine-game (in the user's home)
|
|
# SITE_ADDRESS domains served with HTTPS, default "robotmarkets.ir, www.robotmarkets.ir"
|
|
# 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
|
|
|
|
deploy:
|
|
needs: test
|
|
if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main'
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 30
|
|
container:
|
|
image: node:22-bookworm
|
|
env:
|
|
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
|
|
DEPLOY_USER: ${{ secrets.DEPLOY_USER }}
|
|
DEPLOY_PORT: ${{ secrets.DEPLOY_PORT }}
|
|
DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
|
|
DEPLOY_KNOWN_HOSTS: ${{ secrets.DEPLOY_KNOWN_HOSTS }}
|
|
DEPLOY_PATH: ${{ vars.DEPLOY_PATH }}
|
|
SITE_ADDRESS: ${{ vars.SITE_ADDRESS }}
|
|
HEALTH_URL: ${{ vars.HEALTH_URL }}
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- name: Deploy to the server
|
|
run: |
|
|
if [ -z "$DEPLOY_HOST" ] || [ -z "$DEPLOY_SSH_KEY" ]; then
|
|
echo "DEPLOY_HOST / DEPLOY_SSH_KEY secrets are not set: skipping deploy."
|
|
exit 0
|
|
fi
|
|
port="${DEPLOY_PORT:-22}"
|
|
target="${DEPLOY_USER:-root}@${DEPLOY_HOST}"
|
|
dir="${DEPLOY_PATH:-vending-machine-game}"
|
|
site="${SITE_ADDRESS:-robotmarkets.ir, www.robotmarkets.ir}"
|
|
mkdir -p ~/.ssh && chmod 700 ~/.ssh
|
|
printf '%s\n' "$DEPLOY_SSH_KEY" > ~/.ssh/deploy_key && chmod 600 ~/.ssh/deploy_key
|
|
if [ -n "$DEPLOY_KNOWN_HOSTS" ]; then
|
|
printf '%s\n' "$DEPLOY_KNOWN_HOSTS" > ~/.ssh/known_hosts
|
|
else
|
|
ssh-keyscan -p "$port" "$DEPLOY_HOST" > ~/.ssh/known_hosts 2>/dev/null
|
|
fi
|
|
# Ship the checked-out sources (no git history) and rebuild/restart the container there.
|
|
tar czf - --exclude=./.git --exclude=./node_modules --exclude=./dist . \
|
|
| ssh -i ~/.ssh/deploy_key -p "$port" "$target" \
|
|
"mkdir -p '$dir' && tar xzf - -C '$dir' && cd '$dir' && SITE_ADDRESS='$site' NPM_REGISTRY='$NPM_CONFIG_REGISTRY' docker compose up -d --build --remove-orphans && docker image prune -f"
|
|
|
|
- name: Check the site
|
|
if: env.HEALTH_URL != '' && env.DEPLOY_HOST != ''
|
|
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
|