fix: Eliminate cross-job artifact passing and fix download URL

Found via SSH log analysis (actions_log/.../2332.log, Run #2002):

1. This Gitea Actions instance's runner explicitly rejects the
   actions/upload-artifact@v4 / download-artifact@v4 protocol:
     "GHESNotSupportedError: @actions/artifact v2.0.0+,
      upload-artifact@v4+ and download-artifact@v4+ are not
      currently supported on GHES."
   The old 3-job split (fetch-release -> pre-deploy-check -> deploy)
   relied on upload-artifact/download-artifact to hand the .tar.gz
   from the fetch job to the deploy job, so it could never succeed
   on this server regardless of any other fix.

2. Independently, the guessed download URL pattern
   /releases/download/{tag}/{filename} doesn't exist on this Gitea
   instance -- it silently downloaded a 19-byte "404 page not found"
   body as if it were the artifact (curl exited 0, file "existed").

Fixes:
- Merge fetch-release + pre-deploy-check + deploy into a single
  `deploy` job so the downloaded artifact never needs to cross a
  job boundary -- it's downloaded and scp'd from the same runner
  filesystem in one shot.
- Fetch the real `browser_download_url` from the release JSON
  instead of constructing the URL by convention.
- Add a `file "$ARTIFACT" | grep -q "gzip compressed"` guard right
  after download so a wrong-URL / error-page download fails loudly
  instead of silently proceeding with garbage bytes.
- Update post-deploy-check / post-deploy-report to read from
  `needs.deploy.outputs.*` now that fetch-release no longer exists
  as a separate job.
This commit is contained in:
2026-07-12 00:47:18 +09:00
parent 6e9a9aa41b
commit cc94d5aeae
+46 -93
View File
@@ -20,17 +20,27 @@ env:
REPO: kjh2064/QuantEngineByItz REPO: kjh2064/QuantEngineByItz
jobs: jobs:
fetch-release: deploy:
name: Fetch Release Artifact name: Deploy to Production
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 10 timeout-minutes: 30
outputs: outputs:
release-tag: ${{ steps.fetch.outputs.tag }} release-tag: ${{ steps.fetch.outputs.tag }}
artifact-name: ${{ steps.fetch.outputs.artifact }} artifact-name: ${{ steps.fetch.outputs.artifact }}
artifact-size: ${{ steps.fetch.outputs.size }}
commit-hash: ${{ steps.fetch.outputs.commit }} commit-hash: ${{ steps.fetch.outputs.commit }}
steps: steps:
- name: Verify SSH Key and Secrets
run: |
SSH_KEY_B64="${{ secrets.DEPLOY_SSH_KEY_B64 }}"
SSH_KEY_RAW="${{ secrets.DEPLOY_SSH_KEY }}"
if [ -z "$SSH_KEY_B64" ] && [ -z "$SSH_KEY_RAW" ]; then
echo "ERROR: DEPLOY_SSH_KEY_B64 or DEPLOY_SSH_KEY not configured"
exit 1
fi
[ -z "${{ secrets.GITEA_TOKEN }}" ] && { echo "ERROR: GITEA_TOKEN not configured"; exit 1; }
echo "✓ SSH key and GITEA_TOKEN configured"
- name: Fetch Release Info - name: Fetch Release Info
id: fetch id: fetch
run: | run: |
@@ -39,113 +49,58 @@ jobs:
REPO="${{ env.REPO }}" REPO="${{ env.REPO }}"
if [ -z "$RELEASE_INPUT" ]; then if [ -z "$RELEASE_INPUT" ]; then
# Fetch latest release
RELEASE_URL="https://gitea.taxbaik.com/api/v1/repos/$REPO/releases/latest" RELEASE_URL="https://gitea.taxbaik.com/api/v1/repos/$REPO/releases/latest"
else else
# Fetch specific release
RELEASE_URL="https://gitea.taxbaik.com/api/v1/repos/$REPO/releases/tags/$RELEASE_INPUT" RELEASE_URL="https://gitea.taxbaik.com/api/v1/repos/$REPO/releases/tags/$RELEASE_INPUT"
fi fi
RELEASE=$(curl -s -H "Authorization: token $TOKEN" "$RELEASE_URL") RELEASE=$(curl -sf -H "Authorization: token $TOKEN" "$RELEASE_URL")
TAG=$(echo "$RELEASE" | jq -r '.tag_name') TAG=$(echo "$RELEASE" | jq -r '.tag_name')
COMMIT=$(echo "$RELEASE" | jq -r '.target_commitish' | cut -c1-7) COMMIT=$(echo "$RELEASE" | jq -r '.target_commitish' | cut -c1-7)
ARTIFACT=$(echo "$RELEASE" | jq -r '.assets[0].name')
DOWNLOAD_URL=$(echo "$RELEASE" | jq -r '.assets[0].browser_download_url')
if [ "$TAG" = "null" ] || [ -z "$TAG" ]; then if [ "$TAG" = "null" ] || [ -z "$TAG" ]; then
echo "ERROR: Release not found" echo "ERROR: Release not found"; exit 1
exit 1
fi fi
# Find artifact in assets
ARTIFACT=$(echo "$RELEASE" | jq -r '.assets[0].name')
SIZE=$(echo "$RELEASE" | jq -r '.assets[0].size')
if [ "$ARTIFACT" = "null" ] || [ -z "$ARTIFACT" ]; then if [ "$ARTIFACT" = "null" ] || [ -z "$ARTIFACT" ]; then
echo "ERROR: No artifacts found in release $TAG" echo "ERROR: No artifacts found in release $TAG"; exit 1
exit 1 fi
if [ "$DOWNLOAD_URL" = "null" ] || [ -z "$DOWNLOAD_URL" ]; then
echo "ERROR: No browser_download_url found for asset"; exit 1
fi fi
echo "tag=${TAG}" >> $GITHUB_OUTPUT echo "tag=${TAG}" >> $GITHUB_OUTPUT
echo "artifact=${ARTIFACT}" >> $GITHUB_OUTPUT echo "artifact=${ARTIFACT}" >> $GITHUB_OUTPUT
echo "size=${SIZE}" >> $GITHUB_OUTPUT echo "download_url=${DOWNLOAD_URL}" >> $GITHUB_OUTPUT
echo "commit=${COMMIT}" >> $GITHUB_OUTPUT echo "commit=${COMMIT}" >> $GITHUB_OUTPUT
echo "✓ Release: $TAG" echo "✓ Release: $TAG"
echo "✓ Artifact: $ARTIFACT" echo "✓ Artifact: $ARTIFACT"
echo "✓ Size: $SIZE bytes" echo "✓ Download URL: $DOWNLOAD_URL"
- name: Download Release Artifact - name: Download Release Artifact
run: | run: |
TAG="${{ steps.fetch.outputs.tag }}"
ARTIFACT="${{ steps.fetch.outputs.artifact }}" ARTIFACT="${{ steps.fetch.outputs.artifact }}"
TOKEN="${{ secrets.GITEA_TOKEN }}" TOKEN="${{ secrets.GITEA_TOKEN }}"
REPO="${{ env.REPO }}" DOWNLOAD_URL="${{ steps.fetch.outputs.download_url }}"
DOWNLOAD_URL="https://gitea.taxbaik.com/api/v1/repos/$REPO/releases/download/$TAG/$ARTIFACT"
echo "Downloading: $DOWNLOAD_URL" echo "Downloading: $DOWNLOAD_URL"
curl -L -H "Authorization: token $TOKEN" \ curl -sfL -H "Authorization: token $TOKEN" -o "$ARTIFACT" "$DOWNLOAD_URL"
-o "$ARTIFACT" \
"$DOWNLOAD_URL"
if [ ! -f "$ARTIFACT" ]; then # A 404/error page would still create a small file -- verify it's a
echo "ERROR: Failed to download artifact" # real gzip archive, not an HTML/JSON error body (this is exactly
# how the old /releases/download/{tag}/{file} guessed URL failed
# silently: curl exited 0 but wrote a 19-byte "404 page not found").
file "$ARTIFACT" | grep -q "gzip compressed" || {
echo "ERROR: Downloaded file is not a valid gzip archive:"
file "$ARTIFACT"
cat "$ARTIFACT"
exit 1 exit 1
fi }
echo "✓ Downloaded: $(du -sh $ARTIFACT)" echo "✓ Downloaded: $(du -sh $ARTIFACT)"
- name: Upload to Actions
uses: actions/upload-artifact@v4
with:
name: release-artifact
path: quantengine_*.tar.gz
retention-days: 1
pre-deploy-check:
name: Pre-Deployment Verification
runs-on: ubuntu-latest
needs: fetch-release
timeout-minutes: 5
steps:
- name: Verify SSH Key
run: |
SSH_KEY_B64="${{ secrets.DEPLOY_SSH_KEY_B64 }}"
SSH_KEY_RAW="${{ secrets.DEPLOY_SSH_KEY }}"
if [ -z "$SSH_KEY_B64" ] && [ -z "$SSH_KEY_RAW" ]; then
echo "ERROR: DEPLOY_SSH_KEY_B64 or DEPLOY_SSH_KEY not configured"
exit 1
fi
echo "✓ SSH key configured"
- name: Verify Secrets
run: |
[ -z "${{ secrets.DEPLOY_HOST }}" ] && { echo "ERROR: DEPLOY_HOST not configured"; exit 1; }
[ -z "${{ secrets.DEPLOY_USER }}" ] && { echo "ERROR: DEPLOY_USER not configured"; exit 1; }
echo "✓ All secrets configured"
- name: Verify Release Artifact
run: |
if [ "${{ needs.fetch-release.outputs.artifact-name }}" = "" ]; then
echo "ERROR: Release artifact not found"
exit 1
fi
echo "✓ Release: ${{ needs.fetch-release.outputs.release-tag }}"
echo "✓ Artifact: ${{ needs.fetch-release.outputs.artifact-name }}"
echo "✓ Commit: ${{ needs.fetch-release.outputs.commit-hash }}"
deploy:
name: Deploy to Production
runs-on: ubuntu-latest
needs: [ fetch-release, pre-deploy-check ]
timeout-minutes: 30
steps:
- name: Download Release Artifact
uses: actions/download-artifact@v4
with:
name: release-artifact
- name: Setup SSH - name: Setup SSH
run: | run: |
mkdir -p ~/.ssh mkdir -p ~/.ssh
@@ -172,7 +127,7 @@ jobs:
- name: Upload Release Artifact - name: Upload Release Artifact
run: | run: |
ARTIFACT="${{ needs.fetch-release.outputs.artifact-name }}" ARTIFACT="${{ steps.fetch.outputs.artifact }}"
echo "Uploading: $ARTIFACT" echo "Uploading: $ARTIFACT"
ls -lh "$ARTIFACT" ls -lh "$ARTIFACT"
@@ -184,9 +139,9 @@ jobs:
- name: Deploy & Verify - name: Deploy & Verify
run: | run: |
ARTIFACT="${{ needs.fetch-release.outputs.artifact-name }}" ARTIFACT="${{ steps.fetch.outputs.artifact }}"
RELEASE_TAG="${{ needs.fetch-release.outputs.release-tag }}" RELEASE_TAG="${{ steps.fetch.outputs.tag }}"
COMMIT="${{ needs.fetch-release.outputs.commit-hash }}" COMMIT="${{ steps.fetch.outputs.commit }}"
ssh -i ~/.ssh/deploy_key \ ssh -i ~/.ssh/deploy_key \
-p ${{ env.DEPLOY_PORT }} \ -p ${{ env.DEPLOY_PORT }} \
@@ -245,7 +200,7 @@ jobs:
post-deploy-check: post-deploy-check:
name: Health Check & Verification name: Health Check & Verification
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [ fetch-release, deploy ] needs: deploy
timeout-minutes: 10 timeout-minutes: 10
steps: steps:
@@ -307,7 +262,7 @@ jobs:
fi fi
# Check 5: Release verified # Check 5: Release verified
echo "✓ [5/5] Deployment release: ${{ needs.fetch-release.outputs.release-tag }} (commit: ${{ needs.fetch-release.outputs.commit-hash }})" echo "✓ [5/5] Deployment release: ${{ needs.deploy.outputs.release-tag }} (commit: ${{ needs.deploy.outputs.commit-hash }})"
# Check 6: DB connectivity (GET /Account/Login returns 200 even when # Check 6: DB connectivity (GET /Account/Login returns 200 even when
# the DB password is stale -- the page itself has no DB dependency. # the DB password is stale -- the page itself has no DB dependency.
@@ -349,15 +304,14 @@ jobs:
name: Deployment Report name: Deployment Report
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: always() if: always()
needs: [ fetch-release, deploy, post-deploy-check ] needs: [ deploy, post-deploy-check ]
steps: steps:
- name: Report Status - name: Report Status
run: | run: |
RELEASE="${{ needs.fetch-release.outputs.release-tag }}" RELEASE="${{ needs.deploy.outputs.release-tag }}"
COMMIT="${{ needs.fetch-release.outputs.commit-hash }}" COMMIT="${{ needs.deploy.outputs.commit-hash }}"
ARTIFACT="${{ needs.fetch-release.outputs.artifact-name }}" ARTIFACT="${{ needs.deploy.outputs.artifact-name }}"
FETCH_STATUS="${{ needs.fetch-release.result }}"
DEPLOY_STATUS="${{ needs.deploy.result }}" DEPLOY_STATUS="${{ needs.deploy.result }}"
CHECK_STATUS="${{ needs.post-deploy-check.result }}" CHECK_STATUS="${{ needs.post-deploy-check.result }}"
@@ -370,12 +324,11 @@ jobs:
echo "Artifact: $ARTIFACT" echo "Artifact: $ARTIFACT"
echo "" echo ""
echo "【 Status 】" echo "【 Status 】"
echo "Fetch: $([ "$FETCH_STATUS" = "success" ] && echo "✓" || echo "✗") $FETCH_STATUS"
echo "Deploy: $([ "$DEPLOY_STATUS" = "success" ] && echo "✓" || echo "✗") $DEPLOY_STATUS" echo "Deploy: $([ "$DEPLOY_STATUS" = "success" ] && echo "✓" || echo "✗") $DEPLOY_STATUS"
echo "Health: $([ "$CHECK_STATUS" = "success" ] && echo "✓" || echo "✗") $CHECK_STATUS" echo "Health: $([ "$CHECK_STATUS" = "success" ] && echo "✓" || echo "✗") $CHECK_STATUS"
echo "" echo ""
if [ "$FETCH_STATUS" = "success" ] && [ "$DEPLOY_STATUS" = "success" ] && [ "$CHECK_STATUS" = "success" ]; then if [ "$DEPLOY_STATUS" = "success" ] && [ "$CHECK_STATUS" = "success" ]; then
echo "✅ Deployment Successful" echo "✅ Deployment Successful"
echo "Server: 178.104.200.7" echo "Server: 178.104.200.7"
echo "Release: $RELEASE" echo "Release: $RELEASE"