The OpenClaw session contract smoke and SSE long-task stream checks used to live in the Ansible validate role and ran during the Deploy stage. They depend on the public OpenClaw gateway producing a 'pong' reply, which the bridge itself cannot guarantee end-to-end. When the gateway returned an empty completion envelope (CI run 27010307958), the entire Deploy job failed even though the bridge binary had been installed and was healthy. Move the lightweight session contract check into the GitHub Actions validate stage as a new script. Deploy now only asserts the bridge's own state (binary, ports, /api/ping, /acp/rpc capabilities, routing.resolve), and the OpenClaw contract check runs in validate where the release-blocking failure belongs.
330 lines
12 KiB
YAML
330 lines
12 KiB
YAML
name: Pipeline
|
|
|
|
on:
|
|
pull_request:
|
|
branches: [main]
|
|
push:
|
|
branches: ['**']
|
|
workflow_dispatch:
|
|
inputs:
|
|
target_host:
|
|
description: "Ansible inventory host or alias"
|
|
required: false
|
|
default: "jp-xhttp-contabo.svc.plus"
|
|
type: string
|
|
run_apply:
|
|
description: "Apply deployment (false = dry-run)"
|
|
required: true
|
|
default: true
|
|
type: boolean
|
|
internal_service_token:
|
|
description: "Optional ACP auth token for deploy"
|
|
required: false
|
|
default: ""
|
|
type: string
|
|
|
|
permissions:
|
|
contents: read
|
|
packages: write
|
|
|
|
concurrency:
|
|
group: pipeline-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
defaults:
|
|
run:
|
|
shell: bash
|
|
|
|
env:
|
|
DEFAULT_TARGET_HOST: jp-xhttp-contabo.svc.plus
|
|
|
|
jobs:
|
|
production_state:
|
|
name: Production State
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
production_image: ${{ steps.production_state.outputs.production_image }}
|
|
production_tag: ${{ steps.production_state.outputs.production_tag }}
|
|
production_commit: ${{ steps.production_state.outputs.production_commit }}
|
|
production_version: ${{ steps.production_state.outputs.production_version }}
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
|
|
- name: Probe current production bridge
|
|
id: production_state
|
|
env:
|
|
BRIDGE_SERVER_URL: https://xworkmate-bridge.svc.plus
|
|
BRIDGE_AUTH_TOKEN: ${{ secrets.INTERNAL_SERVICE_TOKEN }}
|
|
run: |
|
|
while IFS='=' read -r key value; do
|
|
echo "${key}=${value}" >> "$GITHUB_OUTPUT"
|
|
done < <(bash ./scripts/github-actions/report-production-state.sh "${BRIDGE_SERVER_URL}")
|
|
|
|
- name: Explain branch push vs production
|
|
if: ${{ github.event_name == 'push' }}
|
|
run: |
|
|
echo "::notice title=Production state::Current production bridge before this run is ${{ steps.production_state.outputs.production_commit }}."
|
|
|
|
prep:
|
|
name: Prep
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
|
|
- name: Set up Go
|
|
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
|
|
with:
|
|
go-version-file: go.mod
|
|
cache: true
|
|
|
|
- name: Install golangci-lint
|
|
run: |
|
|
GOBIN="$(go env GOPATH)/bin"
|
|
go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.64.8
|
|
echo "${GOBIN}" >> "$GITHUB_PATH"
|
|
|
|
- name: Run lint and tests
|
|
run: bash ./scripts/github-actions/prep.sh
|
|
|
|
build:
|
|
name: Build
|
|
needs:
|
|
- production_state
|
|
- prep
|
|
runs-on: ubuntu-latest
|
|
env:
|
|
SERVICE_REGISTRY: ghcr.io
|
|
SERVICE_IMAGE_REPO_OWNER: ${{ vars.IMAGE_REPO_OWNER || github.repository_owner }}
|
|
SERVICE_IMAGE_NAME: xworkmate-bridge
|
|
outputs:
|
|
artifact_name: ${{ steps.artifact_meta.outputs.artifact_name }}
|
|
binary_artifact_name: ${{ steps.binary_artifact_meta.outputs.binary_artifact_name }}
|
|
service_image_ref: ${{ steps.service_ref.outputs.image_ref }}
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
|
|
- name: Set up QEMU
|
|
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
|
|
|
|
- name: Set up Docker Buildx
|
|
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
|
|
|
- name: Log in to GHCR
|
|
if: ${{ github.event_name != 'pull_request' }}
|
|
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
|
|
with:
|
|
registry: ghcr.io
|
|
username: ${{ vars.GHCR_USERNAME || github.repository_owner }}
|
|
password: ${{ secrets.GHCR_TOKEN || github.token }}
|
|
|
|
- name: Resolve service image ref
|
|
id: service_ref
|
|
run: bash ./scripts/github-actions/resolve-service-image-ref.sh
|
|
|
|
- name: Build and optionally push service image
|
|
uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0
|
|
with:
|
|
context: .
|
|
file: Dockerfile
|
|
platforms: linux/amd64
|
|
push: ${{ github.event_name != 'pull_request' }}
|
|
tags: ${{ steps.service_ref.outputs.image_ref }}
|
|
build-args: |
|
|
BUILD_COMMIT=${{ github.sha }}
|
|
BUILD_DATE=${{ github.event.head_commit.timestamp || github.event.repository.pushed_at }}
|
|
BUILD_VERSION=v1.0-beta2
|
|
|
|
- name: Build native linux service binary
|
|
run: bash ./scripts/github-actions/build-artifact.sh dist/native-binary
|
|
|
|
- name: Write image ref artifact
|
|
run: |
|
|
set -euo pipefail
|
|
mkdir -p dist
|
|
printf '%s\n' "${{ steps.service_ref.outputs.image_ref }}" > dist/service-image-ref.txt
|
|
|
|
- name: Record artifact metadata
|
|
id: artifact_meta
|
|
run: bash ./scripts/github-actions/resolve-artifact-name.sh
|
|
|
|
- name: Record native binary artifact metadata
|
|
id: binary_artifact_meta
|
|
run: printf 'binary_artifact_name=xworkmate-bridge-linux-amd64-%s\n' "${GITHUB_SHA}" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Upload image ref artifact
|
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
|
with:
|
|
name: ${{ steps.artifact_meta.outputs.artifact_name }}
|
|
path: dist/service-image-ref.txt
|
|
|
|
- name: Upload native linux service binary
|
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
|
with:
|
|
name: ${{ steps.binary_artifact_meta.outputs.binary_artifact_name }}
|
|
path: dist/native-binary/xworkmate-bridge
|
|
|
|
deploy:
|
|
name: Deploy
|
|
needs: build
|
|
if: ${{ github.event_name != 'pull_request' }}
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
run_apply: ${{ steps.deploy_meta.outputs.run_apply }}
|
|
env:
|
|
INTERNAL_SERVICE_TOKEN: ${{ github.event_name == 'workflow_dispatch' && inputs.internal_service_token || secrets.INTERNAL_SERVICE_TOKEN }}
|
|
GHCR_USERNAME: ${{ vars.GHCR_USERNAME || github.repository_owner }}
|
|
GHCR_PASSWORD: ${{ secrets.GHCR_TOKEN || github.token }}
|
|
steps:
|
|
- name: Checkout service repository
|
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
with:
|
|
path: xworkmate-bridge
|
|
|
|
- name: Checkout playbooks repository
|
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
with:
|
|
repository: x-evor/playbooks
|
|
token: ${{ secrets.WORKSPACE_REPO_TOKEN || github.token }}
|
|
path: playbooks
|
|
|
|
- name: Download build image ref artifact
|
|
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4
|
|
with:
|
|
name: ${{ needs.build.outputs.artifact_name }}
|
|
path: xworkmate-bridge/dist/image-artifact
|
|
|
|
- name: Download native linux service binary
|
|
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4
|
|
with:
|
|
name: ${{ needs.build.outputs.binary_artifact_name }}
|
|
path: xworkmate-bridge/dist/native-binary
|
|
|
|
- name: Set up Python
|
|
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
|
|
with:
|
|
python-version: "3.11"
|
|
|
|
- name: Install Ansible runtime
|
|
run: |
|
|
python -m pip install --upgrade pip
|
|
python -m pip install "ansible-core==2.18.3"
|
|
|
|
- name: Resolve deployment settings
|
|
id: deploy_meta
|
|
run: |
|
|
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
|
|
target_host="${{ inputs.target_host }}"
|
|
run_apply="${{ inputs.run_apply }}"
|
|
else
|
|
target_host="${DEFAULT_TARGET_HOST}"
|
|
run_apply="true"
|
|
fi
|
|
|
|
if [[ -z "${target_host}" ]]; then
|
|
target_host="${DEFAULT_TARGET_HOST}"
|
|
fi
|
|
|
|
echo "target_host=${target_host}" >> "$GITHUB_OUTPUT"
|
|
echo "run_apply=${run_apply}" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Prepare runner SSH access
|
|
working-directory: xworkmate-bridge
|
|
env:
|
|
SINGLE_NODE_VPS_SSH_PRIVATE_KEY: ${{ secrets.SINGLE_NODE_VPS_SSH_PRIVATE_KEY }}
|
|
SSH_KNOWN_HOSTS: ${{ secrets.SSH_KNOWN_HOSTS }}
|
|
run: bash ./scripts/github-actions/prepare-ssh.sh "${{ steps.deploy_meta.outputs.target_host }}" "${SSH_KNOWN_HOSTS}"
|
|
|
|
- name: Install native bridge binary on target
|
|
if: ${{ steps.deploy_meta.outputs.run_apply == 'true' }}
|
|
working-directory: xworkmate-bridge
|
|
run: bash ./scripts/github-actions/deploy-native-binary.sh "${{ steps.deploy_meta.outputs.target_host }}" "dist/native-binary/xworkmate-bridge" "$(git rev-parse --short HEAD)"
|
|
|
|
- name: Run Ansible deploy playbook
|
|
working-directory: playbooks
|
|
env:
|
|
ANSIBLE_CONFIG: ./ansible.cfg
|
|
BRIDGE_AUTH_TOKEN: ${{ env.INTERNAL_SERVICE_TOKEN }}
|
|
run: |
|
|
CHECK_MODE_FLAG=""
|
|
if [[ "${{ steps.deploy_meta.outputs.run_apply }}" != "true" ]]; then
|
|
CHECK_MODE_FLAG="-C"
|
|
fi
|
|
ansible-playbook -i inventory.ini deploy_xworkmate_bridge_vhosts.yml \
|
|
-D ${CHECK_MODE_FLAG} \
|
|
--tags xworkmate_bridge \
|
|
-l "${{ steps.deploy_meta.outputs.target_host }}"
|
|
|
|
publish_release:
|
|
name: Publish GitHub Release
|
|
needs:
|
|
- build
|
|
- deploy
|
|
- validate
|
|
if: ${{ github.event_name != 'pull_request' && needs.deploy.result == 'success' && needs.validate.result == 'success' }}
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: write
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
|
|
- name: Download image ref artifact
|
|
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4
|
|
with:
|
|
name: ${{ needs.build.outputs.artifact_name }}
|
|
path: dist
|
|
|
|
- name: Resolve release metadata
|
|
id: release_meta
|
|
run: bash ./scripts/github-actions/resolve-release-metadata.sh
|
|
|
|
- name: Publish GitHub release
|
|
env:
|
|
GITHUB_TOKEN: ${{ github.token }}
|
|
run: |
|
|
tag="${{ steps.release_meta.outputs.tag }}"
|
|
asset="dist/service-image-ref.txt#xworkmate-bridge-service-image-ref.txt"
|
|
if gh release view "${tag}" --repo "${{ github.repository }}" >/dev/null 2>&1; then
|
|
gh release upload "${tag}" "${asset}" \
|
|
--repo "${{ github.repository }}" \
|
|
--clobber
|
|
else
|
|
gh release create "${tag}" "${asset}" \
|
|
--repo "${{ github.repository }}" \
|
|
--target "${{ github.sha }}" \
|
|
--title "${{ steps.release_meta.outputs.title }}" \
|
|
--notes "Automated release for ${GITHUB_SHA}."
|
|
fi
|
|
|
|
validate:
|
|
name: Validate
|
|
needs:
|
|
- build
|
|
- deploy
|
|
if: ${{ needs.deploy.result == 'success' && needs.deploy.outputs.run_apply == 'true' }}
|
|
runs-on: ubuntu-latest
|
|
env:
|
|
BRIDGE_SERVER_URL: https://xworkmate-bridge.svc.plus
|
|
INTERNAL_SERVICE_TOKEN: ${{ secrets.INTERNAL_SERVICE_TOKEN }}
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
|
|
- name: Validate deployed endpoints
|
|
env:
|
|
BRIDGE_AUTH_TOKEN: ${{ env.INTERNAL_SERVICE_TOKEN }}
|
|
run: bash ./scripts/github-actions/validate-deploy.sh "$(git rev-parse --short HEAD)" "${BRIDGE_SERVER_URL}"
|
|
|
|
- name: Validate public ACP contract
|
|
env:
|
|
BRIDGE_AUTH_TOKEN: ${{ env.INTERNAL_SERVICE_TOKEN }}
|
|
run: bash ./scripts/github-actions/verify-public-rpc-contract.sh
|
|
|
|
- name: Validate OpenClaw session contract
|
|
env:
|
|
BRIDGE_AUTH_TOKEN: ${{ env.INTERNAL_SERVICE_TOKEN }}
|
|
run: bash ./scripts/github-actions/validate-openclaw-session.sh
|