mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 02:54:26 +00:00
fix(discourse): bootstrap only for what the bootstrap consumes
Three notifiers pointed at one handler, and that handler always ran ./launcher rebuild. A rotated database password therefore cost a full image build with bundle install and asset precompile: 961.82s in pass 1 and 13m16s in pass 2 of job 103878400427, on a run that then missed its wall-clock budget by 201s. The launcher draws the line itself. set_template_info reads app.yml on every call and turns env: into -e flags that run_start hands to docker run, while templates:, params: and hooks: are consumed during bootstrap only. An env: change needs a new container, not a new image. run_start takes the docker run branch only when no container exists, which is why the start path still stops and removes it first. The digest task is the discriminator: it records a sha256 over every top-level key except env:, so a credential-only change leaves it untouched and the bootstrap handler is never notified. A hash rather than the content because that section carries the OIDC client secret and the administrator password. The guard against running both paths lives in a fact cleared per role run. A register from inside the included file would have outlived the play and, in a later one, silently skipped the recreate while the config change never reached the container. from_yaml adds no failure mode: the launcher parses the same file with YAML.load, so anything it rejects was already a broken deployment. start without an image is structurally impossible, since a running container implies its image and a missing one notifies the bootstrap. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
a605c0aa79
commit
f47ea0e5bb
4 files changed
+38
-5
No files matched your search
@@ -1,5 +1,21 @@
|
||||
---
|
||||
- name: "🔨 Bootstrap the stack host image"
|
||||
include_tasks: stack_host.yml
|
||||
vars:
|
||||
DISCOURSE_LAUNCHER_ACTION: rebuild
|
||||
when: IS_STACK_HOST | bool
|
||||
listen: rebuild-discourse
|
||||
|
||||
- name: "📝 Record that the bootstrap already recreated the container"
|
||||
ansible.builtin.set_fact:
|
||||
DISCOURSE_REBUILT: true
|
||||
listen: rebuild-discourse
|
||||
|
||||
- name: "🖥️ Handle stack host"
|
||||
include_tasks: stack_host.yml
|
||||
when: IS_STACK_HOST | bool
|
||||
vars:
|
||||
DISCOURSE_LAUNCHER_ACTION: start
|
||||
when:
|
||||
- IS_STACK_HOST | bool
|
||||
- not DISCOURSE_REBUILT | bool
|
||||
listen: recreate-discourse
|
||||
@@ -25,9 +25,9 @@
|
||||
- DISCOURSE_DATABASE_IS_SHARED | bool
|
||||
- DEPLOYMENT_MODE == 'compose'
|
||||
|
||||
- name: 🔨 rebuild discourse
|
||||
- name: "🔨 {{ DISCOURSE_LAUNCHER_ACTION }} discourse"
|
||||
shell: >
|
||||
./launcher rebuild {{ DISCOURSE_CONTAINER }}
|
||||
./launcher {{ DISCOURSE_LAUNCHER_ACTION }} {{ DISCOURSE_CONTAINER }}
|
||||
{% if DOCKER_IN_CONTAINER | bool %}--skip-prereqs{% endif %}
|
||||
args:
|
||||
executable: /bin/bash
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
---
|
||||
# Exception: set_fact outlives the play, so the guard the recreate handler
|
||||
# reads has to be cleared per role run or a stale true silently skips it.
|
||||
- name: "🧹 Clear the bootstrap marker of an earlier play"
|
||||
ansible.builtin.set_fact:
|
||||
DISCOURSE_REBUILT: false
|
||||
|
||||
- name: ⬇️ Pull Discourse docker repository
|
||||
git:
|
||||
repo: "{{ DISCOURSE_REPOSITORY_URL }}"
|
||||
@@ -10,7 +16,7 @@
|
||||
retries: 12
|
||||
delay: 30
|
||||
until: discourse_repo_pull is succeeded
|
||||
notify: recreate-discourse
|
||||
notify: rebuild-discourse
|
||||
become: true
|
||||
|
||||
- name: 🔧 set chmod 700 for '{{ DISCOURSE_CONTAINERS_DIR }}'
|
||||
@@ -26,6 +32,15 @@
|
||||
mode: "0640"
|
||||
notify: recreate-discourse
|
||||
|
||||
# Exception: launcher reads env: on every docker run but consumes templates,
|
||||
# params and hooks only during bootstrap, so only their digest may rebuild.
|
||||
- name: "🔑 Record the digest of everything the bootstrap consumes"
|
||||
stack_host_copy:
|
||||
content: "{{ lookup('template', 'config.yml.j2') | from_yaml | dict2items | rejectattr('key', 'eq', 'env') | items2dict | to_json | hash('sha256') }}"
|
||||
dest: "{{ DISCOURSE_BOOTSTRAP_DIGEST_DEST }}"
|
||||
mode: "0640"
|
||||
notify: rebuild-discourse
|
||||
|
||||
- name: "✅ Verify that '{{ DISCOURSE_CONTAINER }}' is running"
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
@@ -40,7 +55,7 @@
|
||||
register: docker_ps
|
||||
changed_when: docker_ps.stdout.strip() == ""
|
||||
failed_when: docker_ps.rc != 0
|
||||
notify: recreate-discourse
|
||||
notify: rebuild-discourse
|
||||
|
||||
- name: 📂 Load discourse handlers explicitly
|
||||
include_tasks: utils/load/handlers.yml
|
||||
|
||||
@@ -30,6 +30,8 @@ DISCOURSE_CONTAINERS_DIR: "{{ [ DISCOURSE_REPOSITORY_DIR, 'containers' ] | path_
|
||||
|
||||
DISCOURSE_APPLICATION_YML_DEST: "{{ [ DISCOURSE_CONTAINERS_DIR, DISCOURSE_CONTAINER ~ '.yml' ] | path_join }}"
|
||||
|
||||
DISCOURSE_BOOTSTRAP_DIGEST_DEST: "{{ [ DISCOURSE_CONTAINERS_DIR, DISCOURSE_CONTAINER ~ '.bootstrap.sha256' ] | path_join }}"
|
||||
|
||||
DISCOURSE_LDAP_ENABLED: "{{ lookup('config', application_id, 'services.ldap.enabled') }}"
|
||||
|
||||
DISCOURSE_LITELLM_ENABLED: "{{ lookup('config', application_id, 'services.litellm.enabled') | bool }}"
|
||||
|
||||
Reference in new issue
Block a user