name: Deploy MineGeo to Dokploy on: push: branches: - master jobs: deploy: # This label must map to a Docker job image in the Gitea act_runner config. runs-on: ubuntu-latest steps: - name: Check out the pushed commit uses: actions/checkout@v4 - name: Upload source and wait for Dokploy build shell: bash env: DOKPLOY_API_KEY: ${{ secrets.DOKPLOY_API_KEY }} DOKPLOY_APPLICATION_ID: ${{ secrets.DOKPLOY_APPLICATION_ID }} run: | set -eu umask 077 # Pin the deployment origin; runner variables and .env cannot override it. readonly DOKPLOY_URL='https://dokploy.minegeo.site' : "${DOKPLOY_API_KEY:?Add the DOKPLOY_API_KEY Actions secret.}" : "${DOKPLOY_APPLICATION_ID:?Add the DOKPLOY_APPLICATION_ID Actions secret.}" temp_root="${RUNNER_TEMP:-/tmp}" mkdir -p "$temp_root" auth_file="$(mktemp "$temp_root/minegeo-dokploy-auth.XXXXXX")" archive_file="$(mktemp "$temp_root/minegeo-dokploy-source.XXXXXX")" response_file="$(mktemp "$temp_root/minegeo-dokploy-response.XXXXXX")" deployment_file="$(mktemp "$temp_root/minegeo-dokploy-status.XXXXXX")" cleanup() { rm -f "$auth_file" "$archive_file" "$response_file" "$deployment_file" } trap cleanup EXIT # Keep the API key out of curl's command-line arguments and workflow logs. printf 'x-api-key: %s\n' "$DOKPLOY_API_KEY" > "$auth_file" unset DOKPLOY_API_KEY # Only committed files from this revision are uploaded. This excludes # .git metadata, local .env files, caches, and untracked workspace data. git archive --format=zip --output="$archive_file" HEAD before_code="$(curl --silent --get \ --header "@$auth_file" \ --data-urlencode "applicationId=$DOKPLOY_APPLICATION_ID" \ --connect-timeout 10 --max-time 30 \ --output "$deployment_file" --write-out '%{http_code}' \ "$DOKPLOY_URL/api/deployment.all" 2>/dev/null || true)" case "$before_code" in 2??) ;; *) echo "Dokploy API preflight failed (HTTP ${before_code:-000}); response withheld." >&2; exit 1 ;; esac previous_id="$(DOKPLOY_STATUS_FILE="$deployment_file" node <<'NODE' const fs = require("node:fs"); const payload = JSON.parse(fs.readFileSync(process.env.DOKPLOY_STATUS_FILE, "utf8")); const rows = Array.isArray(payload) ? payload : [payload?.data, payload?.deployments, payload?.items].find(Array.isArray); if (!rows) process.exit(2); const latest = rows[0]; process.stdout.write(String(latest?.id ?? latest?.deploymentId ?? "")); NODE )" || { echo "Dokploy returned an unexpected deployment list; source was not uploaded." >&2 exit 1 } upload_code="$(curl --silent \ --header "@$auth_file" \ --form-string "applicationId=$DOKPLOY_APPLICATION_ID" \ --form "zip=@$archive_file;type=application/zip" \ --connect-timeout 15 --max-time 1800 \ --output "$response_file" --write-out '%{http_code}' \ "$DOKPLOY_URL/api/application.dropDeployment" 2>/dev/null || true)" case "$upload_code" in 2??) echo "Dokploy accepted the source archive; waiting for the VPS build and deployment." ;; *) echo "Dokploy source upload failed (HTTP ${upload_code:-000}); response withheld." >&2; exit 1 ;; esac attempt=0 while [ "$attempt" -lt 360 ]; do status_code="$(curl --silent --get \ --header "@$auth_file" \ --data-urlencode "applicationId=$DOKPLOY_APPLICATION_ID" \ --connect-timeout 10 --max-time 30 \ --output "$deployment_file" --write-out '%{http_code}' \ "$DOKPLOY_URL/api/deployment.all" 2>/dev/null || true)" case "$status_code" in 2??) ;; *) echo "Dokploy deployment status check failed (HTTP ${status_code:-000})." >&2; exit 1 ;; esac status="$(DOKPLOY_STATUS_FILE="$deployment_file" DOKPLOY_PREVIOUS_DEPLOYMENT_ID="$previous_id" node <<'NODE' const fs = require("node:fs"); const payload = JSON.parse(fs.readFileSync(process.env.DOKPLOY_STATUS_FILE, "utf8")); const rows = Array.isArray(payload) ? payload : [payload?.data, payload?.deployments, payload?.items].find(Array.isArray); if (!rows) process.exit(2); const previousId = process.env.DOKPLOY_PREVIOUS_DEPLOYMENT_ID; const current = rows.find((row) => { const id = String(row?.id ?? row?.deploymentId ?? ""); return id && id !== previousId; }); if (!current) { process.stdout.write("waiting"); } else { const value = String(current.status ?? "unknown").toLowerCase(); process.stdout.write(["done", "error", "running", "queued", "building", "deploying", "pending", "cancelled", "canceled"].includes(value) ? value : "unknown"); } NODE )" || { echo "Dokploy returned an unexpected deployment status; response withheld." >&2 exit 1 } case "$status" in done) echo "Dokploy build and deployment completed successfully." exit 0 ;; error|cancelled|canceled) echo "Dokploy reported a failed deployment; inspect that deployment's logs in Dokploy." >&2 exit 1 ;; *) if [ $((attempt % 3)) -eq 0 ]; then echo "Dokploy build status: $status." fi ;; esac sleep 10 attempt=$((attempt + 1)) done echo "Timed out waiting for Dokploy to finish; inspect the deployment in Dokploy." >&2 exit 1