Properties
category: spec
tags: [ci, cd, github-actions, plan]
last_updated: 2026-03-18
confidence: high

CI/CD Pipeline Plan

Current State

  • Five repos: robot.wtf, otterwiki (fork), otterwiki-api, otterwiki-mcp, otterwiki-semantic-search
  • All have pytest suites; no GitHub Actions workflows exist in any repo
  • Deploy: ansible-playbook ansible/deploy.yml -i ansible/inventory.yml from robot.wtf/ on a laptop
  • Ansible deploy role installs each plugin directly from GitHub main branch via pip install git+https://...
  • Post-deploy smoke test is baked into the playbook

Test Infrastructure Summary

Repo Test runner Dependencies
robot.wtf pytest, no pyproject requirements.txt (Flask, PyJWT, etc.)
otterwiki pytest via pyproject / tox Self-contained
otterwiki-api pytest via pyproject Requires otterwiki installed
otterwiki-semantic-search pytest via pyproject Requires otterwiki installed
otterwiki-mcp pytest + pytest-asyncio Standalone (no otterwiki dep)

Phase 1: Per-Repo CI on PR (do this session)

Add a .github/workflows/ci.yml to each repo. The workflow is nearly identical across all five — differences are only in Python version and install steps.

Template (robot.wtf)

name: CI
on:
  pull_request:
  push:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
      - run: pip install -r requirements.txt
      - run: pip install pytest
      - run: pytest tests/

These depend on otterwiki. Install the fork's wikibot-io branch before installing the plugin:

      - run: pip install "git+https://github.com/schuyler/otterwiki.git@wikibot-io"
      - run: pip install -e ".[dev]"
      - run: pytest

otterwiki-mcp

Standalone; no otterwiki dep. Standard pip install -e ".[dev]" && pytest.

otterwiki (fork)

Uses tox config in pyproject.toml. Can run pip install -e ".[dev]" && pytest directly to avoid tox overhead in CI.

Phase 2: Deploy Script (do this session)

The Ansible command is easy to forget. Add deploy.sh to robot.wtf/:

#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
ansible-playbook ansible/deploy.yml -i ansible/inventory.yml "$@"

This is the entire CD story for now. Manual deploy stays manual — that's the right call given:

  • Deploy requires pulling from GitHub, so all plugin changes must be merged to main first
  • No staging environment exists
  • Smoke test is already part of the playbook

Phase 3: Auto-Deploy (deferred)

Options when ready:

  1. Webhook receiver on VPS — a tiny Flask endpoint that receives a GitHub push event and runs the Ansible playbook. Avoids needing SSH keys in GitHub Actions secrets for the orchestrating repo.
  2. GitHub Actions + SSH — store SSH key as a secret in robot.wtf, run ansible-playbook from Actions on push to main. Simpler setup but couples deploy to robot.wtf CI only.
  3. Repository dispatch — plugin repos trigger a workflow in robot.wtf via the GitHub API on merge to main. Preserves separation of concerns.

Option 3 is cleanest given the multi-repo dependency model: a push to otterwiki-api shouldn't require a commit to robot.wtf to trigger a deploy, but the deploy logic lives in robot.wtf.

Integration Testing

Currently not feasible without a staging environment. The per-repo tests are isolated (plugin tests install otterwiki in CI but don't test the full stack together). Accept this gap for now.

When a staging environment exists, the right approach is a separate workflow in robot.wtf that deploys to staging and runs the smoke test suite, triggered after all plugin CI passes.

Task Checklist

  • Add .github/workflows/ci.yml to robot.wtf
  • Add .github/workflows/ci.yml to otterwiki (fork)
  • Add .github/workflows/ci.yml to otterwiki-api
  • Add .github/workflows/ci.yml to otterwiki-mcp
  • Add .github/workflows/ci.yml to otterwiki-semantic-search
  • Add deploy.sh to robot.wtf
  • Verify CI passes on each repo (push a test branch or trigger manually)