mirror of
https://gitea.ingwaz.work/Ingwaz/openbrain-mcp.git
synced 2026-03-31 14:49:06 +00:00
293 lines
12 KiB
YAML
293 lines
12 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
|
|
|
|
- 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"
|
|
|
|
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"
|
|
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
|
|
|
|
- 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"
|