Skip to content

Deploy

Deploy #7

Workflow file for this run

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