Files
server-infra/roles/app-deploy/tasks/main.yml
T
westfarn 726ad971cb
Sync runner checkout / sync (push) Successful in 6s
Add stop.sh (compose down) and disable print_forge (#34)
## Summary
- Closes [#33](#33).
- Add `scripts/stop.sh` → `playbooks/stop-apps.yml` → `docker compose down` for one `--app` + `--env` (no `-v`; checkouts/secrets stay).
- `host_apps.enabled: false` skips deploy (including CI `--app`) so a stopped app cannot come back. Rows stay for ports and `stop.sh`.
- Disable `print_forge` beta + prod on all app hosts. After merge (or from this branch): `./scripts/stop.sh --app print_forge --env prod` and `--env beta`.

## Test plan
- [ ] `./scripts/stop.sh --help` shows required `--app` / `--env`
- [ ] `ansible-playbook playbooks/stop-apps.yml --syntax-check -e app=print_forge -e app_env=prod`
- [ ] `./scripts/stop.sh --app print_forge --env prod` compose-downs `print_forge_prod` on all webservers
- [ ] `./scripts/stop.sh --app print_forge --env beta` compose-downs `print_forge_beta` (including adama worker)
- [ ] `./scripts/deploy.sh --app print_forge --env prod --check` skips disabled rows (does not start containers)
- [ ] NPM: `print-forge-preview.aimloperations.com` will 502 until the proxy host is disabled (out of scope)

Reviewed-on: #34
2026-09-16 03:11:23 -07:00

92 lines
3.7 KiB
YAML

---
# Generic app deployment.
#
# CI passes: app=<name> app_env=<beta|prod> app_ref=<git sha>
# Manual run with none of those redeploys every app in this host's host_apps
# at branch master.
#
# host_apps (host_vars) lists what runs on THIS host; app_catalog (group_vars)
# describes how each app is built. host_apps.enabled: false (omit = true)
# keeps the row for ports/stop.sh but skips deploy so CI cannot resurrect it.
- name: Classify catalog by type
ansible.builtin.set_fact:
django_names: "{{ app_catalog | dict2items | selectattr('value.type', 'equalto', 'django') | map(attribute='key') | list }}"
node_names: "{{ app_catalog | dict2items | selectattr('value.type', 'equalto', 'node-static') | map(attribute='key') | list }}"
- name: Resolve matching host_apps for {{ inventory_hostname }}
ansible.builtin.set_fact:
matching_host_apps: >-
{{ (host_apps | default([]) | selectattr('name', 'equalto', app | default('')) | selectattr('env', 'equalto', app_env | default('')) | list)
if (app is defined and app_env is defined)
else (host_apps | default([])) }}
# Explicit --app/--env with no host_apps match used to silently no-op Django
# and still refresh web-static (looked like a "static-only" deploy). Fail loud.
- name: Fail when requested app+env is not on this host
ansible.builtin.fail:
msg: >-
No host_apps entry for app={{ app }} env={{ app_env }} on
{{ inventory_hostname }}. Check inventory/host_vars and app_catalog
(django apps need type: django + compose_file). Catalog keys:
{{ app_catalog.keys() | list }}.
when:
- app is defined
- app_env is defined
- matching_host_apps | length == 0
- name: "Drop disabled host_apps (enabled: false)"
ansible.builtin.set_fact:
deploy_targets: "{{ matching_host_apps | rejectattr('enabled', 'equalto', false) | list }}"
- name: Show skipped disabled apps
ansible.builtin.debug:
msg: "skip disabled {{ item.name }}/{{ item.env }} on {{ inventory_hostname }}"
loop: "{{ matching_host_apps | selectattr('enabled', 'equalto', false) | list }}"
loop_control:
label: "{{ item.name }}/{{ item.env }}"
- name: Show deploy targets
ansible.builtin.debug:
msg: "ref={{ app_ref | default('(per-app default branch)') }} targets={{ deploy_targets | map(attribute='name') | zip(deploy_targets | map(attribute='env')) | list }}"
- name: Fail when requested app missing from app_catalog
ansible.builtin.fail:
msg: >-
app={{ app }} is not in app_catalog. Add it with type: django (docker)
or type: node-static. Known: {{ app_catalog.keys() | list }}.
when:
- app is defined
- app not in app_catalog
- name: Deploy Django (docker compose) apps
ansible.builtin.include_tasks: django.yml
loop: "{{ deploy_targets | selectattr('name', 'in', django_names) | list }}"
loop_control:
loop_var: app_item
label: "{{ app_item.name }}/{{ app_item.env }}"
- name: Run Django migrations once (shared external DB)
ansible.builtin.command:
cmd: >-
docker compose -f {{ app_catalog[item.name].compose_file }} --env-file .env
exec -T {{ app_catalog[item.name].web_service }}
{{ app_catalog[item.name].migrate_cmd }}
chdir: "{{ apps_src_dir }}/{{ item.name }}_{{ item.env }}"
environment:
COMPOSE_PROJECT_NAME: "{{ item.name }}_{{ item.env }}"
loop: "{{ deploy_targets | selectattr('name', 'in', django_names) | list }}"
loop_control:
label: "{{ item.name }}/{{ item.env }}"
become: true
become_user: "{{ admin_user }}"
run_once: true
changed_when: true
- name: Deploy node static apps
ansible.builtin.include_tasks: node_static.yml
loop: "{{ deploy_targets | selectattr('name', 'in', node_names) | list }}"
loop_control:
loop_var: app_item
label: "{{ app_item.name }}/{{ app_item.env }}"