Blame
|
1 | --- |
||||||
| 2 | category: spec |
|||||||
| 3 | tags: [ci, cd, github-actions, plan] |
|||||||
| 4 | last_updated: 2026-03-18 |
|||||||
| 5 | confidence: high |
|||||||
| 6 | --- |
|||||||
| 7 | ||||||||
| 8 | # CI/CD Pipeline Plan |
|||||||
| 9 | ||||||||
| 10 | ## Current State |
|||||||
| 11 | ||||||||
| 12 | - Five repos: `robot.wtf`, `otterwiki` (fork), `otterwiki-api`, `otterwiki-mcp`, `otterwiki-semantic-search` |
|||||||
| 13 | - All have pytest suites; no GitHub Actions workflows exist in any repo |
|||||||
| 14 | - Deploy: `ansible-playbook ansible/deploy.yml -i ansible/inventory.yml` from `robot.wtf/` on a laptop |
|||||||
| 15 | - Ansible `deploy` role installs each plugin directly from GitHub main branch via `pip install git+https://...` |
|||||||
| 16 | - Post-deploy smoke test is baked into the playbook |
|||||||
| 17 | ||||||||
| 18 | ## Test Infrastructure Summary |
|||||||
| 19 | ||||||||
| 20 | | Repo | Test runner | Dependencies | |
|||||||
| 21 | |------|-------------|--------------| |
|||||||
| 22 | | `robot.wtf` | pytest, no pyproject | `requirements.txt` (Flask, PyJWT, etc.) | |
|||||||
| 23 | | `otterwiki` | pytest via pyproject / tox | Self-contained | |
|||||||
| 24 | | `otterwiki-api` | pytest via pyproject | Requires `otterwiki` installed | |
|||||||
| 25 | | `otterwiki-semantic-search` | pytest via pyproject | Requires `otterwiki` installed | |
|||||||
| 26 | | `otterwiki-mcp` | pytest + pytest-asyncio | Standalone (no `otterwiki` dep) | |
|||||||
| 27 | ||||||||
| 28 | ## Phase 1: Per-Repo CI on PR (do this session) |
|||||||
| 29 | ||||||||
| 30 | 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. |
|||||||
| 31 | ||||||||
| 32 | ### Template (robot.wtf) |
|||||||
| 33 | ||||||||
| 34 | ```yaml |
|||||||
| 35 | name: CI |
|||||||
| 36 | on: |
|||||||
| 37 | pull_request: |
|||||||
| 38 | push: |
|||||||
| 39 | branches: [main] |
|||||||
| 40 | ||||||||
| 41 | jobs: |
|||||||
| 42 | test: |
|||||||
| 43 | runs-on: ubuntu-latest |
|||||||
| 44 | steps: |
|||||||
| 45 | - uses: actions/checkout@v4 |
|||||||
| 46 | - uses: actions/setup-python@v5 |
|||||||
| 47 | with: |
|||||||
| 48 | python-version: "3.12" |
|||||||
| 49 | - run: pip install -r requirements.txt |
|||||||
| 50 | - run: pip install pytest |
|||||||
| 51 | - run: pytest tests/ |
|||||||
| 52 | ``` |
|||||||
| 53 | ||||||||
| 54 | ### Plugin repos (otterwiki-api, otterwiki-semantic-search) |
|||||||
| 55 | ||||||||
| 56 | These depend on `otterwiki`. Install the fork's `wikibot-io` branch before installing the plugin: |
|||||||
| 57 | ||||||||
| 58 | ```yaml |
|||||||
| 59 | - run: pip install "git+https://github.com/schuyler/otterwiki.git@wikibot-io" |
|||||||
| 60 | - run: pip install -e ".[dev]" |
|||||||
| 61 | - run: pytest |
|||||||
| 62 | ``` |
|||||||
| 63 | ||||||||
| 64 | ### otterwiki-mcp |
|||||||
| 65 | ||||||||
| 66 | Standalone; no otterwiki dep. Standard `pip install -e ".[dev]" && pytest`. |
|||||||
| 67 | ||||||||
| 68 | ### otterwiki (fork) |
|||||||
| 69 | ||||||||
| 70 | Uses tox config in pyproject.toml. Can run `pip install -e ".[dev]" && pytest` directly to avoid tox overhead in CI. |
|||||||
| 71 | ||||||||
| 72 | ## Phase 2: Deploy Script (do this session) |
|||||||
| 73 | ||||||||
| 74 | The Ansible command is easy to forget. Add `deploy.sh` to `robot.wtf/`: |
|||||||
| 75 | ||||||||
| 76 | ```bash |
|||||||
| 77 | #!/usr/bin/env bash |
|||||||
| 78 | set -euo pipefail |
|||||||
| 79 | cd "$(dirname "$0")" |
|||||||
| 80 | ansible-playbook ansible/deploy.yml -i ansible/inventory.yml "$@" |
|||||||
| 81 | ``` |
|||||||
| 82 | ||||||||
| 83 | This is the entire CD story for now. Manual deploy stays manual — that's the right call given: |
|||||||
| 84 | - Deploy requires pulling from GitHub, so all plugin changes must be merged to main first |
|||||||
| 85 | - No staging environment exists |
|||||||
| 86 | - Smoke test is already part of the playbook |
|||||||
| 87 | ||||||||
| 88 | ## Phase 3: Auto-Deploy (deferred) |
|||||||
| 89 | ||||||||
| 90 | Options when ready: |
|||||||
| 91 | 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. |
|||||||
| 92 | 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. |
|||||||
| 93 | 3. **Repository dispatch** — plugin repos trigger a workflow in `robot.wtf` via the GitHub API on merge to main. Preserves separation of concerns. |
|||||||
| 94 | ||||||||
| 95 | 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`. |
|||||||
| 96 | ||||||||
| 97 | ## Integration Testing |
|||||||
| 98 | ||||||||
| 99 | 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. |
|||||||
| 100 | ||||||||
| 101 | 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. |
|||||||
| 102 | ||||||||
| 103 | ## Task Checklist |
|||||||
| 104 | ||||||||
| 105 | - [ ] Add `.github/workflows/ci.yml` to `robot.wtf` |
|||||||
| 106 | - [ ] Add `.github/workflows/ci.yml` to `otterwiki` (fork) |
|||||||
| 107 | - [ ] Add `.github/workflows/ci.yml` to `otterwiki-api` |
|||||||
| 108 | - [ ] Add `.github/workflows/ci.yml` to `otterwiki-mcp` |
|||||||
| 109 | - [ ] Add `.github/workflows/ci.yml` to `otterwiki-semantic-search` |
|||||||
| 110 | - [ ] Add `deploy.sh` to `robot.wtf` |
|||||||
| 111 | - [ ] Verify CI passes on each repo (push a test branch or trigger manually) |
|||||||
