From 439d6d4455bd36750fca74fb789c5f0a1687c018 Mon Sep 17 00:00:00 2001 From: ianshaloom Date: Sun, 8 Mar 2026 11:37:02 +0300 Subject: [PATCH] fix(ci): fix YAML parse error in deploy workflow caused by inner heredoc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The compose file was written via a bash << 'COMPOSE' heredoc nested inside the YAML run: | block scalar. Lines like "name: petloft-staging" at column 0 cause the YAML parser to break out of the block scalar early, making the entire workflow file invalid YAML — Gitea silently drops invalid workflows, so no jobs triggered at all. Fix: move compose.yml to deploy/staging/compose.yml in the repo, substitute ${REGISTRY} on the runner, base64-encode the result, and decode it on the VPS inside the SSH session. No inner heredoc needed. Co-Authored-By: Claude Sonnet 4.6 --- .gitea/workflows/deploy-staging.yml | 40 ++++++++--------------------- deploy/staging/compose.yml | 20 +++++++++++++++ 2 files changed, 30 insertions(+), 30 deletions(-) create mode 100644 deploy/staging/compose.yml diff --git a/.gitea/workflows/deploy-staging.yml b/.gitea/workflows/deploy-staging.yml index e58d0c9..a8c8fd4 100644 --- a/.gitea/workflows/deploy-staging.yml +++ b/.gitea/workflows/deploy-staging.yml @@ -159,6 +159,8 @@ jobs: runs-on: ubuntu-latest steps: + - uses: actions/checkout@v4 + - name: Write SSH key run: | mkdir -p ~/.ssh @@ -174,9 +176,14 @@ jobs: SSH_USER: ${{ secrets.STAGING_SSH_USER }} SSH_PORT: ${{ secrets.STAGING_SSH_PORT }} run: | - # Auth key is the hostname only — strip the /owner path REGISTRY_HOST=$(echo "$REGISTRY" | cut -d'/' -f1) + # Substitute the actual registry value and base64-encode the compose file. + # Passing it as a base64 string through the SSH heredoc avoids the need for + # an inner heredoc, which breaks YAML parsing when compose content has lines + # at column 0 (YAML sees them as new top-level nodes, invalidating the file). + COMPOSE_B64=$(sed "s|\${REGISTRY}|${REGISTRY}|g" deploy/staging/compose.yml | base64 -w 0) + # StrictHostKeyChecking=accept-new trusts on first connect but rejects # changed keys on subsequent runs — safer than no-verify ssh -i ~/.ssh/staging \ @@ -196,37 +203,10 @@ jobs: mkdir -p /opt/staging - # Write the compose file on every deploy so it stays in sync with CI. - # REGISTRY is interpolated by bash here (not by podman compose), so the - # actual registry host:port/owner value is embedded in the file. - cat > /opt/staging/compose.yml << 'COMPOSE' -name: petloft-staging - -services: - storefront: - image: ${REGISTRY}/storefront:staging - restart: unless-stopped - ports: - - "3000:3000" - env_file: - - path: .env - required: false - - admin: - image: ${REGISTRY}/admin:staging - restart: unless-stopped - ports: - - "3001:3001" - env_file: - - path: .env - required: false -COMPOSE - # Substitute the actual registry value into the compose file - sed -i "s|\${REGISTRY}|${REGISTRY}|g" /opt/staging/compose.yml - - # Create a minimal .env if one doesn't exist. + # Decode the compose file that was base64-encoded on the runner. # Runtime secrets (CLERK_SECRET_KEY, etc.) should be added manually # to /opt/staging/.env on the VPS after first deploy. + echo "${COMPOSE_B64}" | base64 -d > /opt/staging/compose.yml touch /opt/staging/.env cd /opt/staging diff --git a/deploy/staging/compose.yml b/deploy/staging/compose.yml new file mode 100644 index 0000000..04a4005 --- /dev/null +++ b/deploy/staging/compose.yml @@ -0,0 +1,20 @@ +name: petloft-staging + +services: + storefront: + image: ${REGISTRY}/storefront:staging + restart: unless-stopped + ports: + - "3000:3000" + env_file: + - path: .env + required: false + + admin: + image: ${REGISTRY}/admin:staging + restart: unless-stopped + ports: + - "3001:3001" + env_file: + - path: .env + required: false