name: deploy on: workflow_run: workflows: [ci] branches: [main] types: [completed] workflow_dispatch: inputs: deploy_ref: description: "Commit already checked by successful main CI (main or SHA)" default: main required: true deploy_mode: description: "First deploy requires full; plan changes no production resources" type: choice options: [changed, full, plan] default: changed refresh_images: description: "Explicitly refresh mutable third-party Compose tags" type: boolean default: false permissions: contents: read actions: read concurrency: group: deploy-main cancel-in-progress: false env: DEPLOY_HOST: ${{ vars.DEPLOY_HOST || secrets.DEPLOY_HOST }} DEPLOY_PORT: ${{ vars.DEPLOY_PORT || secrets.DEPLOY_PORT }} DEPLOY_USER: ${{ vars.DEPLOY_USER || secrets.DEPLOY_USER }} DEPLOY_KEY: ${{ secrets.DEPLOY_SSH_KEY }} DEPLOY_KNOWN_HOSTS: ${{ vars.DEPLOY_KNOWN_HOSTS }} DEPLOY_RUN_ID: ${{ github.run_id }}-${{ github.run_attempt || 1 }} DEPLOY_MODE: ${{ inputs.deploy_mode || 'changed' }} REFRESH_IMAGES: ${{ inputs.refresh_images && 'true' || 'false' }} jobs: gate: if: >- github.ref == 'refs/heads/main' && (vars.AUTODEPLOY == 'true' || github.event_name == 'workflow_dispatch') && (github.event_name != 'workflow_run' || (github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.head_branch == 'main')) runs-on: homelab timeout-minutes: 10 outputs: sha: ${{ steps.release.outputs.sha }} steps: - name: Checkout repository uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 with: fetch-depth: 0 - name: Check successful CI and download the exact commit release id: release env: GITEA_TOKEN: ${{ github.token }} DEPLOY_REF: ${{ inputs.deploy_ref || 'main' }} EVENT_SHA: ${{ github.event.workflow_run.head_sha }} run: python3 .gitea/workflows/release.py gate --ref "$DEPLOY_REF" --event-sha "$EVENT_SHA" - name: Submit durable deploy to workstation run: bash .gitea/workflows/ssh-run.sh start - name: Write the request result if: always() env: REQUEST_RESULT: ${{ job.status }} CHECKED_SHA: ${{ steps.release.outputs.sha }} run: | if [ -n "${GITHUB_STEP_SUMMARY:-}" ]; then printf '## Deploy request\n\n- Result: **%s**\n- Checked commit: %s\n- Mode: %s\n' "$REQUEST_RESULT" "${CHECKED_SHA:-not checked}" "$DEPLOY_MODE" >>"$GITHUB_STEP_SUMMARY" if [ "$REQUEST_RESULT" != success ]; then echo 'Open the failed step log. If SSH submission failed, check the remote controller state.' >>"$GITHUB_STEP_SUMMARY" fi fi apply: needs: [gate] runs-on: homelab timeout-minutes: 120 steps: - name: Checkout checked commit uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 with: ref: ${{ needs.gate.outputs.sha }} - name: Follow validation and sequential Kubernetes / Compose apply run: bash .gitea/workflows/ssh-run.sh apply - name: Write the deploy result if: always() run: | if [ -f .gitea/workflows/ssh-run.sh ]; then bash .gitea/workflows/ssh-run.sh summary elif [ -n "${GITHUB_STEP_SUMMARY:-}" ]; then echo 'Source checkout failed. The remote deploy state is unknown. Check the job log.' >>"$GITHUB_STEP_SUMMARY" fi verify: needs: [gate, apply] if: always() && needs.gate.result == 'success' runs-on: homelab timeout-minutes: 130 steps: - name: Checkout checked commit uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 with: ref: ${{ needs.gate.outputs.sha }} - name: Follow workload verification and recovery run: bash .gitea/workflows/ssh-run.sh verify - name: Write the deploy result if: always() run: | if [ -f .gitea/workflows/ssh-run.sh ]; then bash .gitea/workflows/ssh-run.sh summary elif [ -n "${GITHUB_STEP_SUMMARY:-}" ]; then echo 'Source checkout failed. The remote deploy state is unknown. Check the job log.' >>"$GITHUB_STEP_SUMMARY" fi smoke: needs: [gate, verify] if: always() && needs.gate.result == 'success' runs-on: homelab timeout-minutes: 15 steps: - name: Checkout checked commit uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 with: ref: ${{ needs.gate.outputs.sha }} - name: Follow public route checks run: bash .gitea/workflows/ssh-run.sh smoke - name: Write the deploy result if: always() run: | if [ -f .gitea/workflows/ssh-run.sh ]; then bash .gitea/workflows/ssh-run.sh summary elif [ -n "${GITHUB_STEP_SUMMARY:-}" ]; then echo 'Source checkout failed. The remote deploy state is unknown. Check the job log.' >>"$GITHUB_STEP_SUMMARY" fi