mirror of
https://gitea.ingwaz.work/Ingwaz/openbrain-mcp.git
synced 2026-06-16 06:17:08 +00:00
Adds OPENBRAIN__TRUTH__ENABLED=true to the .env upsert list in the deploy bootstrap step so the background truth scoring worker runs automatically after every deployment.
297 lines
13 KiB
YAML
297 lines
13 KiB
YAML
name: OpenBrain MCP Build and Deploy
|
|
|
|
on:
|
|
push:
|
|
branches:
|
|
- '**'
|
|
|
|
jobs:
|
|
build-and-deploy:
|
|
runs-on: ubuntu-latest
|
|
env:
|
|
CARGO_TERM_COLOR: always
|
|
VPS_HOST: ${{ secrets.VPS_HOST }}
|
|
VPS_USER: ${{ secrets.VPS_USER }}
|
|
OPENBRAIN__DATABASE__HOST: ${{ secrets.OPENBRAIN__DATABASE__HOST }}
|
|
OPENBRAIN__DATABASE__PORT: ${{ secrets.OPENBRAIN__DATABASE__PORT }}
|
|
OPENBRAIN__DATABASE__NAME: ${{ secrets.OPENBRAIN__DATABASE__NAME }}
|
|
OPENBRAIN__DATABASE__USER: ${{ secrets.OPENBRAIN__DATABASE__USER }}
|
|
OPENBRAIN__DATABASE__PASSWORD: ${{ secrets.OPENBRAIN__DATABASE__PASSWORD }}
|
|
OPENBRAIN__DATABASE__POOL_SIZE: ${{ secrets.OPENBRAIN__DATABASE__POOL_SIZE }}
|
|
OPENBRAIN__AUTH__API_KEYS: ${{ secrets.OPENBRAIN__AUTH__API_KEYS }}
|
|
DEPLOY_DIR: /opt/openbrain-mcp
|
|
SERVICE_NAME: openbrain-mcp
|
|
steps:
|
|
- name: Install prerequisites
|
|
run: |
|
|
set -euxo pipefail
|
|
if command -v apt-get >/dev/null 2>&1; then
|
|
if command -v sudo >/dev/null 2>&1; then SUDO=sudo; else SUDO=; fi
|
|
$SUDO apt-get update
|
|
$SUDO apt-get install -y --no-install-recommends git ca-certificates curl
|
|
elif command -v apk >/dev/null 2>&1; then
|
|
apk add --no-cache git ca-certificates curl
|
|
fi
|
|
|
|
- name: Checkout repository
|
|
run: |
|
|
set -euxo pipefail
|
|
git clone --depth 1 "https://${{ github.token }}@gitea.ingwaz.work/${{ github.repository }}.git" .
|
|
git fetch origin "${{ github.ref }}"
|
|
git checkout FETCH_HEAD
|
|
|
|
- name: Install build dependencies
|
|
run: |
|
|
set -euxo pipefail
|
|
if command -v sudo >/dev/null 2>&1; then SUDO=sudo; else SUDO=; fi
|
|
$SUDO apt-get install -y --no-install-recommends build-essential pkg-config libssl-dev openssh-client
|
|
|
|
- name: Install Rust toolchain
|
|
run: |
|
|
set -euxo pipefail
|
|
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable
|
|
. "$HOME/.cargo/env"
|
|
rustc --version
|
|
cargo --version
|
|
|
|
- name: CI checks
|
|
run: |
|
|
set -euxo pipefail
|
|
. "$HOME/.cargo/env"
|
|
cargo check
|
|
cargo test --no-run
|
|
cargo test --lib
|
|
|
|
- name: Build release
|
|
run: |
|
|
set -euxo pipefail
|
|
. "$HOME/.cargo/env"
|
|
cargo build --release
|
|
test -x target/release/openbrain-mcp
|
|
|
|
- name: Generate ephemeral e2e API key
|
|
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master'
|
|
run: |
|
|
set -euxo pipefail
|
|
install -d -m 700 .ci
|
|
umask 077
|
|
openssl rand -hex 32 > .ci/openbrain_e2e_key
|
|
|
|
- name: Setup SSH auth
|
|
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master'
|
|
run: |
|
|
set -euxo pipefail
|
|
: "${VPS_HOST:?Set repository secret VPS_HOST}"
|
|
: "${VPS_USER:=root}"
|
|
install -d -m 700 "$HOME/.ssh"
|
|
printf '%s\n' "${{ secrets.VPS_SSH_KEY }}" > "$HOME/.ssh/deploy_key"
|
|
chmod 600 "$HOME/.ssh/deploy_key"
|
|
ssh-keyscan -H "$VPS_HOST" >> "$HOME/.ssh/known_hosts"
|
|
|
|
- name: Deploy artifacts
|
|
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master'
|
|
run: |
|
|
set -euxo pipefail
|
|
: "${VPS_HOST:?Set repository secret VPS_HOST}"
|
|
: "${VPS_USER:=root}"
|
|
SSH="ssh -i $HOME/.ssh/deploy_key -o IdentitiesOnly=yes"
|
|
SCP="scp -i $HOME/.ssh/deploy_key -o IdentitiesOnly=yes"
|
|
|
|
# Stop service before deploying to avoid "Text file busy" error
|
|
$SSH "$VPS_USER@$VPS_HOST" "systemctl stop $SERVICE_NAME 2>/dev/null || true"
|
|
|
|
$SSH "$VPS_USER@$VPS_HOST" "mkdir -p $DEPLOY_DIR/.gitea $DEPLOY_DIR/models $DEPLOY_DIR/logs $DEPLOY_DIR/lib"
|
|
|
|
$SCP target/release/openbrain-mcp "$VPS_USER@$VPS_HOST:$DEPLOY_DIR/openbrain-mcp"
|
|
$SCP .gitea/openbrain.service "$VPS_USER@$VPS_HOST:/etc/systemd/system/$SERVICE_NAME.service"
|
|
$SCP .gitea/download-model.sh "$VPS_USER@$VPS_HOST:$DEPLOY_DIR/.gitea/download-model.sh"
|
|
|
|
- name: Bootstrap VPS and restart service
|
|
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master'
|
|
run: |
|
|
set -euxo pipefail
|
|
: "${VPS_HOST:?Set repository secret VPS_HOST}"
|
|
: "${VPS_USER:=root}"
|
|
SSH="ssh -i $HOME/.ssh/deploy_key -o IdentitiesOnly=yes"
|
|
EPHEMERAL_E2E_KEY="$(cat .ci/openbrain_e2e_key)"
|
|
EFFECTIVE_OPENBRAIN__AUTH__API_KEYS="$EPHEMERAL_E2E_KEY"
|
|
if [[ -n "${OPENBRAIN__AUTH__API_KEYS:-}" ]]; then
|
|
EFFECTIVE_OPENBRAIN__AUTH__API_KEYS="${OPENBRAIN__AUTH__API_KEYS},${EPHEMERAL_E2E_KEY}"
|
|
fi
|
|
|
|
: "${OPENBRAIN__DATABASE__HOST:?Set repository secret OPENBRAIN__DATABASE__HOST}"
|
|
: "${OPENBRAIN__DATABASE__NAME:?Set repository secret OPENBRAIN__DATABASE__NAME}"
|
|
: "${OPENBRAIN__DATABASE__USER:?Set repository secret OPENBRAIN__DATABASE__USER}"
|
|
: "${OPENBRAIN__DATABASE__PASSWORD:?Set repository secret OPENBRAIN__DATABASE__PASSWORD}"
|
|
: "${OPENBRAIN__DATABASE__PORT:=5432}"
|
|
: "${OPENBRAIN__DATABASE__POOL_SIZE:=10}"
|
|
|
|
$SSH "$VPS_USER@$VPS_HOST" "\
|
|
DEPLOY_DIR=$DEPLOY_DIR \
|
|
SERVICE_USER=openbrain \
|
|
SERVICE_GROUP=openbrain \
|
|
ORT_VERSION=1.24.3 \
|
|
OPENBRAIN__DATABASE__HOST='$OPENBRAIN__DATABASE__HOST' \
|
|
OPENBRAIN__DATABASE__PORT='$OPENBRAIN__DATABASE__PORT' \
|
|
OPENBRAIN__DATABASE__NAME='$OPENBRAIN__DATABASE__NAME' \
|
|
OPENBRAIN__DATABASE__USER='$OPENBRAIN__DATABASE__USER' \
|
|
OPENBRAIN__DATABASE__PASSWORD='$OPENBRAIN__DATABASE__PASSWORD' \
|
|
OPENBRAIN__DATABASE__POOL_SIZE='$OPENBRAIN__DATABASE__POOL_SIZE' \
|
|
OPENBRAIN__AUTH__API_KEYS='$EFFECTIVE_OPENBRAIN__AUTH__API_KEYS' \
|
|
bash -s" <<'EOS'
|
|
set -euo pipefail
|
|
DEPLOY_DIR="${DEPLOY_DIR:-/opt/openbrain-mcp}"
|
|
SERVICE_USER="${SERVICE_USER:-openbrain}"
|
|
SERVICE_GROUP="${SERVICE_GROUP:-openbrain}"
|
|
ORT_VERSION="${ORT_VERSION:-1.24.3}"
|
|
|
|
if command -v apt-get >/dev/null 2>&1; then
|
|
export DEBIAN_FRONTEND=noninteractive
|
|
apt-get update
|
|
apt-get install -y --no-install-recommends ca-certificates curl tar libssl3
|
|
fi
|
|
|
|
if ! getent group "$SERVICE_GROUP" >/dev/null 2>&1; then
|
|
groupadd --system "$SERVICE_GROUP"
|
|
fi
|
|
if ! id -u "$SERVICE_USER" >/dev/null 2>&1; then
|
|
useradd --system --gid "$SERVICE_GROUP" --home "$DEPLOY_DIR" --shell /usr/sbin/nologin "$SERVICE_USER"
|
|
fi
|
|
|
|
install -d -m 0755 "$DEPLOY_DIR" "$DEPLOY_DIR/models" "$DEPLOY_DIR/logs" "$DEPLOY_DIR/lib"
|
|
|
|
ARCH="$(uname -m)"
|
|
case "$ARCH" in
|
|
x86_64) ORT_ARCH="x64" ;;
|
|
aarch64|arm64) ORT_ARCH="aarch64" ;;
|
|
*) echo "Unsupported arch: $ARCH"; exit 1 ;;
|
|
esac
|
|
|
|
if [[ ! -f "$DEPLOY_DIR/lib/libonnxruntime.so" ]]; then
|
|
TMP_DIR="$(mktemp -d)"
|
|
ORT_TGZ="onnxruntime-linux-${ORT_ARCH}-${ORT_VERSION}.tgz"
|
|
ORT_URL="https://github.com/microsoft/onnxruntime/releases/download/v${ORT_VERSION}/${ORT_TGZ}"
|
|
curl -fL "$ORT_URL" -o "$TMP_DIR/$ORT_TGZ"
|
|
tar -xzf "$TMP_DIR/$ORT_TGZ" -C "$TMP_DIR"
|
|
ORT_ROOT="$TMP_DIR/onnxruntime-linux-${ORT_ARCH}-${ORT_VERSION}"
|
|
cp "$ORT_ROOT/lib/libonnxruntime.so" "$DEPLOY_DIR/lib/libonnxruntime.so"
|
|
cp "$ORT_ROOT/lib/libonnxruntime.so.${ORT_VERSION}" "$DEPLOY_DIR/lib/libonnxruntime.so.${ORT_VERSION}" || true
|
|
rm -rf "$TMP_DIR"
|
|
fi
|
|
|
|
ENV_FILE="$DEPLOY_DIR/.env"
|
|
if [[ ! -f "$ENV_FILE" ]]; then
|
|
if [[ -f "$DEPLOY_DIR/.env.example" ]]; then cp "$DEPLOY_DIR/.env.example" "$ENV_FILE"; else touch "$ENV_FILE"; fi
|
|
fi
|
|
|
|
upsert_env() {
|
|
local key="$1"
|
|
local value="$2"
|
|
local escaped_value
|
|
escaped_value="$(printf '%s' "$value" | sed -e 's/[\\&|]/\\&/g')"
|
|
if grep -qE "^${key}=" "$ENV_FILE"; then
|
|
sed -i "s|^${key}=.*|${key}=${escaped_value}|" "$ENV_FILE"
|
|
else
|
|
printf '%s=%s\n' "$key" "$value" >> "$ENV_FILE"
|
|
fi
|
|
}
|
|
|
|
upsert_env "OPENBRAIN__DATABASE__HOST" "$OPENBRAIN__DATABASE__HOST"
|
|
upsert_env "OPENBRAIN__DATABASE__PORT" "$OPENBRAIN__DATABASE__PORT"
|
|
upsert_env "OPENBRAIN__DATABASE__NAME" "$OPENBRAIN__DATABASE__NAME"
|
|
upsert_env "OPENBRAIN__DATABASE__USER" "$OPENBRAIN__DATABASE__USER"
|
|
upsert_env "OPENBRAIN__DATABASE__PASSWORD" "$OPENBRAIN__DATABASE__PASSWORD"
|
|
upsert_env "OPENBRAIN__DATABASE__POOL_SIZE" "$OPENBRAIN__DATABASE__POOL_SIZE"
|
|
if [[ -n "${OPENBRAIN__AUTH__API_KEYS:-}" ]]; then
|
|
upsert_env "OPENBRAIN__AUTH__ENABLED" "true"
|
|
upsert_env "OPENBRAIN__AUTH__API_KEYS" "$OPENBRAIN__AUTH__API_KEYS"
|
|
fi
|
|
upsert_env "OPENBRAIN__EMBEDDING__MODEL_PATH" "$DEPLOY_DIR/models/all-MiniLM-L6-v2"
|
|
upsert_env "ORT_DYLIB_PATH" "$DEPLOY_DIR/lib/libonnxruntime.so"
|
|
upsert_env "OPENBRAIN__SERVER__HOST" "0.0.0.0"
|
|
upsert_env "OPENBRAIN__TRUTH__ENABLED" "true"
|
|
|
|
chmod +x "$DEPLOY_DIR/openbrain-mcp" "$DEPLOY_DIR/.gitea/download-model.sh"
|
|
chown -R "$SERVICE_USER:$SERVICE_GROUP" "$DEPLOY_DIR"
|
|
EOS
|
|
|
|
$SSH "$VPS_USER@$VPS_HOST" "DEPLOY_DIR=$DEPLOY_DIR bash $DEPLOY_DIR/.gitea/download-model.sh"
|
|
$SSH "$VPS_USER@$VPS_HOST" "cd $DEPLOY_DIR && ./openbrain-mcp migrate"
|
|
|
|
$SSH "$VPS_USER@$VPS_HOST" "systemctl daemon-reload"
|
|
$SSH "$VPS_USER@$VPS_HOST" "systemctl enable $SERVICE_NAME"
|
|
$SSH "$VPS_USER@$VPS_HOST" "systemctl restart $SERVICE_NAME"
|
|
|
|
- name: Verify deployment
|
|
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master'
|
|
run: |
|
|
set -euxo pipefail
|
|
: "${VPS_HOST:?Set repository secret VPS_HOST}"
|
|
: "${VPS_USER:=root}"
|
|
SSH="ssh -i $HOME/.ssh/deploy_key -o IdentitiesOnly=yes"
|
|
|
|
sleep 5
|
|
$SSH "$VPS_USER@$VPS_HOST" "systemctl status $SERVICE_NAME --no-pager || journalctl -u $SERVICE_NAME --no-pager -n 80"
|
|
curl -fsS "http://$VPS_HOST:3100/health"
|
|
curl -fsS "http://$VPS_HOST:3100/ready"
|
|
|
|
- name: Run VPS e2e tests
|
|
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master'
|
|
env:
|
|
OPENBRAIN_E2E_REMOTE: "true"
|
|
OPENBRAIN_E2E_BASE_URL: http://${{ secrets.VPS_HOST }}:3100
|
|
OPENBRAIN__AUTH__ENABLED: "true"
|
|
OPENBRAIN__TRUTH__ENABLED: "true"
|
|
run: |
|
|
set -euxo pipefail
|
|
export OPENBRAIN_E2E_API_KEY="$(cat .ci/openbrain_e2e_key)"
|
|
. "$HOME/.cargo/env"
|
|
cargo test --test e2e_mcp -- --test-threads=1
|
|
cargo test --test e2e_truth -- --test-threads=1
|
|
|
|
- name: Remove ephemeral e2e key and restart service
|
|
if: always() && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master')
|
|
run: |
|
|
set -euxo pipefail
|
|
: "${VPS_HOST:?Set repository secret VPS_HOST}"
|
|
: "${VPS_USER:=root}"
|
|
SSH="ssh -i $HOME/.ssh/deploy_key -o IdentitiesOnly=yes"
|
|
EPHEMERAL_E2E_KEY="$(cat .ci/openbrain_e2e_key)"
|
|
BASE_AUTH_KEYS="${OPENBRAIN__AUTH__API_KEYS:-}"
|
|
|
|
$SSH "$VPS_USER@$VPS_HOST" "\
|
|
DEPLOY_DIR=$DEPLOY_DIR \
|
|
OPENBRAIN__AUTH__API_KEYS='$BASE_AUTH_KEYS' \
|
|
bash -s" <<'EOS'
|
|
set -euo pipefail
|
|
DEPLOY_DIR="${DEPLOY_DIR:-/opt/openbrain-mcp}"
|
|
ENV_FILE="$DEPLOY_DIR/.env"
|
|
|
|
upsert_env() {
|
|
local key="$1"
|
|
local value="$2"
|
|
local escaped_value
|
|
escaped_value="$(printf '%s' "$value" | sed -e 's/[\\&|]/\\&/g')"
|
|
if grep -qE "^${key}=" "$ENV_FILE"; then
|
|
sed -i "s|^${key}=.*|${key}=${escaped_value}|" "$ENV_FILE"
|
|
else
|
|
printf '%s=%s\n' "$key" "$value" >> "$ENV_FILE"
|
|
fi
|
|
}
|
|
|
|
if [[ -n "${OPENBRAIN__AUTH__API_KEYS:-}" ]]; then
|
|
upsert_env "OPENBRAIN__AUTH__ENABLED" "true"
|
|
upsert_env "OPENBRAIN__AUTH__API_KEYS" "$OPENBRAIN__AUTH__API_KEYS"
|
|
else
|
|
upsert_env "OPENBRAIN__AUTH__ENABLED" "false"
|
|
sed -i '/^OPENBRAIN__AUTH__API_KEYS=/d' "$ENV_FILE"
|
|
fi
|
|
EOS
|
|
|
|
$SSH "$VPS_USER@$VPS_HOST" "systemctl restart $SERVICE_NAME"
|
|
|
|
- name: Cleanup SSH key
|
|
if: always()
|
|
run: |
|
|
rm -f "$HOME/.ssh/deploy_key"
|