diff --git a/.github/workflows/pipeline.yml b/.github/workflows/pipeline.yml index 0245b84..3350f9d 100644 --- a/.github/workflows/pipeline.yml +++ b/.github/workflows/pipeline.yml @@ -174,6 +174,11 @@ jobs: needs: deploy if: ${{ needs.deploy.result == 'success' && needs.deploy.outputs.run_apply == 'true' }} runs-on: ubuntu-latest + env: + BRIDGE_SERVER_URL: ${{ vars.BRIDGE_SERVER_URL || 'https://xworkmate-bridge.svc.plus' }} + ACP_INGRESS_URL: ${{ vars.ACP_INGRESS_URL || 'https://acp-server.svc.plus' }} + OPENCLAW_URL: ${{ vars.OPENCLAW_URL || 'wss://openclaw.svc.plus' }} + BRIDGE_AUTH_TOKEN: ${{ secrets.BRIDGE_AUTH_TOKEN || secrets.INTERNAL_SERVICE_TOKEN }} steps: - name: Checkout uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 diff --git a/scripts/github-actions/validate-deploy.sh b/scripts/github-actions/validate-deploy.sh index d4c4319..3aee7d6 100644 --- a/scripts/github-actions/validate-deploy.sh +++ b/scripts/github-actions/validate-deploy.sh @@ -1,31 +1,96 @@ #!/usr/bin/env bash set -euo pipefail -BASE_URL="${1:-https://xworkmate-bridge.svc.plus}" -INGRESS_URL="${2:-https://acp-server.svc.plus}" +normalize_url() { + local value="$1" + if [[ "${value}" =~ ^https:([^/].*)$ ]]; then + printf 'https://%s\n' "${BASH_REMATCH[1]}" + return + fi + if [[ "${value}" =~ ^http:([^/].*)$ ]]; then + printf 'http://%s\n' "${BASH_REMATCH[1]}" + return + fi + printf '%s\n' "${value}" +} -bridge_root="$(curl -fsS "${BASE_URL}/")" -test "${bridge_root}" = "xworkmate-bridge is running" +websocket_probe_url() { + local value="$1" + if [[ "${value}" =~ ^wss://(.*)$ ]]; then + printf 'https://%s\n' "${BASH_REMATCH[1]}" + return + fi + if [[ "${value}" =~ ^ws://(.*)$ ]]; then + printf 'http://%s\n' "${BASH_REMATCH[1]}" + return + fi + printf '%s\n' "${value}" +} -codex_root="$(curl -fsS "${INGRESS_URL}/codex")" -test "${codex_root}" = "xworkmate-bridge is running" +BASE_URL="$(normalize_url "${BRIDGE_SERVER_URL:-${1:-https://xworkmate-bridge.svc.plus}}")" +INGRESS_URL="$(normalize_url "${ACP_INGRESS_URL:-${2:-https://acp-server.svc.plus}}")" +AUTH_TOKEN="${BRIDGE_AUTH_TOKEN:-${INTERNAL_SERVICE_TOKEN:-${3:-}}}" +OPENCLAW_HTTP_PROBE_URL="$(websocket_probe_url "${OPENCLAW_URL:-${4:-wss://openclaw.svc.plus}}")" -codex_rpc="$( - curl -sS "${INGRESS_URL}/codex/acp/rpc" \ - -H 'Content-Type: application/json' \ - --data '{"jsonrpc":"2.0","id":"cap-1","method":"acp.capabilities"}' -)" -opencode_rpc="$( - curl -sS "${INGRESS_URL}/opencode/acp/rpc" \ - -H 'Content-Type: application/json' \ - --data '{"jsonrpc":"2.0","id":"cap-1","method":"acp.capabilities"}' -)" -gemini_rpc="$( - curl -sS "${INGRESS_URL}/gemini/acp/rpc" \ - -H 'Content-Type: application/json' \ - --data '{"jsonrpc":"2.0","id":"cap-1","method":"acp.capabilities"}' -)" +curl_common=( + --silent + --show-error + --fail + --location + --max-time 20 +) -grep -q '"missing bearer authorization"' <<<"${codex_rpc}" -grep -q '"missing bearer authorization"' <<<"${opencode_rpc}" -grep -q '"providers"' <<<"${gemini_rpc}" +probe_jsonrpc_capabilities() { + local endpoint="$1" + local response + local headers=( + -H 'Content-Type: application/json' + ) + + if [[ -n "${AUTH_TOKEN}" ]]; then + headers+=(-H "Authorization: Bearer ${AUTH_TOKEN}") + fi + + response="$( + curl "${curl_common[@]}" \ + "${headers[@]}" \ + --data '{"jsonrpc":"2.0","id":"cap-1","method":"acp.capabilities"}' \ + "${endpoint}" + )" + + grep -q '"jsonrpc":"2.0"' <<<"${response}" + grep -Eq '"result"|"providers"' <<<"${response}" +} + +probe_safe_http_endpoint() { + local endpoint="$1" + local status + status="$( + curl \ + --silent \ + --show-error \ + --output /dev/null \ + --write-out '%{http_code}' \ + --location \ + --max-time 20 \ + "${endpoint}" + )" + + case "${status}" in + 2*|3*|401|403|404|405|426) + return 0 + ;; + *) + printf 'Unexpected HTTP status %s for %s\n' "${status}" "${endpoint}" >&2 + return 1 + ;; + esac +} + +bridge_root="$(curl "${curl_common[@]}" "${BASE_URL}/")" +grep -qi 'xworkmate-bridge' <<<"${bridge_root}" + +probe_safe_http_endpoint "${OPENCLAW_HTTP_PROBE_URL}" +probe_jsonrpc_capabilities "${INGRESS_URL}/codex/acp/rpc" +probe_jsonrpc_capabilities "${INGRESS_URL}/opencode/acp/rpc" +probe_jsonrpc_capabilities "${INGRESS_URL}/gemini/acp/rpc"