Two-layer structure: Sources (raw notes) + Wiki (compile output) Four domains: Dev (40), Venture (3), Homelab (23), Reference (0) Includes CLAUDE.md spec, index pages at all levels, compile log Co-Authored-By: Lovebug <lovebug@herbylab.dev>
9.4 KiB
| created | path | project | status | tags | type | updated | |||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2026-04-18 | Sources/Dev | pbsii-cicd-pipeline | active |
|
project-plan | 2026-04-18 |
PBSII CI/CD Pipeline (Revised)
Goal
Automate deployment of PBSII layers using GitHub Actions with a self-hosted
runner on ustest1. Each layer deploys independently based on which files
changed. Branch strategy controls target environment: main → production,
staging → staging. Start with the dashboard layer, then extend to
collector and parser.
Context
- PBSII is a private monorepo on GitHub with subdirectories:
collector/,parser/,dashboard/,n8n/ - Ansible project (
wp-i) handles all server config and Docker deployments, lives onustest1 - Each Docker container has its own folder and
docker-compose.ymlon the server (e.g.,/opt/docker/dashboard/) - Ansible docker role currently deploys all containers in a loop — migrating to per-container tags
- A standalone pbs-hub deploy role exists as a reference pattern
- Each PBSII layer deploys differently: dashboard is Docker, collector is systemd, parser is Python/venv
- n8n workflows are version-controlled JSON — no CI/CD needed
Architecture
GitHub Push (to dashboard/ on main)
→ GitHub signals self-hosted runner on ustest1
→ ustest1 already has wp-i, SSH keys, Ansible — everything it needs
→ Runs Ansible playbook with dashboard tag
→ Ansible SSHs to Linode production, deploys dashboard
→ Google Chat notification on success/failure
GitHub Push (to dashboard/ on staging)
→ Same flow, but Ansible targets staging server
Why Self-Hosted Runner
ustest1(Proxmox homelab) already haswp-i, SSH keys to Linode, and Ansible installed- No secrets to copy into GitHub cloud runners
- No cloning the
wp-irepo onto temporary VMs - No rebuilding Ansible environment on every run
- Solves the staging drift problem — staging deployments go through the same Ansible path as production
Phase 1: Self-Hosted Runner Setup
1a. Install GitHub Actions runner on ustest1
In the PBSII GitHub repo → Settings → Actions → Runners → New self-hosted runner:
- Follow GitHub's instructions to download and configure the runner
agent on
ustest1 - Install as a systemd service so it starts on boot
- Verify runner shows as "Idle" in GitHub repo settings
1b. Runner user permissions
- The runner agent runs as a user on
ustest1— that user needs SSH access to Linode (production + staging) - Ensure that user has access to
wp-ionustest1 - Ensure Ansible vault password is accessible (file or environment variable)
1c. Label the runner
Add labels to the runner for targeting:
self-hosted(default)ustest1(custom, for clarity)
Phase 2: Ansible Dashboard Role
2a. Create the dashboard Ansible role in wp-i
Use the existing pbs-hub deploy role as a reference pattern. The role should:
- Copy dashboard source files to server at
/opt/docker/dashboard/ - Copy
docker-compose.ymlandDockerfileto the server - Run
docker compose up -d --buildin the dashboard folder - Healthcheck to verify dashboard is responding
2b. Tag the role
- Ensure the role can be called with
--tags dashboard - Verify it only touches the dashboard container, nothing else
2c. Environment targeting
The playbook needs to accept a variable (e.g., target_env) that
determines whether it deploys to production or staging:
target_env: production→ production Linode IP, production compose overridestarget_env: staging→ staging Linode IP, staging compose overrides
This keeps the same role for both environments — only the target changes.
Phase 3: GitHub Actions Workflow (Dashboard)
3a. Production workflow
File: .github/workflows/deploy-dashboard.yml
name: Deploy Dashboard
on:
push:
branches: [main]
paths:
- 'dashboard/**'
jobs:
deploy:
runs-on: [self-hosted, ustest1]
steps:
- name: Checkout PBSII
uses: actions/checkout@v4
- name: Run Ansible playbook
run: |
cd /path/to/wp-i
ansible-playbook playbook.yml \
--tags dashboard \
-e target_env=production
- name: Notify Google Chat (success)
if: success()
run: |
curl -X POST "$GOOGLE_CHAT_WEBHOOK" \
-H 'Content-Type: application/json' \
-d '{"text": "✅ Dashboard deployed to production (commit: ${{
github.sha }})" }'
- name: Notify Google Chat (failure)
if: failure()
run: |
curl -X POST "$GOOGLE_CHAT_WEBHOOK" \
-H 'Content-Type: application/json' \
-d '{"text": "❌ Dashboard deploy FAILED (commit: ${{ github.sha
}})" }'
env:
GOOGLE_CHAT_WEBHOOK: ${{ secrets.GOOGLE_CHAT_WEBHOOK }}
3b. Staging workflow
File: .github/workflows/deploy-dashboard-staging.yml
Same as above but:
- Trigger:
branches: [staging] - Ansible flag:
-e target_env=staging - Notification says "staging" instead of "production"
Alternatively, this could be one workflow file with a branch conditional — decide during implementation.
3c. Test the workflow
- Push a small change to
dashboard/onstagingbranch first - Watch the Actions tab in GitHub
- Verify dashboard redeploys on staging server
- Verify Google Chat notification arrives
- Verify pushing to other directories does NOT trigger this workflow
- Once staging is solid, test the
mainbranch → production flow
Phase 4: Extend to Other Layers
Once the dashboard pipeline is proven, repeat the pattern:
4a. Collector pipeline
File: .github/workflows/deploy-collector.yml
Trigger: paths: ['collector/**']
Ansible role needs to:
- Cross-compile Go binary in CI step (or build on server)
- Copy binary to server
- Restart the systemd service (
systemctl restart pbs-collector) - Verify collector responds on its HTTP API
Note: Go cross-compiles easily — building on ustest1 for Linux/amd64
target and shipping the binary to Linode avoids needing Go installed on
production.
4b. Parser pipeline
File: .github/workflows/deploy-parser.yml
Trigger: paths: ['parser/**']
Ansible role needs to:
- Copy parser source to server
- Ensure Python venv exists with correct deps (uv sync)
- No restart needed — n8n triggers it on schedule
Phase 5: Google Chat Notifications
5a. Setup
- Add
GOOGLE_CHAT_WEBHOOKto GitHub Secrets (can use same webhook as existing n8n alerts, or create a dedicated one for deployments) - Decide notification format — commit hash, branch, layer name, success/failure
5b. Notification content
Success: ✅ [layer] deployed to [env] — commit abc1234
Failure: ❌ [layer] deploy FAILED on [env] — commit abc1234 — check Actions tab
Repo Structure After Implementation
pbsii/
├── .github/
│ └── workflows/
│ ├── deploy-dashboard.yml
│ ├── deploy-dashboard-staging.yml
│ ├── deploy-collector.yml
│ ├── deploy-collector-staging.yml
│ ├── deploy-parser.yml
│ └── deploy-parser-staging.yml
├── collector/
│ ├── README.md
│ ├── go.mod
│ ├── main.go
│ ├── Makefile
│ └── pbs-collector.service
├── parser/
│ ├── README.md
│ ├── pyproject.toml
│ └── parser.py
├── dashboard/
│ ├── README.md
│ ├── pyproject.toml
│ ├── Dockerfile
│ ├── docker-compose.yml
│ └── app.py
├── n8n/
│ └── workflows/
│ └── *.json
├── sql/
│ └── schema.sql
└── README.md
Ansible roles live in wp-i, not in this repo:
wp-i/
└── roles/
├── dashboard/
├── collector/
└── parser/
GitHub Secrets Required
GOOGLE_CHAT_WEBHOOK— webhook URL for deployment notificationsANSIBLE_VAULT_PASSWORD— if vault is needed for deploy (may not be needed if runner user has access to vault password file onustest1)
Note: SSH keys and Ansible inventory are already on ustest1 — no need to
store them in GitHub.
Open Questions
- What is the path to
wp-ionustest1? Needed for the workflowcdstep. - Which user does the runner agent run as on
ustest1? That user needs SSH + Ansible access. - Does
ustest1currently have SSH keys to both production and staging Linode? If not, set up during Phase 1. - One workflow file per layer per environment (6 files), or one per layer with branch conditionals (3 files)? Start simple, refactor if it gets noisy.
- Google Chat: dedicated deployment webhook or reuse existing?
Key Principles
- Each layer deploys independently — a change to the parser never touches the dashboard
- Ansible stays the deployment executor — GitHub Actions is just the trigger
- Self-hosted runner on
ustest1— no secrets in the cloud, no rebuilding environments - Branch strategy solves staging drift —
stagingbranch → staging server,main→ production - Build on server for v1 — move to CI builds later if needed
- Google Chat notifications on every deploy — success and failure
- Start with dashboard, prove the pattern, then extend
- Test on staging first, always
...sent from Jenny & Travis