diff --git a/.forgejo/workflows/test-and-deploy.yml b/.forgejo/workflows/test-and-deploy.yml new file mode 100644 index 0000000..d38656b --- /dev/null +++ b/.forgejo/workflows/test-and-deploy.yml @@ -0,0 +1,24 @@ +name: test-and-deploy + +on: + push: + branches: [main] + pull_request: + workflow_dispatch: + +jobs: + test: + runs-on: 138 + steps: + - uses: actions/checkout@v4 + - name: Unit tests + run: bash scripts/test-138.sh + + deploy-138: + needs: test + if: success() && github.event_name != 'pull_request' && github.ref == 'refs/heads/main' + runs-on: 138 + steps: + - uses: actions/checkout@v4 + - name: Deploy the host checkout + run: bash scripts/deploy-138.sh diff --git a/scripts/deploy-138.sh b/scripts/deploy-138.sh new file mode 100755 index 0000000..c8d5c89 --- /dev/null +++ b/scripts/deploy-138.sh @@ -0,0 +1,99 @@ +#!/usr/bin/env bash +# Test, then replace the Python MCP host checkout on ns1. +# Does not restart verae-mcp-timestamping (the live listener is the Go binary), +# verae-timestamping-svc, or forgejo-runner, and does not read secret files. +set -euo pipefail + +LOCK="${LOCK:-/tmp/verae-mcp-py-deploy.lock}" +DEST="${DEST:-/opt/verae/verae-mcp-timestamping-server}" +BACKUP_ROOT="${BACKUP_ROOT:-/SSD2/verae-deploy-backups}" +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +SCHEMA_FILE="src/verae_mcp_timestamping_server/metadata_history.py" + +mkdir -p "$(dirname "$LOCK")" +exec 9>"$LOCK" +if ! flock -w 900 9; then + echo "another python mcp deploy holds the lock" >&2 + exit 1 +fi + +prepare_cache() { + sudo mkdir -p "$BACKUP_ROOT" + sudo chmod 1777 "$BACKUP_ROOT" + sudo mkdir -p "$BACKUP_ROOT/uv-cache-$USER" + sudo chown "$USER:$USER" "$BACKUP_ROOT/uv-cache-$USER" + export UV_CACHE_DIR="$BACKUP_ROOT/uv-cache-$USER" + export UV_LINK_MODE=copy +} + +run_tests() { + prepare_cache + cd "$ROOT" + uv sync --frozen --group dev + uv run pytest -q + if ! grep -q 'verae-meta-git/1' "$SCHEMA_FILE"; then + echo "checkout is missing verae-meta-git/1" >&2 + exit 1 + fi +} + +probe_tree() { + local tree="$1" + grep -q 'verae-meta-git/1' "$tree/$SCHEMA_FILE" + sudo -u verae-mcp env HOME=/var/lib/verae-mcp \ + "$tree/.venv/bin/python" -c 'import verae_mcp_timestamping_server.metadata_history as m; assert m.SCHEMA == "verae-meta-git/1"' +} + +install_tree() { + local stage previous ts sha + ts="$(date -u +%Y%m%dT%H%M%SZ)" + sha="$(git -C "$ROOT" rev-parse HEAD)" + stage="$(mktemp -d "$BACKUP_ROOT/py-mcp-stage.XXXXXX")" + sudo rsync -a \ + --exclude '.git/' \ + --exclude '.venv/' \ + --exclude '__pycache__/' \ + --exclude '.pytest_cache/' \ + --exclude 'debug-trace/' \ + "$ROOT/" "$stage/" + printf '%s\n' "$sha" | sudo tee "$stage/DEPLOYED_SHA" >/dev/null + if [[ -d "$DEST/.venv" ]]; then + sudo mv "$DEST/.venv" "$stage/.venv" + fi + sudo chown -R verae-mcp:verae-mcp "$stage" + sudo mkdir -p "$BACKUP_ROOT/uv-cache-verae-mcp" + sudo chown verae-mcp:verae-mcp "$BACKUP_ROOT/uv-cache-verae-mcp" + if ! sudo -u verae-mcp env HOME=/var/lib/verae-mcp UV_CACHE_DIR="$BACKUP_ROOT/uv-cache-verae-mcp" UV_LINK_MODE=copy \ + uv sync --frozen --group dev --directory "$stage"; then + sudo rm -rf "$stage" + echo "uv sync failed" >&2 + return 1 + fi + if ! probe_tree "$stage"; then + sudo rm -rf "$stage" + echo "staged python checkout failed probe" >&2 + return 1 + fi + previous="" + if [[ -d "$DEST" ]]; then + previous="$BACKUP_ROOT/py-mcp-src-${ts}" + sudo mv "$DEST" "$previous" + fi + if ! sudo mv "$stage" "$DEST"; then + if [[ -n "$previous" ]]; then + sudo mv "$previous" "$DEST" + fi + echo "could not move the python checkout into place" >&2 + return 1 + fi + sudo chown -R verae-mcp:verae-mcp "$DEST" + echo "deployed ${ts} ${sha} checkout probe ok" +} + +run_tests +if [[ "${1:-}" == "--test-only" ]]; then + echo "tests ok" + exit 0 +fi + +install_tree diff --git a/scripts/test-138.sh b/scripts/test-138.sh new file mode 100755 index 0000000..a6b8bb0 --- /dev/null +++ b/scripts/test-138.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +# Unit-test the Python MCP server. Does not restart a service. +set -euo pipefail +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +exec bash "$ROOT/scripts/deploy-138.sh" --test-only