Deploy #7
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Deploy | |
| on: | |
| push: | |
| branches: [dev, main] | |
| workflow_dispatch: | |
| inputs: | |
| target: | |
| description: "배포 대상 (staging | prod)" | |
| required: true | |
| default: "staging" | |
| type: choice | |
| options: [staging, prod] | |
| # 동시 배포 금지. 같은 대상에 새 push 가 오면 in-progress 취소. | |
| concurrency: | |
| group: deploy-${{ github.ref_name }} | |
| cancel-in-progress: false | |
| permissions: | |
| contents: read | |
| packages: write # ghcr.io push 권한 | |
| env: | |
| REGISTRY: ghcr.io | |
| IMAGE: ${{ github.repository }} | |
| jobs: | |
| # --------------------------------------------------------------------------- | |
| # 1) build image → ghcr.io 푸시 | |
| # --------------------------------------------------------------------------- | |
| build: | |
| name: Build & push image | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| outputs: | |
| image_tag: ${{ steps.meta.outputs.image_tag }} | |
| target: ${{ steps.meta.outputs.target }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Resolve target + tag | |
| id: meta | |
| run: | | |
| # 우선순위: workflow_dispatch.inputs.target → branch | |
| if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then | |
| target="${{ inputs.target }}" | |
| elif [ "${{ github.ref_name }}" = "main" ]; then | |
| target="prod" | |
| else | |
| target="staging" | |
| fi | |
| sha_short="$(echo "${{ github.sha }}" | cut -c1-7)" | |
| image_tag="${target}-${sha_short}" | |
| echo "target=$target" >> "$GITHUB_OUTPUT" | |
| echo "image_tag=$image_tag" >> "$GITHUB_OUTPUT" | |
| echo "image_ref=${REGISTRY}/${IMAGE}:${image_tag}" >> "$GITHUB_OUTPUT" | |
| echo "image_ref_target=${REGISTRY}/${IMAGE}:${target}" >> "$GITHUB_OUTPUT" | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Login to GHCR | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Build & push | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| file: deploy/Dockerfile | |
| push: true | |
| tags: | | |
| ${{ steps.meta.outputs.image_ref }} | |
| ${{ steps.meta.outputs.image_ref_target }} | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| # --------------------------------------------------------------------------- | |
| # 2) N100 SSH secrets 준비 여부 확인. 미등록이면 이미지 build/push 까지만 하고 deploy 는 skip. | |
| # --------------------------------------------------------------------------- | |
| deploy-preflight: | |
| name: Check deploy secrets | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 3 | |
| outputs: | |
| deploy_ready: ${{ steps.secrets.outputs.deploy_ready }} | |
| steps: | |
| - name: Check N100 secrets | |
| id: secrets | |
| run: | | |
| if [ -n "$N100_HOST" ] && [ -n "$N100_USER" ] && [ -n "$N100_SSH_KEY" ]; then | |
| echo "deploy_ready=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "deploy_ready=false" >> "$GITHUB_OUTPUT" | |
| echo "::notice::N100 deploy secrets are not configured yet. Image build/push completed; SSH deploy is skipped." | |
| fi | |
| env: | |
| N100_HOST: ${{ secrets.N100_HOST }} | |
| N100_USER: ${{ secrets.N100_USER }} | |
| N100_SSH_KEY: ${{ secrets.N100_SSH_KEY }} | |
| # --------------------------------------------------------------------------- | |
| # 3) SSH 로 N100 접속 → docker compose pull && up -d | |
| # --------------------------------------------------------------------------- | |
| deploy: | |
| name: Deploy to ${{ needs.build.outputs.target }} | |
| needs: [build, deploy-preflight] | |
| if: needs.deploy-preflight.outputs.deploy_ready == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| # prod 는 환경(environment) 보호로 수동 승인 가능 | |
| environment: | |
| name: ${{ needs.build.outputs.target }} | |
| steps: | |
| - name: Pull & restart on N100 | |
| uses: appleboy/ssh-action@v1.0.3 | |
| with: | |
| host: ${{ secrets.N100_HOST }} | |
| username: ${{ secrets.N100_USER }} | |
| key: ${{ secrets.N100_SSH_KEY }} | |
| # 호스트의 deploy/ 디렉터리에서 pull → up. | |
| # IMAGE_TAG selects the pushed GHCR image; MURUN_ENV_FILE selects .env.staging/.env.prod. | |
| script: | | |
| set -e | |
| cd ${MURUN_DEPLOY_DIR:-~/murun-peterabcd/deploy} | |
| export IMAGE_TAG="${{ needs.build.outputs.image_tag }}" | |
| export MURUN_ENV_FILE=".env.${{ needs.build.outputs.target }}" | |
| on_error() { | |
| echo "::group::docker compose ps" | |
| docker compose -p murun-${{ needs.build.outputs.target }} --env-file "$MURUN_ENV_FILE" ps || true | |
| echo "::endgroup::" | |
| echo "::group::app logs" | |
| docker compose -p murun-${{ needs.build.outputs.target }} --env-file "$MURUN_ENV_FILE" logs --tail=160 app || true | |
| echo "::endgroup::" | |
| echo "::group::caddy logs" | |
| docker compose -p murun-${{ needs.build.outputs.target }} --env-file "$MURUN_ENV_FILE" logs --tail=80 caddy || true | |
| echo "::endgroup::" | |
| } | |
| trap on_error ERR | |
| docker compose -p murun-${{ needs.build.outputs.target }} --env-file "$MURUN_ENV_FILE" pull | |
| docker compose -p murun-${{ needs.build.outputs.target }} --env-file "$MURUN_ENV_FILE" up -d --remove-orphans | |
| docker image prune -f |