188 lines
7.0 KiB
YAML
188 lines
7.0 KiB
YAML
name: TaxBaik CI/CD
|
|
|
|
on:
|
|
push:
|
|
branches:
|
|
- master
|
|
|
|
jobs:
|
|
build-and-deploy:
|
|
runs-on: ubuntu-latest
|
|
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Setup .NET
|
|
uses: actions/setup-dotnet@v4
|
|
with:
|
|
dotnet-version: '10.0'
|
|
|
|
- name: Restore dependencies
|
|
run: dotnet restore TaxBaik.sln
|
|
|
|
- name: Build solution
|
|
run: |
|
|
dotnet clean TaxBaik.sln -c Release
|
|
dotnet build TaxBaik.sln -c Release --no-restore
|
|
|
|
- name: Test solution
|
|
run: dotnet test TaxBaik.sln -c Release --no-build
|
|
|
|
- name: Publish Web (통합 앱)
|
|
run: dotnet publish TaxBaik.Web/ -c Release -o ./publish --no-restore
|
|
|
|
- name: Write production secrets
|
|
run: |
|
|
set -e
|
|
JWT_SECRET_KEY="${{ secrets.TAXBAIK_JWT_SECRET_KEY }}"
|
|
if [ -z "$JWT_SECRET_KEY" ]; then
|
|
echo "Missing TAXBAIK_JWT_SECRET_KEY secret" >&2
|
|
exit 1
|
|
fi
|
|
JWT_SECRET_KEY="$JWT_SECRET_KEY" python3 - <<'PY'
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
|
|
config = {
|
|
"Jwt": {
|
|
"SecretKey": os.environ["JWT_SECRET_KEY"]
|
|
}
|
|
}
|
|
|
|
Path("./publish/appsettings.Production.json").write_text(
|
|
json.dumps(config, ensure_ascii=False, indent=2),
|
|
encoding="utf-8",
|
|
)
|
|
PY
|
|
|
|
- name: Copy migrations to publish
|
|
run: |
|
|
cp -r db/migrations ./publish/migrations || true
|
|
|
|
- name: Generate build info
|
|
run: |
|
|
mkdir -p ./publish/wwwroot
|
|
COMMIT_HASH=$(git rev-parse --short HEAD)
|
|
BUILD_TIME=$(date -u +'%Y-%m-%d %H:%M:%S UTC')
|
|
echo "Version: $COMMIT_HASH" > ./publish/wwwroot/version.txt
|
|
echo "Built: $BUILD_TIME" >> ./publish/wwwroot/version.txt
|
|
echo "✓ Version: $COMMIT_HASH"
|
|
|
|
- name: Deploy (CI only, 통합 Web)
|
|
run: |
|
|
set -e
|
|
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
|
DEPLOY_HOME="/home/kjh2064"
|
|
DEPLOY_DIR="$DEPLOY_HOME/deployments/taxbaik_${TIMESTAMP}"
|
|
DEPLOY_HOST="${{ secrets.DEPLOY_HOST }}"
|
|
DEPLOY_USER="${{ secrets.DEPLOY_USER }}"
|
|
|
|
echo "=== Deploying TaxBaik v$(git rev-parse --short HEAD) ==="
|
|
mkdir -p ~/.ssh
|
|
SSH_KEY_B64="${{ secrets.DEPLOY_SSH_KEY_B64 }}"
|
|
SSH_KEY_RAW="${{ secrets.DEPLOY_SSH_KEY }}"
|
|
if [ -n "$SSH_KEY_B64" ]; then
|
|
printf '%s' "$SSH_KEY_B64" | base64 -d > ~/.ssh/id_ed25519
|
|
elif [ -n "$SSH_KEY_RAW" ]; then
|
|
if printf '%s' "$SSH_KEY_RAW" | grep -q 'BEGIN .*PRIVATE KEY'; then
|
|
printf '%b\n' "$SSH_KEY_RAW" > ~/.ssh/id_ed25519
|
|
else
|
|
printf '%s' "$SSH_KEY_RAW" | base64 -d > ~/.ssh/id_ed25519
|
|
fi
|
|
else
|
|
echo "Missing DEPLOY_SSH_KEY_B64 or DEPLOY_SSH_KEY secret" >&2
|
|
exit 1
|
|
fi
|
|
sed -i 's/\r$//' ~/.ssh/id_ed25519
|
|
chmod 600 ~/.ssh/id_ed25519
|
|
ssh-keyscan -H "$DEPLOY_HOST" >> ~/.ssh/known_hosts 2>/dev/null || true
|
|
|
|
tar -czf taxbaik_publish.tgz -C ./publish .
|
|
scp -i ~/.ssh/id_ed25519 -o StrictHostKeyChecking=yes taxbaik_publish.tgz "$DEPLOY_USER@$DEPLOY_HOST:/tmp/taxbaik_publish_${TIMESTAMP}.tgz"
|
|
ssh -i ~/.ssh/id_ed25519 -o StrictHostKeyChecking=yes "$DEPLOY_USER@$DEPLOY_HOST" "
|
|
set -e
|
|
mkdir -p '$DEPLOY_DIR'
|
|
tar -xzf '/tmp/taxbaik_publish_${TIMESTAMP}.tgz' -C '$DEPLOY_DIR'
|
|
rm -f '/tmp/taxbaik_publish_${TIMESTAMP}.tgz'
|
|
ln -sfn '$DEPLOY_DIR' '$DEPLOY_HOME/taxbaik_active'
|
|
sudo systemctl restart taxbaik
|
|
"
|
|
sleep 5
|
|
echo "✓ Deployed to $DEPLOY_HOST:$DEPLOY_DIR"
|
|
|
|
- name: Verify deployment
|
|
run: |
|
|
set -e
|
|
DEPLOY_HOST="${{ secrets.DEPLOY_HOST }}"
|
|
DEPLOY_USER="${{ secrets.DEPLOY_USER }}"
|
|
mkdir -p ~/.ssh
|
|
SSH_KEY_B64="${{ secrets.DEPLOY_SSH_KEY_B64 }}"
|
|
SSH_KEY_RAW="${{ secrets.DEPLOY_SSH_KEY }}"
|
|
if [ -n "$SSH_KEY_B64" ]; then
|
|
printf '%s' "$SSH_KEY_B64" | base64 -d > ~/.ssh/id_ed25519
|
|
elif [ -n "$SSH_KEY_RAW" ]; then
|
|
if printf '%s' "$SSH_KEY_RAW" | grep -q 'BEGIN .*PRIVATE KEY'; then
|
|
printf '%b\n' "$SSH_KEY_RAW" > ~/.ssh/id_ed25519
|
|
else
|
|
printf '%s' "$SSH_KEY_RAW" | base64 -d > ~/.ssh/id_ed25519
|
|
fi
|
|
else
|
|
echo "Missing DEPLOY_SSH_KEY_B64 or DEPLOY_SSH_KEY secret" >&2
|
|
exit 1
|
|
fi
|
|
sed -i 's/\r$//' ~/.ssh/id_ed25519
|
|
chmod 600 ~/.ssh/id_ed25519
|
|
ssh-keyscan -H "$DEPLOY_HOST" >> ~/.ssh/known_hosts 2>/dev/null || true
|
|
sleep 10
|
|
HOME_STATUS=$(ssh -i ~/.ssh/id_ed25519 -o StrictHostKeyChecking=yes "$DEPLOY_USER@$DEPLOY_HOST" "curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:5001/taxbaik/" || echo "000")
|
|
LOGIN_STATUS=$(ssh -i ~/.ssh/id_ed25519 -o StrictHostKeyChecking=yes "$DEPLOY_USER@$DEPLOY_HOST" "curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:5001/taxbaik/admin/login" || echo "000")
|
|
ADMIN_TEST_PASSWORD="${{ secrets.TAXBAIK_ADMIN_TEST_PASSWORD }}"
|
|
AUTH_BODY=$(ssh -i ~/.ssh/id_ed25519 -o StrictHostKeyChecking=yes "$DEPLOY_USER@$DEPLOY_HOST" "python3 -c \"import json, urllib.request; req = urllib.request.Request('http://127.0.0.1:5001/taxbaik/api/auth/login', data=json.dumps({'username':'admin','password':'${ADMIN_TEST_PASSWORD}'}).encode(), headers={'Content-Type':'application/json'}, method='POST'); print(urllib.request.urlopen(req, timeout=20).read().decode())\"" || echo "")
|
|
echo "Home Status: $HOME_STATUS"
|
|
echo "Login Status: $LOGIN_STATUS"
|
|
echo "Auth Body: $AUTH_BODY"
|
|
if [ "$HOME_STATUS" = "200" ] && [ "$LOGIN_STATUS" = "200" ] && echo "$AUTH_BODY" | grep -q '"token"'; then
|
|
echo "✓ Service is running"
|
|
else
|
|
echo "Service verification failed (home: $HOME_STATUS, login: $LOGIN_STATUS, auth: $AUTH_BODY)" >&2
|
|
exit 1
|
|
fi
|
|
|
|
browser-e2e:
|
|
runs-on: ubuntu-latest
|
|
needs: build-and-deploy
|
|
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Setup Node.js
|
|
uses: actions/setup-node@v4
|
|
with:
|
|
node-version: '22'
|
|
cache: npm
|
|
|
|
- name: Cache Playwright browsers
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: |
|
|
~/.cache/ms-playwright
|
|
key: ${{ runner.os }}-playwright-${{ hashFiles('package-lock.json') }}
|
|
restore-keys: |
|
|
${{ runner.os }}-playwright-
|
|
|
|
- name: Install Playwright dependencies
|
|
run: |
|
|
set -e
|
|
npm ci
|
|
npx playwright install chromium
|
|
|
|
- name: Browser E2E verification
|
|
env:
|
|
E2E_BASE_URL: http://${{ secrets.DEPLOY_HOST }}/taxbaik
|
|
E2E_ADMIN_USERNAME: admin
|
|
E2E_ADMIN_PASSWORD: ${{ secrets.TAXBAIK_ADMIN_TEST_PASSWORD }}
|
|
run: npm run test:e2e
|