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 - name: Install Playwright dependencies run: | set -e npm ci npx playwright install chromium --with-deps - 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