name: Deploy on: workflow_run: workflows: [Release] types: [completed] env: IMAGE_NAME: networkcat/rustyip permissions: contents: read jobs: deploy: name: Deploy runs-on: ubuntu-latest if: github.event.workflow_run.conclusion == 'success' environment: production concurrency: group: deploy-production cancel-in-progress: false steps: - name: Checkout repository uses: actions/checkout@v4 - name: Resolve image tag from release id: image run: | TAG="${{ github.event.workflow_run.head_branch }}" TAG="${TAG#v}" echo "full=${{ env.IMAGE_NAME }}:${TAG}" >> "$GITHUB_OUTPUT" # ---- Terraform: provision infrastructure ---- - name: Setup Terraform uses: hashicorp/setup-terraform@v3 with: terraform_wrapper: false - name: Terraform init working-directory: deploy/terraform env: AWS_ACCESS_KEY_ID: ${{ secrets.TF_STATE_ACCESS_KEY }} AWS_SECRET_ACCESS_KEY: ${{ secrets.TF_STATE_SECRET_KEY }} run: | terraform init \ -backend-config="bucket=${{ secrets.TF_STATE_BUCKET }}" \ -backend-config="endpoints={s3=\"${{ secrets.TF_STATE_ENDPOINT }}\"}" - name: Terraform apply working-directory: deploy/terraform env: AWS_ACCESS_KEY_ID: ${{ secrets.TF_STATE_ACCESS_KEY }} AWS_SECRET_ACCESS_KEY: ${{ secrets.TF_STATE_SECRET_KEY }} run: | terraform apply -auto-approve \ -var="vultr_api_key=${{ secrets.VULTR_API_KEY }}" \ -var="ssh_public_key=${{ secrets.SSH_PUBLIC_KEY }}" - name: Extract VPS IP (masked) id: vps working-directory: deploy/terraform env: AWS_ACCESS_KEY_ID: ${{ secrets.TF_STATE_ACCESS_KEY }} AWS_SECRET_ACCESS_KEY: ${{ secrets.TF_STATE_SECRET_KEY }} run: | VPS_IP=$(terraform output -raw instance_ip) echo "::add-mask::${VPS_IP}" echo "ip=${VPS_IP}" >> "$GITHUB_OUTPUT" # ---- SSH setup ---- - name: Setup SSH key run: | mkdir -p ~/.ssh echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/deploy_key chmod 600 ~/.ssh/deploy_key for i in 1 2 3; do if ssh-keyscan -H "${{ steps.vps.outputs.ip }}" >> ~/.ssh/known_hosts 2>/dev/null; then echo "ssh-keyscan succeeded on attempt $i" exit 0 fi echo "ssh-keyscan attempt $i failed, retrying in 10s..." sleep 10 done echo "ssh-keyscan failed after 3 attempts" exit 1 # ---- Detect first deployment ---- - name: Detect first deployment id: detect run: | set +e ssh -o ConnectTimeout=10 -o StrictHostKeyChecking=accept-new \ -i ~/.ssh/deploy_key root@"${{ steps.vps.outputs.ip }}" \ "test -f /opt/rustyip/.active_color" if [ $? -ne 0 ]; then echo "first_deploy=true" >> "$GITHUB_OUTPUT" else echo "first_deploy=false" >> "$GITHUB_OUTPUT" fi # ---- Ansible: configure VPS and deploy ---- - name: Install Ansible run: pip install ansible - name: Generate Ansible inventory run: | cat > deploy/ansible/inventory.yml </dev/null) if [ "$status" = "healthy" ]; then echo "Deployment verified: app-${DEPLOY_COLOR} is healthy" exit 0 fi echo "Waiting for health check... (attempt $i/15)" sleep 4 done echo "Health check failed after 60 seconds" exit 1 # ---- Cleanup ---- - name: Cleanup SSH key if: always() run: rm -f ~/.ssh/deploy_key