- .gitea/workflows/deploy.yml: trigger on pull_request to main so PRs
get test feedback; gate build-and-push + deploy on push events so
PRs only run the test jobs (no registry push, no SSH deploy).
- docker-compose.yml: change `${POSTGRES_PASSWORD:-mangalord}` to
`${POSTGRES_PASSWORD:?...}` so a deploy without an .env fails fast
instead of booting Postgres with a known-default credential.
- .env.example: change the example value to a "change-me" sentinel,
add a banner explaining that production needs HTTPS in front of
the frontend container because COOKIE_SECURE=true makes browsers
refuse cookies over plain HTTP.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
152 lines
5.1 KiB
YAML
152 lines
5.1 KiB
YAML
name: deploy
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
pull_request:
|
|
branches: [main]
|
|
workflow_dispatch:
|
|
|
|
jobs:
|
|
test-backend:
|
|
runs-on: ubuntu-latest
|
|
container:
|
|
image: rust:1-slim
|
|
services:
|
|
postgres:
|
|
image: postgres:16-alpine
|
|
env:
|
|
POSTGRES_USER: mangalord
|
|
POSTGRES_PASSWORD: mangalord
|
|
POSTGRES_DB: mangalord
|
|
options: >-
|
|
--health-cmd "pg_isready -U mangalord"
|
|
--health-interval 5s
|
|
--health-timeout 5s
|
|
--health-retries 10
|
|
env:
|
|
DATABASE_URL: postgres://mangalord:mangalord@postgres:5432/mangalord
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- name: Install build deps
|
|
run: |
|
|
apt-get update
|
|
apt-get install -y --no-install-recommends pkg-config libssl-dev ca-certificates
|
|
- name: Cache cargo registry and target
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: |
|
|
~/.cargo/registry
|
|
~/.cargo/git
|
|
backend/target
|
|
key: cargo-${{ runner.os }}-${{ hashFiles('backend/Cargo.lock') }}
|
|
restore-keys: |
|
|
cargo-${{ runner.os }}-
|
|
- name: cargo test
|
|
working-directory: backend
|
|
run: cargo test --locked
|
|
|
|
test-frontend:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: '22'
|
|
cache: npm
|
|
cache-dependency-path: frontend/package-lock.json
|
|
- name: npm ci
|
|
working-directory: frontend
|
|
run: npm ci
|
|
- name: vitest
|
|
working-directory: frontend
|
|
run: npm test
|
|
|
|
build-and-push:
|
|
runs-on: ubuntu-latest
|
|
needs: [test-backend, test-frontend]
|
|
# PRs only run the test jobs; build + deploy are reserved for
|
|
# post-merge pushes to main. Without this gate every PR would push
|
|
# a tagged image to the registry and SSH-deploy to prod.
|
|
if: github.event_name != 'pull_request'
|
|
outputs:
|
|
image_tag: ${{ steps.meta.outputs.image_tag }}
|
|
version: ${{ steps.meta.outputs.version }}
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Resolve image tags
|
|
id: meta
|
|
run: |
|
|
version="$(grep -m1 '^version' backend/Cargo.toml | cut -d'"' -f2)"
|
|
frontend_version="$(grep -m1 '"version"' frontend/package.json | cut -d'"' -f4)"
|
|
if [ "$version" != "$frontend_version" ]; then
|
|
echo "Version mismatch: backend=$version frontend=$frontend_version" >&2
|
|
exit 1
|
|
fi
|
|
echo "image_tag=${GITHUB_SHA}" >> "$GITHUB_OUTPUT"
|
|
echo "version=${version}" >> "$GITHUB_OUTPUT"
|
|
|
|
- uses: docker/setup-buildx-action@v3
|
|
|
|
- name: docker login
|
|
uses: docker/login-action@v3
|
|
with:
|
|
registry: ${{ secrets.REGISTRY_URL }}
|
|
username: ${{ secrets.REGISTRY_USERNAME }}
|
|
password: ${{ secrets.REGISTRY_PASSWORD }}
|
|
|
|
- name: Build & push backend
|
|
uses: docker/build-push-action@v5
|
|
with:
|
|
context: ./backend
|
|
push: true
|
|
tags: |
|
|
${{ secrets.REGISTRY_URL }}/mangalord-backend:latest
|
|
${{ secrets.REGISTRY_URL }}/mangalord-backend:${{ steps.meta.outputs.image_tag }}
|
|
${{ secrets.REGISTRY_URL }}/mangalord-backend:${{ steps.meta.outputs.version }}
|
|
cache-from: type=gha,scope=backend
|
|
cache-to: type=gha,mode=max,scope=backend
|
|
|
|
- name: Build & push frontend
|
|
uses: docker/build-push-action@v5
|
|
with:
|
|
context: ./frontend
|
|
push: true
|
|
tags: |
|
|
${{ secrets.REGISTRY_URL }}/mangalord-frontend:latest
|
|
${{ secrets.REGISTRY_URL }}/mangalord-frontend:${{ steps.meta.outputs.image_tag }}
|
|
${{ secrets.REGISTRY_URL }}/mangalord-frontend:${{ steps.meta.outputs.version }}
|
|
cache-from: type=gha,scope=frontend
|
|
cache-to: type=gha,mode=max,scope=frontend
|
|
|
|
deploy:
|
|
runs-on: ubuntu-latest
|
|
needs: build-and-push
|
|
if: github.event_name != 'pull_request'
|
|
steps:
|
|
- name: SSH deploy
|
|
uses: appleboy/ssh-action@v1.0.3
|
|
with:
|
|
host: ${{ secrets.SSH_HOST }}
|
|
username: ${{ secrets.SSH_USER }}
|
|
key: ${{ secrets.SSH_PRIVATE_KEY }}
|
|
port: ${{ secrets.SSH_PORT || 22 }}
|
|
envs: REGISTRY_URL,REGISTRY_USERNAME,REGISTRY_PASSWORD,IMAGE_TAG,DEPLOY_PATH
|
|
script_stop: true
|
|
script: |
|
|
set -euo pipefail
|
|
cd "$DEPLOY_PATH"
|
|
echo "$REGISTRY_PASSWORD" | docker login "$REGISTRY_URL" -u "$REGISTRY_USERNAME" --password-stdin
|
|
export REGISTRY_URL IMAGE_TAG
|
|
docker compose -f docker-compose.yml -f docker-compose.prod.yml pull
|
|
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
|
|
docker image prune -f
|
|
docker logout "$REGISTRY_URL"
|
|
env:
|
|
REGISTRY_URL: ${{ secrets.REGISTRY_URL }}
|
|
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
|
|
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
|
|
IMAGE_TAG: ${{ needs.build-and-push.outputs.image_tag }}
|
|
DEPLOY_PATH: ${{ vars.DEPLOY_PATH }}
|