init(opencode-ansible): scaffold dedicated Ansible repo for opencode propagation

This commit is contained in:
Twentyninehairs_bot 2026-05-04 22:26:08 -07:00
commit 991be3dd90
Signed by: Twentyninehairs_bot
GPG Key ID: CC558AA42F05E387
18 changed files with 226 additions and 0 deletions

51
README.md Normal file
View File

@ -0,0 +1,51 @@
# OpenCode Ansible
Dedicated Ansible orchestration repo for propagating opencode (oh-my-openagent) across the fleet.
## Structure
```
opencode-ansible/
├── ansible.cfg # Ansible configuration
├── inventory/
│ ├── opencode.yml # Main inventory
│ └── group_vars/
│ └── opencode.yml # Group-wide defaults
├── host_vars/ # Per-host overrides
├── playbooks/ # Deployment playbooks
├── roles/ # Ansible roles (if needed)
├── templates/ # Jinja2 templates
├── scripts/ # CLI wrapper scripts
├── callback_plugins/ # Audit logging
├── audit/ # JSONL audit trail
└── docs/ # Documentation
```
## Quick Start
```bash
# Check readiness of a host
ansible-playbook -i inventory/opencode.yml playbooks/readiness.yml --limit crew2
# Full deployment pipeline
bash scripts/full-deploy.sh --limit crew2
# Verify deployment
ansible-playbook -i inventory/opencode.yml playbooks/verify.yml --limit crew2
```
## Playbooks
| Playbook | Purpose |
|----------|---------|
| `readiness.yml` | Pre-install host assessment |
| `bootstrap.yml` | Install prerequisites and service accounts |
| `install.yml` | Install opencode binary/runtime |
| `config.yml` | Deploy configuration and sync repos |
| `service.yml` | Deploy and enable systemd services |
| `verify.yml` | Post-deployment health checks |
| `rollback.yml` | Revert opencode deployment |
## Target Hosts
crew2, crewsupport, llm01, erpnext, poweredget150, a10, noc, kenny-GE76-Raider-11UE

33
ansible.cfg Normal file
View File

@ -0,0 +1,33 @@
[defaults]
# Inventory
inventory = inventory/opencode.yml
# Connection
host_key_checking = False
timeout = 30
# Output
stdout_callback = yaml
bin_ansible_callbacks = True
# Callback plugins (audit logging)
callback_plugins = callback_plugins
callbacks_enabled = audit_log
# Retry files — disable, use audit log instead
retry_files_enabled = False
# Forks — fleet is small, keep low
forks = 5
# Logging (in addition to audit callback)
log_path = audit/ansible.log
# Vault — reference existing netman vault password file
vault_password_file = /home/kenny/nc/ansible/.ansible/vault_pass.txt
[privilege_escalation]
become = False
[ssh_connection]
pipelining = True

0
audit/.gitkeep Normal file
View File

View File

@ -0,0 +1,107 @@
# audit_log.py — Ansible callback plugin for fleet-ops audit trail
# Logs each playbook run to /audit/<date>-<playbook>.jsonl
# Format: one JSON object per line (JSONL), one file per playbook per day
from __future__ import annotations
from ansible.plugins.callback import CallbackBase
import json
import os
from datetime import datetime, timezone
DOCUMENTATION = """
name: audit_log
type: notification
short_description: Write structured audit log to /audit/
description:
- Appends a JSON record for each playbook run to /audit/<date>-<playbook>.jsonl
- Fields: timestamp, completed, playbook, check_mode, outcome, summary, tasks
options: {}
"""
class CallbackModule(CallbackBase):
CALLBACK_VERSION = 2.0
CALLBACK_TYPE = "notification"
CALLBACK_NAME = "audit_log"
CALLBACK_NEEDS_WHITELIST = True
AUDIT_DIR = "/audit"
def __init__(self):
super().__init__()
self._playbook_name = "unknown"
self._start_time = None
self._check_mode = False
self._task_results: list[dict] = []
def v2_playbook_on_start(self, playbook):
self._playbook_name = os.path.basename(playbook._file_name)
self._start_time = datetime.now(timezone.utc).isoformat()
self._task_results = []
def v2_playbook_on_play_start(self, play):
self._check_mode = play.check_mode
def _record(self, result, status: str):
entry = {
"host": result._host.name,
"task": result._task.get_name(),
"status": status,
}
if status == "failed":
entry["msg"] = str(result._result.get("msg", ""))
self._task_results.append(entry)
def v2_runner_on_ok(self, result):
self._record(result, "ok")
def v2_runner_on_failed(self, result, ignore_errors=False):
self._record(result, "failed" if not ignore_errors else "ignored")
def v2_runner_on_unreachable(self, result):
self._record(result, "unreachable")
def v2_runner_on_skipped(self, result):
self._record(result, "skipped")
def v2_playbook_on_stats(self, stats):
end_time = datetime.now(timezone.utc).isoformat()
summary = {}
for host in stats.processed:
summary[host] = {
"ok": stats.ok.get(host, 0),
"changed": stats.changed.get(host, 0),
"failed": stats.failures.get(host, 0),
"unreachable": stats.dark.get(host, 0),
"skipped": stats.skipped.get(host, 0),
}
any_failed = any(
v["failed"] > 0 or v["unreachable"] > 0 for v in summary.values()
)
outcome = "failed" if any_failed else "success"
if self._check_mode:
outcome = f"check-{outcome}"
log_entry = {
"timestamp": self._start_time,
"completed": end_time,
"playbook": self._playbook_name,
"check_mode": self._check_mode,
"outcome": outcome,
"summary": summary,
"tasks": self._task_results,
}
os.makedirs(self.AUDIT_DIR, exist_ok=True)
date_str = (self._start_time or end_time)[:10]
playbook_stem = self._playbook_name.replace(".yml", "").replace(".yaml", "")
log_file = os.path.join(self.AUDIT_DIR, f"{date_str}-{playbook_stem}.jsonl")
try:
with open(log_file, "a") as f:
f.write(json.dumps(log_entry) + "\n")
except OSError as e:
self._display.warning(f"audit_log: could not write to {log_file}: {e}")

0
docs/.gitkeep Normal file
View File

0
host_vars/.gitkeep Normal file
View File

View File

@ -0,0 +1,3 @@
---
# OpenCode Propagation: Group-wide Defaults
# TODO: Implement in Task 2

3
inventory/opencode.yml Normal file
View File

@ -0,0 +1,3 @@
---
# OpenCode Propagation: Main Inventory
# TODO: Implement in Task 2

4
playbooks/bootstrap.yml Normal file
View File

@ -0,0 +1,4 @@
---
# OpenCode Propagation: Bootstrap Prerequisites
# Installs prerequisites (Python, Node.js, service accounts, etc.)
# TODO: Implement in Task 8

4
playbooks/config.yml Normal file
View File

@ -0,0 +1,4 @@
---
# OpenCode Propagation: Config and Repo Sync
# Deploys configuration and syncs Gitea repos on target hosts
# TODO: Implement in Task 10

4
playbooks/install.yml Normal file
View File

@ -0,0 +1,4 @@
---
# OpenCode Propagation: Install OpenCode
# Installs opencode binary/runtime on target hosts
# TODO: Implement in Task 9

4
playbooks/readiness.yml Normal file
View File

@ -0,0 +1,4 @@
---
# OpenCode Propagation: Readiness Assessment
# Verifies target hosts are ready for opencode installation
# TODO: Implement in Task 7

5
playbooks/rollback.yml Normal file
View File

@ -0,0 +1,5 @@
---
# OpenCode Propagation: Rollback
# Reverts opencode deployment to clean state
# Requires --extra-vars "confirm_rollback=yes" to run
# TODO: Implement in Task 15

4
playbooks/service.yml Normal file
View File

@ -0,0 +1,4 @@
---
# OpenCode Propagation: Service Deployment
# Deploys and enables systemd user services for opencode
# TODO: Implement in Task 11

4
playbooks/verify.yml Normal file
View File

@ -0,0 +1,4 @@
---
# OpenCode Propagation: Verification and Health Checks
# Post-deployment verification and health checks
# TODO: Implement in Task 12

0
roles/.gitkeep Normal file
View File

0
scripts/.gitkeep Normal file
View File

0
templates/.gitkeep Normal file
View File