Deploy to amvara9 #255
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Deploy to amvara9 only when the production branch is pushed (this repo: `master`). | |
| # Pushes to `development` do not run this workflow (merge to `master` to deploy). Use workflow_dispatch for manual runs. | |
| # Repository: https://github.com/satisfecho/pos — workflow must run from satisfecho/pos; server clone must have origin = satisfecho/pos. | |
| # Requires repository secret: SSH_PRIVATE_KEY_AMVARA9 (private key for root@amvara9; raw PEM or base64). | |
| # Optional: DEPLOY_HOST (default 167.235.138.59), DEPLOY_USER (default root), DEPLOY_PATH (default /development/pos). | |
| # Marketing sites under /<slug>/ (see config/marketing-sites.json): optional GUSTAZO_ARTIFACT_TOKEN / MARKETING_ARTIFACT_TOKEN (PAT with Actions read on each repo). CI rsync runs after git reset so bundles are not wiped. | |
| # Optional repo Variables: GUSTAZO_BRANCH, GUSTAZO_ARTIFACT_NAME per legacy Gustazo defaults. | |
| # Smoke test uses SMOKE_TEST_BASE_URL (default https://www.satisfecho.de) so SSL validates. | |
| name: Deploy to amvara9 | |
| on: | |
| push: | |
| branches: | |
| - master | |
| workflow_dispatch: | |
| jobs: | |
| deploy: | |
| runs-on: ubuntu-latest | |
| # One deploy at a time to the same host/path regardless of branch (avoids overlapping git/build/up). | |
| concurrency: | |
| group: deploy-amvara9 | |
| cancel-in-progress: false | |
| env: | |
| FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true | |
| GUSTAZO_REPO: satisfecho/040_gustazo | |
| GUSTAZO_BRANCH: ${{ vars.GUSTAZO_BRANCH }} | |
| GUSTAZO_ARTIFACT_NAME: ${{ vars.GUSTAZO_ARTIFACT_NAME }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Fetch marketing site artifacts (curl + GitHub API) | |
| env: | |
| MARKETING_ARTIFACT_TOKEN: ${{ secrets.GUSTAZO_ARTIFACT_TOKEN }} | |
| GH_TOKEN: ${{ secrets.GUSTAZO_ARTIFACT_TOKEN }} | |
| MARKETING_SYNC_FORCE: "1" | |
| # Fails the job if any listed site is still the placeholder (e.g. fine-grained PAT only on 040_gustazo) | |
| MARKETING_VERIFY_NO_PLACEHOLDERS: "1" | |
| run: bash scripts/sync-all-marketing-sites.sh | |
| - name: Set up SSH | |
| run: | | |
| mkdir -p ~/.ssh | |
| KEY="${{ secrets.SSH_PRIVATE_KEY_AMVARA9 }}" | |
| echo "Key length: ${#KEY} chars, looks like PEM: $(echo \"$KEY\" | grep -q 'BEGIN' && echo \"$KEY\" | grep -q 'PRIVATE KEY' && echo yes || echo no)" | |
| # If secret contains PEM markers, use as raw key; else decode as base64 | |
| if echo "$KEY" | grep -q "BEGIN" && echo "$KEY" | grep -q "PRIVATE KEY"; then | |
| # Preserve newlines: interpret \n if stored literally, strip CR | |
| printf '%b' "$KEY" | tr -d '\r' > ~/.ssh/deploy_key | |
| else | |
| echo "$KEY" | tr -d '\n\r\t ' | sed 's/[^A-Za-z0-9+\/=]//g' | base64 -d > ~/.ssh/deploy_key | |
| fi | |
| chmod 600 ~/.ssh/deploy_key | |
| if ! ssh-keygen -y -f ~/.ssh/deploy_key > /dev/null 2>&1; then | |
| echo "::error::Invalid SSH_PRIVATE_KEY_AMVARA9: ssh-keygen could not read the key. Paste the full raw key (-----BEGIN...END-----) or a single-line base64 string (base64 -w 0 keyfile)." | |
| exit 1 | |
| fi | |
| ssh-keyscan -H "${{ secrets.DEPLOY_HOST || '167.235.138.59' }}" >> ~/.ssh/known_hosts 2>/dev/null || true | |
| # Git reset must run BEFORE rsync of marketing bundles: deploy-amvara9.sh uses `docker compose build` | |
| # which COPYs front/sites/*. If we rsync before `git reset --hard`, placeholders from Git overwrite | |
| # fetched artifacts on the server (explains Gustazo staying on placeholder in prod). | |
| - name: Checkout latest code on amvara9 (git fetch / reset — marketing dirs reset to placeholders) | |
| env: | |
| DEPLOY_HOST: ${{ secrets.DEPLOY_HOST || '167.235.138.59' }} | |
| DEPLOY_USER: ${{ secrets.DEPLOY_USER || 'root' }} | |
| DEPLOY_PATH: ${{ secrets.DEPLOY_PATH || '/development/pos' }} | |
| run: | | |
| ssh -i ~/.ssh/deploy_key -o StrictHostKeyChecking=accept-new \ | |
| "${DEPLOY_USER}@${DEPLOY_HOST}" "mkdir -p '${DEPLOY_PATH}/front/sites'" | |
| BRANCH="${{ github.ref_name }}" | |
| ssh -i ~/.ssh/deploy_key -o StrictHostKeyChecking=accept-new \ | |
| "${DEPLOY_USER}@${DEPLOY_HOST}" \ | |
| "cd '${DEPLOY_PATH}' && git fetch origin && git checkout -f '${BRANCH}' && git reset --hard 'origin/${BRANCH}' && git clean -fd" | |
| - name: Sync marketing sites to server (after git reset; bundles from Fetch step above) | |
| env: | |
| DEPLOY_HOST: ${{ secrets.DEPLOY_HOST || '167.235.138.59' }} | |
| DEPLOY_USER: ${{ secrets.DEPLOY_USER || 'root' }} | |
| DEPLOY_PATH: ${{ secrets.DEPLOY_PATH || '/development/pos' }} | |
| run: | | |
| rsync -az -e "ssh -i ~/.ssh/deploy_key -o StrictHostKeyChecking=accept-new -o ServerAliveInterval=60" \ | |
| ./front/sites/ "${DEPLOY_USER}@${DEPLOY_HOST}:${DEPLOY_PATH}/front/sites/" | |
| - name: Build and restart stack on amvara9 | |
| env: | |
| DEPLOY_HOST: ${{ secrets.DEPLOY_HOST || '167.235.138.59' }} | |
| DEPLOY_USER: ${{ secrets.DEPLOY_USER || 'root' }} | |
| DEPLOY_PATH: ${{ secrets.DEPLOY_PATH || '/development/pos' }} | |
| timeout-minutes: 20 | |
| run: | | |
| ssh -i ~/.ssh/deploy_key -o StrictHostKeyChecking=accept-new -o ServerAliveInterval=60 -o ServerAliveCountMax=10 \ | |
| "${DEPLOY_USER}@${DEPLOY_HOST}" "cd '${DEPLOY_PATH}' && bash scripts/deploy-amvara9.sh" | |
| - name: Smoke test (landing, version, API health) | |
| env: | |
| SMOKE_TEST_BASE_URL: ${{ vars.SMOKE_TEST_BASE_URL || 'https://www.satisfecho.de' }} | |
| run: | | |
| BASE="${SMOKE_TEST_BASE_URL}" | |
| echo "Smoke test: $BASE (landing), version in footer, $BASE/api/health" | |
| LANDING_OK="" | |
| for attempt in 1 2 3 4 5 6; do | |
| echo "Landing HTTP check attempt ${attempt}/6..." | |
| if curl -sf -o /dev/null -w "%{http_code}" "$BASE/" | grep -q 200; then | |
| LANDING_OK=1 | |
| break | |
| fi | |
| [ "$attempt" -lt 6 ] && sleep 15 | |
| done | |
| [ -n "$LANDING_OK" ] || (echo "Landing page failed after retries"; exit 1) | |
| HTML="" | |
| for attempt in 1 2 3 4 5 6; do | |
| echo "Landing HTML + version meta attempt ${attempt}/6..." | |
| HTML=$(curl -sfL "$BASE/?t=$(date +%s)") && break | |
| [ "$attempt" -lt 6 ] && sleep 15 | |
| done | |
| [ -n "$HTML" ] || (echo "Failed to fetch landing page after retries"; exit 1) | |
| if echo "$HTML" | grep -qE 'name="app-version"[^>]*content="[0-9]+\.[0-9]+\.[0-9]+"'; then | |
| echo "Version meta tag found in landing page." | |
| elif echo "$HTML" | grep -q 'name="app-version"' && echo "$HTML" | grep -qE 'content="[0-9]+\.[0-9]+\.[0-9]+"'; then | |
| echo "Version meta (split) found in landing page." | |
| else | |
| echo "Version meta tag missing in landing page HTML (expected app-version with semver)." | |
| echo "First 500 chars of head: $(echo "$HTML" | head -c 500)" | |
| exit 1 | |
| fi | |
| echo "Version correctly shown in landing page (no login required)." | |
| HEALTH_OK="" | |
| for attempt in 1 2 3 4 5 6; do | |
| echo "API health attempt ${attempt}/6..." | |
| if curl -sf -o /dev/null -w "%{http_code}" "$BASE/api/health" | grep -q 200; then | |
| HEALTH_OK=1 | |
| break | |
| fi | |
| [ "$attempt" -lt 6 ] && sleep 15 | |
| done | |
| [ -n "$HEALTH_OK" ] || (echo "API health failed after retries"; exit 1) | |
| echo "Smoke test passed." | |
| echo "Marketing /gustazo/ (expect real bundle, not repo placeholder)" | |
| if ! curl -sf -o /dev/null "$BASE/gustazo/"; then | |
| echo "::warning::GET $BASE/gustazo/ did not return 200." | |
| else | |
| GZ_HTML=$(curl -sfL "$BASE/gustazo/?t=$(date +%s)" || true) | |
| if echo "$GZ_HTML" | grep -q 'bundle not loaded'; then | |
| echo "::warning::Gustazo still serves placeholder HTML (verify GUSTAZO_ARTIFACT_TOKEN and deploy rsync runs after git reset)." | |
| else | |
| echo "Gustazo path OK (no placeholder title)." | |
| fi | |
| fi |