# aibook-fun — CI/CD: build the Next.js marketing site, push to reg.mini.dev, # pin the image ref in Infisical, then trigger a pull + bounce on the droplet. # # Runs on Gitea Actions (GitHub-compatible syntax). This repo has Actions # enabled; the runner must be labeled `ubuntu-latest`. # # Repository secrets required (Settings → Actions → Secrets): # MINIMUS_TOKEN reg.mini.dev registry token (u: minimus) # INFISICAL_CLIENT_ID Infisical Machine Identity Client ID # INFISICAL_CLIENT_SECRET Infisical Machine Identity Client Secret # INFISICAL_PROJECT_ID the /AIBook-Fun project ID # DROPLET_IP droplet reserved IP (or resolve via DNS) # DROPLET_SSH_KEY private SSH key for root@ on the droplet # # NOTE: first provision is NOT done here — it's terraform + ansible converge # (deploy/docker-compose/terraform + ansible/deploy.yml). This workflow handles # everything after the site exists: every main-branch push to app/deploy code # rebuilds and rolls forward idempotently. name: deploy on: push: branches: [main] paths: - 'marketing-site/**' - 'deploy/**' - '.gitea/workflows/**' workflow_dispatch: permissions: contents: read concurrency: group: aibook-fun-deploy cancel-in-progress: false env: REGISTRY: reg.mini.dev IMAGE_REPO: weownai/aibook-fun-marketing INFISICAL_ENV: prod INFISICAL_PATH: /AIBook-Fun jobs: build-and-deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Build image run: | docker build \ --pull \ -f deploy/docker-compose/docker/Dockerfile \ -t "${{ env.REGISTRY }}/${{ env.IMAGE_REPO }}:${{ github.sha }}" \ -t "${{ env.REGISTRY }}/${{ env.IMAGE_REPO }}:latest" \ marketing-site/ - name: Login to reg.mini.dev run: echo "${{ secrets.MINIMUS_TOKEN }}" | docker login "${{ env.REGISTRY }}" -u minimus --password-stdin - name: Push image run: | docker push "${{ env.REGISTRY }}/${{ env.IMAGE_REPO }}:${{ github.sha }}" docker push "${{ env.REGISTRY }}/${{ env.IMAGE_REPO }}:latest" - name: Pin AIBOOK_IMAGE in Infisical env: INFISICAL_UNIVERSAL_AUTH_CLIENT_ID: ${{ secrets.INFISICAL_CLIENT_ID }} INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET: ${{ secrets.INFISICAL_CLIENT_SECRET }} run: | sudo curl -1sLf 'https://artifacts-cli.infisical.com/setup.deb.sh' | sudo -E bash sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -qq infisical >/dev/null export INFISICAL_TOKEN="$(infisical login --method=universal-auth --plain --silent)" infisical secrets set \ AIBOOK_IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_REPO }}:${{ github.sha }}" \ --projectId="${{ secrets.INFISICAL_PROJECT_ID }}" \ --env="${{ env.INFISICAL_ENV }}" \ --path="${{ env.INFISICAL_PATH }}" - name: Deploy to droplet (pull + up) uses: appleboy/ssh-action@v1 with: host: ${{ secrets.DROPLET_IP }} username: root key: ${{ secrets.DROPLET_SSH_KEY }} command_timeout: "15m" script: | set -euo pipefail cd /opt/aibook_fun # Droplet-side Infisical auth (auth file written by cloud-init). source .infisical-auth.env export INFISICAL_UNIVERSAL_AUTH_CLIENT_ID="$INFISICAL_CLIENT_ID" export INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET="$INFISICAL_CLIENT_SECRET" export INFISICAL_TOKEN="$(infisical login --method=universal-auth --plain --silent)" # Re-auth reg.mini.dev if the docker login from ansible has lapsed. MINIMUS_TOKEN="$(infisical secrets get MINIMUS_TOKEN --projectId="$INFISICAL_PROJECT_ID" --env=prod --path=/AIBook-Fun --plain || true)" if [ -n "${MINIMUS_TOKEN:-}" ]; then printf '%s' "$MINIMUS_TOKEN" | docker login reg.mini.dev -u minimus --password-stdin fi # AIBOOK_IMAGE is injected by `infisical run`; also export it so the # ${AIBOOK_IMAGE:?} compose ref resolves even without run-injection. export AIBOOK_IMAGE="$(infisical secrets get AIBOOK_IMAGE --projectId="$INFISICAL_PROJECT_ID" --env=prod --path=/AIBook-Fun --plain)" infisical run --projectId="$INFISICAL_PROJECT_ID" --env=prod --path=/AIBook-Fun \ -- docker compose up -d --remove-orphans