diff --git a/playbook/deploy-webapp.yml b/playbook/deploy-webapp.yml new file mode 100644 index 0000000..e947c37 --- /dev/null +++ b/playbook/deploy-webapp.yml @@ -0,0 +1,65 @@ +--- +# Reference production deploy playbook for containerized web apps. +# Per-project playbooks derive from this. Additions-only to sam/semaphore.git /playbook. +# +# Required extra-vars: +# prefix: project prefix (e.g. church-site) +# version: image tag to deploy (e.g. v2026.08.27-abc1234) — enables rollback by passing a prior tag +# Optional: +# node: proxmox node for prod LXC (default pve4) +# port: container port to expose (default 80) +# +# Deploy: -e "prefix=church-site version=v2026.08.27-abc1234" +# Rollback: re-run with a prior version tag. + +- name: Deploy {{ prefix }}:{{ version }} to production + hosts: "{{ node | default('pve4') }}" + gather_facts: false + vars: + registry: "192.168.122.103:3000" + image: "192.168.122.103:3000/sam/{{ prefix }}:{{ version }}" + ctname: "{{ prefix }}-prod" + cport: "{{ port | default('80') }}" + tasks: + - name: Require version argument + ansible.builtin.assert: + that: + - version is defined + - version | length > 0 + fail_msg: "version is required (pass -e version=vX.Y.Z). Rollback = pass a prior tag." + + - name: Ensure prod LXC exists (create on first deploy) + ansible.builtin.shell: | + if ! pct status "$(pct list | awk -v n={{ ctname }} '$0 ~ n {print $1}')" >/dev/null 2>&1; then + echo "prod LXC {{ ctname }} must be provisioned by Semaphore before first deploy" + exit 3 + fi + changed_when: false + + - name: Log in to Gitea registry (creds injected from Infisical, never printed) + ansible.builtin.shell: > + echo "{{ lookup('env','REGISTRY_TOKEN') }}" | + docker login {{ registry }} -u "{{ lookup('env','REGISTRY_USER') }}" --password-stdin + no_log: true + + - name: Pull versioned image + ansible.builtin.command: "docker pull {{ image }}" + + - name: Stop/remove previous container (idempotent) + ansible.builtin.shell: "docker rm -f {{ prefix }} 2>/dev/null || true" + changed_when: false + + - name: Run the versioned image + ansible.builtin.command: > + docker run -d --name {{ prefix }} --restart unless-stopped + -p {{ cport }}:{{ cport }} {{ image }} + + - name: Verify container is running + ansible.builtin.command: "docker ps --filter name={{ prefix }} --filter status=running -q" + register: running + changed_when: false + failed_when: running.stdout | length == 0 + + - name: Record deploy result + ansible.builtin.debug: + msg: "Deployed {{ image }} to {{ ctname }} (port {{ cport }}). Rollback: re-run with a prior version tag."