Public Access
docs: add implementation plan for ARM deploy to OrangePi via Ansible
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
ce4bb97215
commit
76c1fd6347
1 file changed
+232
@@ -0,0 +1,232 @@
|
||||
# ARM Deploy to OrangePi via Ansible — Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Add a Gitea Actions workflow that deploys the ARM64 Docker image to an OrangePi via Ansible after each successful release.
|
||||
|
||||
**Architecture:** A separate `deploy.yml` workflow triggers via `workflow_run` when the `Release Pipeline` succeeds. The runner acts as Ansible controller, downloads the ARM artifacts from the Gitea release, writes a dynamic inventory from secrets, and runs a playbook that copies files, loads the Docker image, and restarts the container on the OrangePi.
|
||||
|
||||
**Tech Stack:** Gitea Actions, Ansible (ansible-playbook), Docker, Docker Compose
|
||||
|
||||
---
|
||||
|
||||
## File Map
|
||||
|
||||
| File | Action | Responsibility |
|
||||
|---|---|---|
|
||||
| `.gitea/workflows/deploy.yml` | Create | Gitea workflow triggered by release pipeline success |
|
||||
| `ansible/deploy.yml` | Create | Ansible playbook — copy artifacts, load image, restart container |
|
||||
|
||||
> Note: Inventory is written dynamically in the workflow from secrets (not committed) so no sensitive host details land in the repo.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Ansible Playbook
|
||||
|
||||
**Files:**
|
||||
- Create: `ansible/deploy.yml`
|
||||
|
||||
- [ ] **Step 1: Create the playbook**
|
||||
|
||||
Create `ansible/deploy.yml` with this exact content:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Deploy Brainwave ARM to OrangePi
|
||||
hosts: orangepi
|
||||
gather_facts: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure deploy directory exists
|
||||
file:
|
||||
path: /home/user/brainwave
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: Copy ARM tarball
|
||||
copy:
|
||||
src: "{{ artifact_path }}/brainwave-arm64.tar.gz"
|
||||
dest: /home/user/brainwave/brainwave-arm64.tar.gz
|
||||
|
||||
- name: Copy docker-compose file
|
||||
copy:
|
||||
src: "{{ artifact_path }}/docker-compose.arm64.yml"
|
||||
dest: /home/user/brainwave/docker-compose.yml
|
||||
|
||||
- name: Load Docker image
|
||||
command: docker load -i /home/user/brainwave/brainwave-arm64.tar.gz
|
||||
|
||||
- name: Start container
|
||||
command: docker compose up -d
|
||||
args:
|
||||
chdir: /home/user/brainwave
|
||||
```
|
||||
|
||||
> `artifact_path` is passed in by the workflow via `--extra-vars`. The SSH user (`ANSIBLE_USER`) must be in the `docker` group on the OrangePi — if not, add `become: true` to the last two tasks and ensure `ANSIBLE_USER` has sudo access.
|
||||
|
||||
- [ ] **Step 2: Syntax-check the playbook locally**
|
||||
|
||||
Run (requires `ansible` installed locally, skip if not available):
|
||||
|
||||
```bash
|
||||
ansible-playbook ansible/deploy.yml --syntax-check -i "localhost," -e "artifact_path=/tmp"
|
||||
```
|
||||
|
||||
Expected output: `playbook: ansible/deploy.yml`
|
||||
|
||||
If Ansible is not installed locally, skip this step — the workflow will catch syntax errors on first run.
|
||||
|
||||
- [ ] **Step 3: Commit the playbook**
|
||||
|
||||
```bash
|
||||
git add ansible/deploy.yml
|
||||
git commit -m "feat: add Ansible playbook for ARM deploy to OrangePi"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: Deploy Workflow
|
||||
|
||||
**Files:**
|
||||
- Create: `.gitea/workflows/deploy.yml`
|
||||
|
||||
- [ ] **Step 1: Create the workflow file**
|
||||
|
||||
Create `.gitea/workflows/deploy.yml` with this exact content:
|
||||
|
||||
```yaml
|
||||
name: Deploy ARM to OrangePi
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ["Release Pipeline"]
|
||||
types:
|
||||
- completed
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.workflow_run.conclusion == 'success'
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Ansible
|
||||
run: |
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -y ansible
|
||||
|
||||
- name: Write SSH Key
|
||||
run: |
|
||||
echo "${{ secrets.ANSIBLE_SSH_KEY }}" > /tmp/ansible_key
|
||||
chmod 600 /tmp/ansible_key
|
||||
|
||||
- name: Write Inventory
|
||||
run: |
|
||||
printf '[orangepi]\norangepi ansible_host=%s ansible_user=%s ansible_ssh_private_key_file=/tmp/ansible_key ansible_ssh_common_args="-o StrictHostKeyChecking=no"\n' \
|
||||
"${{ secrets.ANSIBLE_HOST }}" \
|
||||
"${{ secrets.ANSIBLE_USER }}" > /tmp/inventory.ini
|
||||
|
||||
- name: Download ARM Release Artifacts
|
||||
run: |
|
||||
TAG="${{ github.event.workflow_run.head_branch }}"
|
||||
BASE_URL="${{ github.server_url }}/${{ github.repository }}/releases/download/${TAG}"
|
||||
wget -q "${BASE_URL}/brainwave-arm64.tar.gz"
|
||||
wget -q "${BASE_URL}/docker-compose.arm64.yml"
|
||||
|
||||
- name: Deploy via Ansible
|
||||
run: |
|
||||
ansible-playbook ansible/deploy.yml \
|
||||
-i /tmp/inventory.ini \
|
||||
-e "artifact_path=${{ github.workspace }}"
|
||||
|
||||
- name: Cleanup SSH Key
|
||||
if: always()
|
||||
run: rm -f /tmp/ansible_key
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Validate the YAML syntax**
|
||||
|
||||
```bash
|
||||
python3 -c "import yaml; yaml.safe_load(open('.gitea/workflows/deploy.yml'))" && echo "YAML valid"
|
||||
```
|
||||
|
||||
Expected output: `YAML valid`
|
||||
|
||||
- [ ] **Step 3: Commit the workflow**
|
||||
|
||||
```bash
|
||||
git add .gitea/workflows/deploy.yml
|
||||
git commit -m "feat: add deploy workflow to push ARM release to OrangePi via Ansible"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 3: Add Gitea Secrets
|
||||
|
||||
**Files:** None (Gitea UI configuration)
|
||||
|
||||
- [ ] **Step 1: Open your Gitea repo secrets**
|
||||
|
||||
Navigate to: `Settings → Secrets and Variables → Actions → New Secret`
|
||||
|
||||
- [ ] **Step 2: Add the three required secrets**
|
||||
|
||||
Add each of these:
|
||||
|
||||
| Name | Value |
|
||||
|---|---|
|
||||
| `ANSIBLE_HOST` | IP address or hostname of your OrangePi (e.g. `192.168.1.50`) |
|
||||
| `ANSIBLE_USER` | SSH username on the OrangePi (e.g. `orangepi` or `user`) |
|
||||
| `ANSIBLE_SSH_KEY` | Full content of the private SSH key (including `-----BEGIN...` and `-----END...` lines) |
|
||||
|
||||
> The SSH public key must already be in `~/.ssh/authorized_keys` on the OrangePi for `ANSIBLE_USER`.
|
||||
|
||||
- [ ] **Step 3: Verify the OrangePi SSH user is in the docker group**
|
||||
|
||||
SSH into the OrangePi and run:
|
||||
|
||||
```bash
|
||||
groups $USER
|
||||
```
|
||||
|
||||
If `docker` is not listed, add it and re-login:
|
||||
|
||||
```bash
|
||||
sudo usermod -aG docker $USER
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 4: End-to-End Verification
|
||||
|
||||
- [ ] **Step 1: Push a version tag to trigger the release pipeline**
|
||||
|
||||
```bash
|
||||
git tag v0.0.1-test
|
||||
git push origin v0.0.1-test
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Monitor the Release Pipeline run**
|
||||
|
||||
In Gitea: `Actions → Release Pipeline` — wait for it to complete successfully. Confirm the release assets (`brainwave-arm64.tar.gz`, `docker-compose.arm64.yml`) appear under `Releases → v0.0.1-test`.
|
||||
|
||||
- [ ] **Step 3: Monitor the Deploy ARM to OrangePi run**
|
||||
|
||||
In Gitea: `Actions → Deploy ARM to OrangePi` — confirm the workflow triggers after the release pipeline completes and all steps pass.
|
||||
|
||||
- [ ] **Step 4: Verify deployment on the OrangePi**
|
||||
|
||||
SSH into the OrangePi and run:
|
||||
|
||||
```bash
|
||||
docker ps
|
||||
```
|
||||
|
||||
Expected: a `brainwave` container in `Up` state with port `3001` mapped.
|
||||
|
||||
```bash
|
||||
curl http://localhost:3001
|
||||
```
|
||||
|
||||
Expected: a response from the Brainwave app (not a connection refused error).
|
||||
Reference in new issue
Block a user