asxpio/.forgejo/workflows/deploy.yaml
Sergei Poljanski 3ab8ebcf3a
Some checks failed
Build and Deploy to Production / test (push) Successful in 22s
Build and Deploy to Production / build (push) Failing after 1s
Build and Deploy to Production / deploy (push) Has been skipped
ci: stop using FORGEJO_* secret names
Forgejo reserves names starting with FORGEJO_, and since runner v13
secrets.FORGEJO_TOKEN, _USER and _REGISTRY no longer reach the job as
the values set on the repository: Kaniko logged in with the wrong
credentials and every image push failed with UNAUTHORIZED.

The registry host and user are not secret (they are in every image
name), so write them in the workflow; the token comes from FJ_TOKEN,
as in datacenterbingo.

Assisted-by: Claude:opus-5-5
2026-11-13 01:47:28 +04:00

202 lines
7.2 KiB
YAML

name: Build and Deploy to Production
on:
push:
branches:
- main
jobs:
# Same job as in test.yaml (which covers non-main branches); duplicated here
# so a red suite blocks the build+deploy — Forgejo has no cross-workflow needs.
test:
runs-on: nix-latest
# Settings via env, not /etc/nix/nix.conf: in the image that is a
# symlink into /nix/store, which the runner's nix-store volume
# shadows, so it dangles once the image moves on.
env:
NIX_CONFIG: |
experimental-features = nix-command flakes
build-users-group =
sandbox = false
services:
postgres:
image: postgres:17-alpine
env:
POSTGRES_USER: postgres
POSTGRES_HOST_AUTH_METHOD: trust
POSTGRES_DB: asxpio_test
steps:
- name: Prepare container for actions
run: |
# node for JS actions (checkout); git-minimal is already in the image
nix-env -iA nixpkgs.nodejs_22
- name: Checkout
uses: actions/checkout@v4
- name: Cache gems
uses: actions/cache@v4
with:
path: .gems
key: gems-${{ hashFiles('Gemfile.lock') }}
- name: Wait for Postgres
run: |
for i in $(seq 1 30); do
nix develop -c pg_isready -h postgres -U postgres && exit 0
sleep 2
done
echo "Postgres service never became ready" >&2
exit 1
- name: Run test suite
env:
TEST_DATABASE_URL: postgres://postgres@postgres:5432/asxpio_test
run: nix develop -c bundle exec rake test
build:
runs-on: arch-latest
needs: test
container:
image: gcr.io/kaniko-project/executor:debug
outputs:
image_tag: ${{ steps.build_image.outputs.image_tag }}
steps:
- name: Build and push Docker Image using Kaniko
id: build_image
run: |
# Tags
SHORT_SHA=${GITHUB_SHA::8}
IMAGE_BASE=git.czsk.it/asxpi/asxpio
IMAGE_TAG=${IMAGE_BASE}:${SHORT_SHA}
IMAGE_LATEST=${IMAGE_BASE}:latest
# Auth Conf
mkdir -p /kaniko/.docker
echo "{\"auths\":{\"git.czsk.it\":{\"auth\":\"$(echo -n asxpi:${{ secrets.FJ_TOKEN }} | base64)\"}}}" > /kaniko/.docker/config.json
# Build & push
/kaniko/executor \
--context=git://git.czsk.it/${{ github.repository }}.git \
--git=branch=${{ github.ref_name }} \
--destination=$IMAGE_TAG \
--destination=$IMAGE_LATEST
# Output the specific tag for deployment
echo "image_tag=${IMAGE_TAG}" >> $GITHUB_OUTPUT
deploy:
runs-on: arch-latest
needs: build
steps:
- name: Install dependencies
run: |
pacman -Syu --noconfirm nodejs openssh rsync
- name: Checkout code
uses: actions/checkout@v4
- name: Deploy docker-compose.yml
uses: easingthemes/ssh-deploy@v5.1.0
with:
SSH_PRIVATE_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
REMOTE_HOST: ${{ secrets.DEPLOY_IP }}
REMOTE_USER: ${{ secrets.DEPLOY_USER }}
SOURCE: "docker-compose.yml"
TARGET: "/opt/asxpio/"
- name: Deploy .env from secrets
uses: appleboy/ssh-action@v1.2.3
env:
SESSION_SECRET: ${{ secrets.SESSION_SECRET }}
SMTP_PASSWORD: ${{ secrets.SMTP_PASSWORD }}
ADMIN_USER: ${{ secrets.ADMIN_USER }}
ADMIN_PASSWORD: ${{ secrets.ADMIN_PASSWORD }}
ASXPIO_DB_PASSWORD: ${{ secrets.ASXPIO_DB_PASSWORD }}
ASXPIO_S3_ACCESS_KEY: ${{ secrets.ASXPIO_S3_ACCESS_KEY }}
ASXPIO_S3_SECRET_KEY: ${{ secrets.ASXPIO_S3_SECRET_KEY }}
LTC_ADDRESS: ${{ secrets.LTC_ADDRESS }}
CRYPTO_ADDRESSES: ${{ secrets.CRYPTO_ADDRESSES }}
INDEXNOW: ${{ secrets.INDEXNOW }}
with:
host: ${{ secrets.DEPLOY_IP }}
username: ${{ secrets.DEPLOY_USER }}
key: ${{ secrets.DEPLOY_SSH_KEY }}
envs: SESSION_SECRET,SMTP_PASSWORD,ADMIN_USER,ADMIN_PASSWORD,ASXPIO_DB_PASSWORD,ASXPIO_S3_ACCESS_KEY,ASXPIO_S3_SECRET_KEY,LTC_ADDRESS,CRYPTO_ADDRESSES,INDEXNOW
script_stop: true
script: |
umask 077
mkdir -p /opt/asxpio
# Garage only takes its own key format (id: GK + 24 hex digits,
# secret: 64 hex digits). Hash the MinIO-era secrets into it, as
# storage's init/garage-bootstrap.sh does; a pair already in
# Garage's format is used as it is.
garage_key_id() {
if printf '%s' "$1" | grep -Eq '^GK[0-9a-f]{24}$'; then printf '%s' "$1"
else printf 'GK%s' "$(printf '%s' "$1" | sha256sum | cut -c1-24)"; fi
}
garage_secret() {
if printf '%s' "$1" | grep -Eq '^[0-9a-f]{64}$'; then printf '%s' "$1"
else printf '%s' "$1" | sha256sum | cut -c1-64; fi
}
S3_ACCESS_KEY="$(garage_key_id "${ASXPIO_S3_ACCESS_KEY}")"
S3_SECRET_KEY="$(garage_secret "${ASXPIO_S3_SECRET_KEY}")"
cat > /opt/asxpio/.env <<EOF
RACK_ENV=production
SESSION_SECRET=${SESSION_SECRET}
SMTP_ADDR=smtp.fastmail.com
SMTP_PORT=587
SMTP_USER=me@asxp.io
SMTP_PASSWORD=${SMTP_PASSWORD}
MAIL_FROM=IE Sergei Poljanski Contact Form <me@asxp.io>
MAIL_TO=ie@asxp.io
ADMIN_USER=${ADMIN_USER}
ADMIN_PASSWORD=${ADMIN_PASSWORD}
DATABASE_URL=postgres://asxpio:${ASXPIO_DB_PASSWORD}@postgres:5432/asxpio
S3_ENDPOINT=http://garage:3900
S3_PUBLIC_ENDPOINT=https://s3.asxp.io
S3_REGION=us-east-1
S3_BUCKET=asxpio-invoices
S3_ACCESS_KEY=${S3_ACCESS_KEY}
S3_SECRET_KEY=${S3_SECRET_KEY}
LTC_ADDRESS=${LTC_ADDRESS}
CRYPTO_ADDRESSES=${CRYPTO_ADDRESSES}
BINGO_HOST=datacenterbingo.xyz
BINGO_S3_BUCKET=datacenterbingo-photos
PUBLIC_S3_BUCKET=public
PUBLIC_BASE_URL=https://s3.asxp.io/public
INDEXNOW_KEY=${INDEXNOW}
EOF
chmod 600 /opt/asxpio/.env
- name: Deploy and update container
uses: appleboy/ssh-action@v1.2.3
env:
NEW_IMAGE: ${{ needs.build.outputs.image_tag }}
with:
host: ${{ secrets.DEPLOY_IP }}
username: ${{ secrets.DEPLOY_USER }}
key: ${{ secrets.DEPLOY_SSH_KEY }}
envs: NEW_IMAGE
script: |
cd /opt/asxpio
sed -i "s|image:.*|image: $NEW_IMAGE|" docker-compose.yml
docker pull $NEW_IMAGE
docker compose up -d
# Wait for the image HEALTHCHECK (GET /healthz) to report healthy;
# `docker ps | grep` passed even while the app crash-looped.
for i in $(seq 1 18); do
status=$(docker inspect --format '{{.State.Health.Status}}' asxpio 2>/dev/null || echo missing)
if [ "$status" = "healthy" ]; then
echo "Deployment successful!"
exit 0
fi
sleep 5
done
echo "asxpio never became healthy (last status: $status)" >&2
docker logs --tail 50 asxpio
exit 1