name: Deploy to VPS on: workflow_dispatch: inputs: deploy_sha: description: Optional exact commit SHA to deploy. Defaults to the head of the dispatched ref. required: false type: string workflow_run: workflows: [CI] types: [completed] branches: [main] concurrency: group: deploy-vps cancel-in-progress: false jobs: deploy: if: >- github.event_name == 'workflow_dispatch' || ( github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'push' && github.event.workflow_run.head_branch == 'main' ) runs-on: ubuntu-latest steps: - name: Resolve deploy target id: resolve_deploy env: GH_TOKEN: ${{ github.token }} DEPLOY_SHA_INPUT: ${{ inputs.deploy_sha }} run: | if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then DEPLOY_SHA="$DEPLOY_SHA_INPUT" if [ -z "$DEPLOY_SHA" ]; then DEPLOY_SHA="${GITHUB_SHA}" fi if ! printf '%s' "$DEPLOY_SHA" | grep -Eq '^[0-9a-f]{40}$'; then echo "Invalid deploy_sha: expected a 40-character lowercase hex SHA, got '$DEPLOY_SHA'." >&2 exit 1 fi echo "Manual dispatch: deploying SHA $DEPLOY_SHA from ref ${GITHUB_REF_NAME}." echo "deploy_sha=$DEPLOY_SHA" >> "$GITHUB_OUTPUT" echo "fetch_ref=${GITHUB_REF_NAME}" >> "$GITHUB_OUTPUT" echo "should_deploy=true" >> "$GITHUB_OUTPUT" exit 0 fi DEPLOY_SHA="${{ github.event.workflow_run.head_sha }}" MAIN_SHA=$(gh api repos/${{ github.repository }}/git/ref/heads/main --jq '.object.sha') if [ "$DEPLOY_SHA" != "$MAIN_SHA" ]; then echo "Skipping: CI SHA $DEPLOY_SHA is not the current tip of main ($MAIN_SHA). Likely a stale rerun." echo "deploy_sha=$DEPLOY_SHA" >> "$GITHUB_OUTPUT" echo "fetch_ref=main" >> "$GITHUB_OUTPUT" echo "should_deploy=false" >> "$GITHUB_OUTPUT" exit 0 fi echo "SHA verified: $DEPLOY_SHA is the current tip of main." echo "deploy_sha=$DEPLOY_SHA" >> "$GITHUB_OUTPUT" echo "fetch_ref=main" >> "$GITHUB_OUTPUT" echo "should_deploy=true" >> "$GITHUB_OUTPUT" - name: Require VPS host fingerprint env: VPS_HOST_FINGERPRINT: ${{ secrets.VPS_HOST_FINGERPRINT }} run: | set -e if [ -z "$VPS_HOST_FINGERPRINT" ]; then echo "VPS_HOST_FINGERPRINT secret is required for host key verification." >&2 exit 1 fi - name: Deploy via SSH if: steps.resolve_deploy.outputs.should_deploy == 'true' env: DEPLOY_SHA: ${{ steps.resolve_deploy.outputs.deploy_sha }} FETCH_REF: ${{ steps.resolve_deploy.outputs.fetch_ref }} uses: appleboy/ssh-action@v1.2.2 with: host: ${{ secrets.VPS_HOST }} username: aoagent key: ${{ secrets.VPS_SSH_KEY }} fingerprint: ${{ secrets.VPS_HOST_FINGERPRINT }} envs: DEPLOY_SHA,FETCH_REF script: | set -e cd /home/aoagent/agent-orchestrator echo "==> Ensuring full worktree..." if [ "$(git config --bool core.sparseCheckout || echo false)" = "true" ]; then git sparse-checkout disable fi echo "==> Fetching latest changes for $FETCH_REF..." git fetch origin "$FETCH_REF" echo "==> Current: $(git rev-parse --short HEAD)" echo "==> Deploying target SHA: ${DEPLOY_SHA:0:7}" git checkout --force "$DEPLOY_SHA" echo "==> Updated: $(git rev-parse --short HEAD)" test -f packages/web/server/start-all.ts echo "==> Installing dependencies..." pnpm install --frozen-lockfile echo "==> Building..." pnpm build echo "==> Release requirement: AO_ALLOW_FILESYSTEM_BROWSE=1 must be present in the ao web runtime for the multi-project add-project browser." echo "==> Restarting services..." pm2 restart ao --update-env pm2 restart openclaw-gateway --update-env || true echo "==> Deploy complete: $(git rev-parse --short HEAD)"