Make /opt/stack a Forgejo git checkout instead of rsync-only
Prefer stack_deploy_method=git: init/fetch on the server, register an SSH deploy key with Forgejo, and leave local secrets untracked. Document git pull for day-to-day updates; keep rsync as an opt-in fallback.
This commit is contained in:
parent
6c9e085cbd
commit
90f3cf62c5
4 changed files with 159 additions and 10 deletions
|
|
@ -6,3 +6,4 @@
|
||||||
- After any meaningful change set: **commit and push** to `main` with a clear, complete-sentence message (what changed and why).
|
- After any meaningful change set: **commit and push** to `main` with a clear, complete-sentence message (what changed and why).
|
||||||
- Never commit secrets: `.env`, `wireguard/wg0.conf`, `config/`, GGUF models, `ansible/.secrets/`.
|
- Never commit secrets: `.env`, `wireguard/wg0.conf`, `config/`, GGUF models, `ansible/.secrets/`.
|
||||||
- Prefer SSH push (`GIT_SSH_COMMAND` with `~/.ssh/id_ed25519` if needed).
|
- Prefer SSH push (`GIT_SSH_COMMAND` with `~/.ssh/id_ed25519` if needed).
|
||||||
|
- Server checkout: `/opt/stack` is the same repo (`git pull` on ser5). Ansible uses `stack_deploy_method: git`.
|
||||||
|
|
|
||||||
|
|
@ -195,9 +195,17 @@ forgejo_root_url: "http://{{ forgejo_domain }}:{{ forgejo_http_port }}/"
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Stack deployment
|
# Stack deployment
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Sync this repo (parent of ansible/) to stack_root on the server.
|
# Deploy stack *code* to the server.
|
||||||
# Set false if you manage /opt/stack yourself (git clone, etc.).
|
# git — /opt/stack is a clone of Forgejo (preferred; use `git pull` on ser5)
|
||||||
deploy_stack_files: true
|
# rsync — push from the control machine (laptop) tree
|
||||||
|
stack_deploy_method: git
|
||||||
|
deploy_stack_files: true # master switch for code deploy tasks
|
||||||
|
|
||||||
|
# Forgejo remote used on the server (same host → prefer loopback SSH port)
|
||||||
|
stack_git_repo: "ssh://git@127.0.0.1:2222/tim/stack.git"
|
||||||
|
stack_git_branch: main
|
||||||
|
# Also accept the LAN URL if someone clones from another machine
|
||||||
|
stack_git_repo_lan: "ssh://git@192.168.8.123:2222/tim/stack.git"
|
||||||
|
|
||||||
# Paths on the control machine (the machine running ansible-playbook)
|
# Paths on the control machine (the machine running ansible-playbook)
|
||||||
# Default: the arr-stack repo that contains this ansible/ directory
|
# Default: the arr-stack repo that contains this ansible/ directory
|
||||||
|
|
|
||||||
|
|
@ -47,14 +47,142 @@
|
||||||
loop: "{{ media_subdirs }}"
|
loop: "{{ media_subdirs }}"
|
||||||
tags: [stack, storage]
|
tags: [stack, storage]
|
||||||
|
|
||||||
- name: Deploy stack files from control machine
|
# ---------------------------------------------------------------------------
|
||||||
|
# Code deploy: git (preferred) or rsync
|
||||||
|
# Local-only paths stay untracked: config/, .env, models/*.gguf, wireguard/wg0.conf
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
- name: Ensure git is installed on stack host
|
||||||
|
ansible.builtin.apt:
|
||||||
|
name: git
|
||||||
|
state: present
|
||||||
|
when: deploy_stack_files | bool and stack_deploy_method == "git"
|
||||||
|
tags: [stack, deploy, git]
|
||||||
|
|
||||||
|
- name: Ensure stack owner has an SSH key for Forgejo git
|
||||||
|
become: true
|
||||||
|
become_user: "{{ stack_owner }}"
|
||||||
|
ansible.builtin.user:
|
||||||
|
name: "{{ stack_owner }}"
|
||||||
|
generate_ssh_key: true
|
||||||
|
ssh_key_type: ed25519
|
||||||
|
ssh_key_file: "/home/{{ stack_owner }}/.ssh/id_ed25519"
|
||||||
|
ssh_key_comment: "{{ stack_owner }}@{{ inventory_hostname }}-forgejo"
|
||||||
|
when: deploy_stack_files | bool and stack_deploy_method == "git"
|
||||||
|
tags: [stack, deploy, git]
|
||||||
|
|
||||||
|
- name: Read stack owner public key
|
||||||
|
ansible.builtin.slurp:
|
||||||
|
src: "/home/{{ stack_owner }}/.ssh/id_ed25519.pub"
|
||||||
|
register: stack_owner_pubkey
|
||||||
|
when: deploy_stack_files | bool and stack_deploy_method == "git"
|
||||||
|
tags: [stack, deploy, git]
|
||||||
|
|
||||||
|
- name: Ensure Forgejo knows the stack host deploy key (idempotent via API best-effort)
|
||||||
|
ansible.builtin.shell: |
|
||||||
|
set -e
|
||||||
|
PUB='{{ stack_owner_pubkey.content | b64decode | trim }}'
|
||||||
|
# Create a short-lived admin token, add key if fingerprint not present, drop token
|
||||||
|
TOKEN=$(docker exec -u 1000 forgejo gitea admin user generate-access-token \
|
||||||
|
--username "{{ stack_owner }}" \
|
||||||
|
--token-name "ansible-deploy-$(date +%s)" \
|
||||||
|
--scopes all 2>/dev/null | awk '/Access token was successfully created:/{print $NF}')
|
||||||
|
if [ -z "$TOKEN" ]; then
|
||||||
|
echo "Could not create Forgejo token (is forgejo up?). Continuing — key may already exist."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
# List keys; add if title missing
|
||||||
|
if curl -fsS -H "Authorization: token $TOKEN" http://127.0.0.1:3002/api/v1/user/keys \
|
||||||
|
| grep -q "ansible-ser5\|{{ inventory_hostname }}-forgejo"; then
|
||||||
|
echo "SSH key already registered"
|
||||||
|
else
|
||||||
|
curl -fsS -X POST http://127.0.0.1:3002/api/v1/user/keys \
|
||||||
|
-H "Authorization: token $TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "{\"title\":\"ansible-{{ inventory_hostname }}\",\"key\":\"$PUB\"}" || true
|
||||||
|
fi
|
||||||
|
args:
|
||||||
|
executable: /bin/bash
|
||||||
|
when:
|
||||||
|
- deploy_stack_files | bool
|
||||||
|
- stack_deploy_method == "git"
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
tags: [stack, deploy, git]
|
||||||
|
|
||||||
|
- name: Initialize git repo in stack root if missing
|
||||||
|
become: true
|
||||||
|
become_user: "{{ stack_owner }}"
|
||||||
|
ansible.builtin.command: git init -b {{ stack_git_branch }}
|
||||||
|
args:
|
||||||
|
chdir: "{{ stack_root }}"
|
||||||
|
creates: "{{ stack_root }}/.git"
|
||||||
|
when: deploy_stack_files | bool and stack_deploy_method == "git"
|
||||||
|
tags: [stack, deploy, git]
|
||||||
|
|
||||||
|
- name: Configure origin remote
|
||||||
|
become: true
|
||||||
|
become_user: "{{ stack_owner }}"
|
||||||
|
ansible.builtin.shell: |
|
||||||
|
set -e
|
||||||
|
cd "{{ stack_root }}"
|
||||||
|
if git remote get-url origin >/dev/null 2>&1; then
|
||||||
|
git remote set-url origin "{{ stack_git_repo }}"
|
||||||
|
else
|
||||||
|
git remote add origin "{{ stack_git_repo }}"
|
||||||
|
fi
|
||||||
|
args:
|
||||||
|
executable: /bin/bash
|
||||||
|
when: deploy_stack_files | bool and stack_deploy_method == "git"
|
||||||
|
changed_when: true
|
||||||
|
tags: [stack, deploy, git]
|
||||||
|
|
||||||
|
- name: Mark stack root as safe.directory for git
|
||||||
|
become: true
|
||||||
|
become_user: "{{ stack_owner }}"
|
||||||
|
ansible.builtin.command: git config --global --add safe.directory {{ stack_root }}
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
when: deploy_stack_files | bool and stack_deploy_method == "git"
|
||||||
|
tags: [stack, deploy, git]
|
||||||
|
|
||||||
|
- name: Fetch and hard-reset tracked files to origin (keeps untracked secrets)
|
||||||
|
become: true
|
||||||
|
become_user: "{{ stack_owner }}"
|
||||||
|
ansible.builtin.shell: |
|
||||||
|
set -euo pipefail
|
||||||
|
cd "{{ stack_root }}"
|
||||||
|
export GIT_SSH_COMMAND="ssh -i /home/{{ stack_owner }}/.ssh/id_ed25519 -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new"
|
||||||
|
git fetch origin "{{ stack_git_branch }}"
|
||||||
|
# Preserve local-only paths; only reset tracked files
|
||||||
|
git checkout -B "{{ stack_git_branch }}" "origin/{{ stack_git_branch }}"
|
||||||
|
args:
|
||||||
|
executable: /bin/bash
|
||||||
|
when: deploy_stack_files | bool and stack_deploy_method == "git"
|
||||||
|
register: stack_git_pull
|
||||||
|
tags: [stack, deploy, git]
|
||||||
|
|
||||||
|
- name: Show git HEAD after pull
|
||||||
|
become: true
|
||||||
|
become_user: "{{ stack_owner }}"
|
||||||
|
ansible.builtin.command: git -C {{ stack_root }} log -1 --oneline
|
||||||
|
register: stack_git_head
|
||||||
|
changed_when: false
|
||||||
|
when: deploy_stack_files | bool and stack_deploy_method == "git"
|
||||||
|
tags: [stack, deploy, git]
|
||||||
|
|
||||||
|
- name: Report stack git revision
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: "stack @ {{ stack_git_head.stdout | default('n/a') }}"
|
||||||
|
when: deploy_stack_files | bool and stack_deploy_method == "git"
|
||||||
|
tags: [stack, deploy, git]
|
||||||
|
|
||||||
|
- name: Deploy stack files from control machine (rsync fallback)
|
||||||
ansible.posix.synchronize:
|
ansible.posix.synchronize:
|
||||||
src: "{{ stack_src }}/"
|
src: "{{ stack_src }}/"
|
||||||
dest: "{{ stack_root }}/"
|
dest: "{{ stack_root }}/"
|
||||||
delete: false
|
delete: false
|
||||||
recursive: true
|
recursive: true
|
||||||
# Non-root rsync cannot chown/chgrp files Docker created as root
|
|
||||||
# (e.g. searxng). Content sync only; ownership fixed in next task.
|
|
||||||
archive: true
|
archive: true
|
||||||
owner: false
|
owner: false
|
||||||
group: false
|
group: false
|
||||||
|
|
@ -72,11 +200,11 @@
|
||||||
- "--no-owner"
|
- "--no-owner"
|
||||||
- "--no-group"
|
- "--no-group"
|
||||||
- "--chmod=Du=rwx,Dgo=rx,Fu=rw,Fgo=r"
|
- "--chmod=Du=rwx,Dgo=rx,Fu=rw,Fgo=r"
|
||||||
when: deploy_stack_files | bool
|
when: deploy_stack_files | bool and stack_deploy_method == "rsync"
|
||||||
become: false
|
become: false
|
||||||
tags: [stack, deploy]
|
tags: [stack, deploy, rsync]
|
||||||
|
|
||||||
- name: Fix ownership after rsync
|
- name: Fix ownership of stack tree
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
path: "{{ stack_root }}"
|
path: "{{ stack_root }}"
|
||||||
state: directory
|
state: directory
|
||||||
|
|
@ -84,7 +212,6 @@
|
||||||
group: "{{ stack_group }}"
|
group: "{{ stack_group }}"
|
||||||
recurse: true
|
recurse: true
|
||||||
when: deploy_stack_files | bool
|
when: deploy_stack_files | bool
|
||||||
# become: true from play — can reset root-owned files from containers
|
|
||||||
tags: [stack, deploy]
|
tags: [stack, deploy]
|
||||||
|
|
||||||
- name: Deploy production .env for this host
|
- name: Deploy production .env for this host
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,19 @@ ansible-playbook site.yml -K # -K only if sudo still asks once; playboo
|
||||||
|
|
||||||
What it does: OS updates, tools (incl. Microsoft `edit`), Docker, AMD/Vulkan, UFW (LAN only), `/opt/stack` + `/storage`, `.env`, `arr-stack.service`, and **tim** as local admin (passwordless sudo, docker/video/render/…, owns stack + storage, permissive GPU nodes).
|
What it does: OS updates, tools (incl. Microsoft `edit`), Docker, AMD/Vulkan, UFW (LAN only), `/opt/stack` + `/storage`, `.env`, `arr-stack.service`, and **tim** as local admin (passwordless sudo, docker/video/render/…, owns stack + storage, permissive GPU nodes).
|
||||||
|
|
||||||
|
**Code on the server:** `/opt/stack` is a **git clone** of Forgejo `tim/stack` (`stack_deploy_method: git`).
|
||||||
|
Day-to-day updates on ser5:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh tim@192.168.8.123
|
||||||
|
cd /opt/stack
|
||||||
|
git pull
|
||||||
|
docker compose up -d # if compose/images changed
|
||||||
|
```
|
||||||
|
|
||||||
|
Ansible can also refresh code: `ansible-playbook site.yml -K --tags stack,deploy` (runs `git fetch` + checkout).
|
||||||
|
Local-only (not in git): `.env`, `config/`, `models/**/*.gguf`, `wireguard/wg0.conf`.
|
||||||
|
|
||||||
**Before first full stack start** (on ser5):
|
**Before first full stack start** (on ser5):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue