diff --git a/.gitea/workflows/deploy-prod.yml b/.gitea/workflows/deploy-prod.yml index 39c2b933..de3ca5ab 100644 --- a/.gitea/workflows/deploy-prod.yml +++ b/.gitea/workflows/deploy-prod.yml @@ -21,6 +21,7 @@ env: DEPLOY_PORT: 22 SERVICE_NAME: quantengine REPO: kjh2064/QuantEngineByItz + SSH_KNOWN_HOSTS: ~/.ssh/known_hosts jobs: deploy: @@ -36,19 +37,18 @@ jobs: steps: - name: Verify SSH Key and Secrets run: | - # SSH_PRIVATE_KEY is the actual secret name registered in this repo - # (verified via GET /repos/{r}/actions/secrets -- DEPLOY_SSH_KEY_B64 / - # DEPLOY_SSH_KEY were never actually created despite CLAUDE.md - # claiming so; kept as fallback names in case they're added later). + set -euo pipefail SSH_KEY="${{ secrets.SSH_PRIVATE_KEY }}" - SSH_KEY_B64="${{ secrets.DEPLOY_SSH_KEY_B64 }}" - SSH_KEY_RAW="${{ secrets.DEPLOY_SSH_KEY }}" - if [ -z "$SSH_KEY" ] && [ -z "$SSH_KEY_B64" ] && [ -z "$SSH_KEY_RAW" ]; then - echo "ERROR: No SSH key secret configured (checked SSH_PRIVATE_KEY, DEPLOY_SSH_KEY_B64, DEPLOY_SSH_KEY)" + TOKEN="${{ secrets.GITEA_TOKEN }}" + if [ -z "$SSH_KEY" ]; then + echo "ERROR: SSH_PRIVATE_KEY secret not configured" exit 1 fi - [ -z "${{ secrets.GITEA_TOKEN }}" ] && { echo "ERROR: GITEA_TOKEN not configured"; exit 1; } - echo "✓ SSH key and GITEA_TOKEN configured" + if [ -z "$TOKEN" ]; then + echo "ERROR: GITEA_TOKEN not configured" + exit 1 + fi + echo "✓ Required deployment secrets configured" - name: Fetch Release Info id: fetch @@ -116,51 +116,44 @@ jobs: - name: Setup SSH run: | + set -euo pipefail mkdir -p ~/.ssh + SSH_KNOWN_HOSTS="$HOME/.ssh/known_hosts" SSH_KEY="${{ secrets.SSH_PRIVATE_KEY }}" - SSH_KEY_B64="${{ secrets.DEPLOY_SSH_KEY_B64 }}" - SSH_KEY_RAW="${{ secrets.DEPLOY_SSH_KEY }}" - - write_key() { - # $1 = raw secret value; auto-detects PEM vs base64 - if printf '%s' "$1" | grep -q 'BEGIN.*PRIVATE KEY'; then - printf '%b\n' "$1" > ~/.ssh/deploy_key - else - printf '%s' "$1" | base64 -d > ~/.ssh/deploy_key - fi - } - - if [ -n "$SSH_KEY" ]; then - write_key "$SSH_KEY" - elif [ -n "$SSH_KEY_B64" ]; then - printf '%s' "$SSH_KEY_B64" | base64 -d > ~/.ssh/deploy_key - elif [ -n "$SSH_KEY_RAW" ]; then - write_key "$SSH_KEY_RAW" - else - echo "ERROR: No SSH key configured" + if [ -z "$SSH_KEY" ]; then + echo "ERROR: SSH_PRIVATE_KEY not configured" exit 1 fi + if printf '%s' "$SSH_KEY" | grep -q 'BEGIN.*PRIVATE KEY'; then + printf '%b\n' "$SSH_KEY" > ~/.ssh/deploy_key + else + printf '%s' "$SSH_KEY" | base64 -d > ~/.ssh/deploy_key + fi sed -i 's/\r$//' ~/.ssh/deploy_key chmod 600 ~/.ssh/deploy_key - ssh-keyscan -p ${{ env.DEPLOY_PORT }} ${{ env.DEPLOY_HOST }} >> ~/.ssh/known_hosts 2>/dev/null || true + ssh-keyscan -p ${{ env.DEPLOY_PORT }} ${{ env.DEPLOY_HOST }} > "$SSH_KNOWN_HOSTS" 2>/dev/null || true echo "✓ SSH configured" - name: Upload Release Artifact run: | + SSH_KNOWN_HOSTS="$HOME/.ssh/known_hosts" ARTIFACT="${{ steps.fetch.outputs.artifact }}" echo "Uploading: $ARTIFACT" ls -lh "$ARTIFACT" scp -i ~/.ssh/deploy_key \ -P ${{ env.DEPLOY_PORT }} \ - -o StrictHostKeyChecking=accept-new \ + -o IdentitiesOnly=yes \ + -o StrictHostKeyChecking=yes \ + -o UserKnownHostsFile="${SSH_KNOWN_HOSTS}" \ -o ConnectTimeout=10 \ "$ARTIFACT" ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }}:/tmp/ echo "✓ Release artifact uploaded" - name: Deploy & Verify run: | + SSH_KNOWN_HOSTS="$HOME/.ssh/known_hosts" ARTIFACT="${{ steps.fetch.outputs.artifact }}" RELEASE_TAG="${{ steps.fetch.outputs.tag }}" COMMIT="${{ steps.fetch.outputs.commit }}" @@ -178,7 +171,9 @@ jobs: # remote script's environment. ssh -i ~/.ssh/deploy_key \ -p ${{ env.DEPLOY_PORT }} \ - -o StrictHostKeyChecking=accept-new \ + -o IdentitiesOnly=yes \ + -o StrictHostKeyChecking=yes \ + -o UserKnownHostsFile="${SSH_KNOWN_HOSTS}" \ -o ConnectTimeout=10 \ ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }} \ "ARTIFACT='$ARTIFACT' RELEASE_TAG='$RELEASE_TAG' COMMIT='$COMMIT' SERVICE_NAME='$SERVICE_NAME' bash -s" << 'REMOTE' @@ -234,28 +229,26 @@ jobs: steps: - name: Setup SSH (for service check) run: | + set -euo pipefail mkdir -p ~/.ssh + SSH_KNOWN_HOSTS="$HOME/.ssh/known_hosts" SSH_KEY="${{ secrets.SSH_PRIVATE_KEY }}" - SSH_KEY_B64="${{ secrets.DEPLOY_SSH_KEY_B64 }}" - SSH_KEY_RAW="${{ secrets.DEPLOY_SSH_KEY }}" - - if [ -n "$SSH_KEY" ]; then - if printf '%s' "$SSH_KEY" | grep -q 'BEGIN.*PRIVATE KEY'; then - printf '%b\n' "$SSH_KEY" > ~/.ssh/deploy_key - else - printf '%s' "$SSH_KEY" | base64 -d > ~/.ssh/deploy_key - fi - elif [ -n "$SSH_KEY_B64" ]; then - printf '%s' "$SSH_KEY_B64" | base64 -d > ~/.ssh/deploy_key - elif [ -n "$SSH_KEY_RAW" ]; then - printf '%s' "$SSH_KEY_RAW" | base64 -d > ~/.ssh/deploy_key + if [ -z "$SSH_KEY" ]; then + echo "ERROR: SSH_PRIVATE_KEY not configured" + exit 1 + fi + if printf '%s' "$SSH_KEY" | grep -q 'BEGIN.*PRIVATE KEY'; then + printf '%b\n' "$SSH_KEY" > ~/.ssh/deploy_key + else + printf '%s' "$SSH_KEY" | base64 -d > ~/.ssh/deploy_key fi chmod 600 ~/.ssh/deploy_key 2>/dev/null || true - ssh-keyscan -p 22 ${{ env.DEPLOY_HOST }} >> ~/.ssh/known_hosts 2>/dev/null || true + ssh-keyscan -p ${{ env.DEPLOY_PORT }} ${{ env.DEPLOY_HOST }} > "$SSH_KNOWN_HOSTS" 2>/dev/null || true - name: Health Check run: | + SSH_KNOWN_HOSTS="$HOME/.ssh/known_hosts" # IMPORTANT: quantengine.service binds ASPNETCORE_URLS to # http://127.0.0.1:5000 (loopback only) -- Nginx is the only # thing that reaches it from outside, via quant.taxbaik.com. @@ -272,7 +265,9 @@ jobs: # checks already correctly do via SSH. ssh -i ~/.ssh/deploy_key \ -p ${{ env.DEPLOY_PORT }} \ - -o StrictHostKeyChecking=accept-new \ + -o IdentitiesOnly=yes \ + -o StrictHostKeyChecking=yes \ + -o UserKnownHostsFile="${SSH_KNOWN_HOSTS}" \ -o ConnectTimeout=10 \ ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }} bash -s << 'REMOTE' set -e