diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 155640b5..37599827 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -130,11 +130,13 @@ jobs: uses: actions/checkout@v4 - name: Deploy to VPS + env: + GHCR_PAT: ${{ secrets.GHCR_PAT }} run: | mkdir -p ~/.ssh echo "${{ secrets.VPS_SSH_KEY }}" > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa scp -o StrictHostKeyChecking=accept-new deploy.sh ${{ secrets.VPS_USER }}@${{ secrets.VPS_HOST }}:/tmp/deploy.sh - ssh -o StrictHostKeyChecking=accept-new ${{ secrets.VPS_USER }}@${{ secrets.VPS_HOST }} " - bash /tmp/deploy.sh '${{ secrets.GHCR_PAT }}' '${{ github.sha }}' + printf '%s' "$GHCR_PAT" | ssh -o StrictHostKeyChecking=accept-new ${{ secrets.VPS_USER }}@${{ secrets.VPS_HOST }} " + bash /tmp/deploy.sh '${{ github.sha }}' " diff --git a/deploy.sh b/deploy.sh index 81239c02..59a70255 100644 --- a/deploy.sh +++ b/deploy.sh @@ -1,12 +1,17 @@ #!/usr/bin/env bash set -euo pipefail -GHCR_PAT="$1" -NEW_TAG="${2:-latest}" +NEW_TAG="${1:-latest}" TAG_FILE="/var/lib/polyweather/.current_tag" COMPOSE_DIR="/root/PolyWeather" LOCK_FILE="${POLYWEATHER_DEPLOY_LOCK_FILE:-/var/lock/polyweather-deploy.lock}" +GHCR_PAT="" +if ! IFS= read -r GHCR_PAT || [ -z "$GHCR_PAT" ]; then + echo "❌ GHCR token must be provided on stdin" + exit 1 +fi + mkdir -p "$(dirname "$LOCK_FILE")" exec 9>"$LOCK_FILE" if ! flock -n 9; then @@ -14,7 +19,8 @@ if ! flock -n 9; then exit 1 fi -echo "$GHCR_PAT" | docker login ghcr.io -u yangyuan-zhen --password-stdin +printf '%s' "$GHCR_PAT" | docker login ghcr.io -u yangyuan-zhen --password-stdin +unset GHCR_PAT cd "$COMPOSE_DIR" git fetch origin main && git reset --hard origin/main diff --git a/tests/test_deployment_runtime_config.py b/tests/test_deployment_runtime_config.py index f26cf649..f3825dd4 100644 --- a/tests/test_deployment_runtime_config.py +++ b/tests/test_deployment_runtime_config.py @@ -123,6 +123,23 @@ def test_deploy_script_retries_compose_recreate_races(): assert 'compose_up_retry "frontend" -d --no-deps polyweather_frontend' in script +def test_deploy_token_is_passed_over_stdin_not_process_args(): + script = (ROOT / "deploy.sh").read_text(encoding="utf-8") + workflow = (ROOT / ".github" / "workflows" / "ci.yml").read_text( + encoding="utf-8" + ) + + assert 'NEW_TAG="${1:-latest}"' in script + assert 'GHCR_PAT="$1"' not in script + assert "read -r GHCR_PAT" in script + assert 'printf \'%s\' "$GHCR_PAT" | docker login' in script + + assert "GHCR_PAT: ${{ secrets.GHCR_PAT }}" in workflow + assert 'printf \'%s\' "$GHCR_PAT" | ssh' in workflow + assert "bash /tmp/deploy.sh '${{ github.sha }}'" in workflow + assert "bash /tmp/deploy.sh '${{ secrets.GHCR_PAT }}'" not in workflow + + def test_docker_compose_keeps_polyweather_ports_on_loopback(): compose = (ROOT / "docker-compose.yml").read_text(encoding="utf-8")