feat(deploy): branch model — box tracks release, dev is WIP-integration [#779 #344]

webhook ref-guard + sha-watch sensor pinned to ?sha=release;
sensor renamed pfv_deploy_gitea_release_head; master frozen.
https://projects.knownelement.com/issues/779
This commit is contained in:
2026-09-04 06:32:56 -05:00
parent 41116c7feb
commit 09f3276b31
3 changed files with 24 additions and 17 deletions
+11 -4
View File
@@ -134,16 +134,23 @@ apps.
## 🚀 Deployment pipeline ## 🚀 Deployment pipeline
**Branch model (2026-09-04):** `dev` = WIP integration — the pipeline
IGNORES it. `release` = what the box runs — pushing release IS the deploy.
`master` is frozen (pre-branch-model history). A PR process with AI +
human reviewers will gate dev → release (founder, upcoming).
```mermaid ```mermaid
flowchart LR flowchart LR
PUSH[📝 git push] --> HOOK[gitea webhook<br/>fast path] PUSH[📝 push to dev] --> NOOP[🚫 pipeline ignores dev]
PUSH --> SHA[sha-watch<br/>safety net · 300 s poll] REL[🚀 merge dev → release<br/>and push] --> HOOK[gitea webhook<br/>fast path]
REL --> SHA[sha-watch<br/>release sha · 300 s poll]
HOOK & SHA --> CHECK{changed files?} HOOK & SHA --> CHECK{changed files?}
CHECK -->|packages/**| RESTART[♻️ core restart<br/>webhook triggers need it] CHECK -->|configuration.yaml,<br/>packages/**| RESTART[♻️ core restart<br/>webhook triggers need it]
CHECK -->|other YAML| RELOAD[⚡ reload only<br/>~5 min to live] CHECK -->|other YAML| RELOAD[⚡ reload only<br/>~5 min to live]
``` ```
Gate before pushing `packages/**`: `ha core check` must pass on-box. Gate before pushing `release` (and after any `packages/**` change on
`dev`): `ha core check` must pass on-box.
Measured end-to-end: 4 m 52 s commit → live. Measured end-to-end: 4 m 52 s commit → live.
## 🗺️ Repo map ## 🗺️ Repo map
+9 -9
View File
@@ -246,7 +246,7 @@
url: /lovelace-pfv-server-room/temps url: /lovelace-pfv-server-room/temps
message: '{{ trigger.entity_id }} climbing ~10F/15min — check cooling message: '{{ trigger.entity_id }} climbing ~10F/15min — check cooling
before the 104F threshold. (rate-of-change early warning)' before the 104F threshold. (rate-of-change early warning)'
# Deploy pipeline [#734]: gitea repo webhook (master pushes) -> forced pull # Deploy pipeline [#734]: gitea repo webhook (release pushes) -> forced pull
# -> minimal reload. Core restarts ONLY when configuration.yaml or a # -> minimal reload. Core restarts ONLY when configuration.yaml or a
# packages platform-sensor file changed; everything else reloads with no # packages platform-sensor file changed; everything else reloads with no
# bounce. The git_pull add-on's auto_restart is disabled - this webhook # bounce. The git_pull add-on's auto_restart is disabled - this webhook
@@ -254,7 +254,7 @@
- id: pfv_deploy_git_webhook - id: pfv_deploy_git_webhook
alias: PFV deploy - git push to pull + minimal reload alias: PFV deploy - git push to pull + minimal reload
description: >- description: >-
Gitea webhook on master pushes. Forces a git pull now, then Gitea webhook on release pushes. Forces a git pull now, then
homeassistant.reload_all unless files that require a core restart homeassistant.reload_all unless files that require a core restart
changed. Replaces the git_pull add-on timer auto-restart [#734]. changed. Replaces the git_pull add-on timer auto-restart [#734].
mode: queued mode: queued
@@ -269,9 +269,9 @@
actions: actions:
- if: - if:
- condition: template - condition: template
value_template: "{{ trigger.json.ref | default('') != 'refs/heads/master' }}" value_template: "{{ trigger.json.ref | default('') != 'refs/heads/release' }}"
then: then:
- stop: "deploy webhook: non-master ref {{ trigger.json.ref | default('') }} ignored" - stop: "deploy webhook: non-release ref {{ trigger.json.ref | default('') }} ignored"
- variables: - variables:
changed_files: >- changed_files: >-
{{ (trigger.json.commits | default([]) | map(attribute='added') | flatten | list) {{ (trigger.json.commits | default([]) | map(attribute='added') | flatten | list)
@@ -337,7 +337,7 @@
data: data:
title: PFV deploy complete title: PFV deploy complete
message: "Pulled + reloaded YAML at {{ now().strftime('%H:%M:%S') }} - no core restart. Files: {{ changed_files | join(', ') | truncate(120) }}" message: "Pulled + reloaded YAML at {{ now().strftime('%H:%M:%S') }} - no core restart. Files: {{ changed_files | join(', ') | truncate(120) }}"
# Deploy pipeline safety net [#734]: polls gitea master HEAD (rest sensor # Deploy pipeline safety net [#734]: polls gitea release HEAD (rest sensor
# in packages/deploy_pipeline.yaml) and applies pulls reload-only. The # in packages/deploy_pipeline.yaml) and applies pulls reload-only. The
# gitea webhook is the fast path once gitea's SSRF allowlist permits # gitea webhook is the fast path once gitea's SSRF allowlist permits
# pfv-bms.knel.net; until then this net catches every push within 5 min # pfv-bms.knel.net; until then this net catches every push within 5 min
@@ -346,7 +346,7 @@
- id: pfv_deploy_sha_watch - id: pfv_deploy_sha_watch
alias: PFV deploy - gitea sha watch safety net alias: PFV deploy - gitea sha watch safety net
description: >- description: >-
Master HEAD sha changed -> pull + CORE RESTART. Interim deploy Release HEAD sha changed -> pull + CORE RESTART. Interim deploy
trigger while gitea webhook delivery is blocked by its SSRF host trigger while gitea webhook delivery is blocked by its SSRF host
allowlist [#734]. Restart (not reload) is REQUIRED: automation allowlist [#734]. Restart (not reload) is REQUIRED: automation
reload loses webhook-trigger registration - found live 2026-09-03 reload loses webhook-trigger registration - found live 2026-09-03
@@ -354,12 +354,12 @@
scan ingest, this pipeline's own fast path) silently stop firing scan ingest, this pipeline's own fast path) silently stop firing
while the endpoint still returns HTTP 200. Core restart while the endpoint still returns HTTP 200. Core restart
re-registers them. Accepted trade-off: ~1 min HA outage per re-registers them. Accepted trade-off: ~1 min HA outage per
master deploy. release deploy.
mode: single mode: single
max_exceeded: silent max_exceeded: silent
triggers: triggers:
- trigger: state - trigger: state
entity_id: sensor.pfv_deploy_gitea_master_head entity_id: sensor.pfv_deploy_gitea_release_head
conditions: conditions:
- condition: template - condition: template
value_template: >- value_template: >-
@@ -383,7 +383,7 @@
data: data:
title: PFV deploy (sha watch) title: PFV deploy (sha watch)
message: >- message: >-
New master HEAD {{ states('sensor.pfv_deploy_gitea_master_head')[:7] }} New release HEAD {{ states('sensor.pfv_deploy_gitea_release_head')[:7] }}
pulled. Restarting HA core (webhook re-registration) - back in pulled. Restarting HA core (webhook re-registration) - back in
~1 min. ~1 min.
- action: homeassistant.restart - action: homeassistant.restart
+4 -4
View File
@@ -1,4 +1,4 @@
# Deploy pipeline [#734]: gitea master-HEAD sha watcher. Feeds the # Deploy pipeline [#734]: gitea release-branch sha watcher. Feeds the
# sha-watch deploy automation (reload-only apply). The gitea webhook # sha-watch deploy automation (reload-only apply). The gitea webhook
# (gitea hook #10 -> pfv_deploy_git_webhook automation) is the fast path # (gitea hook #10 -> pfv_deploy_git_webhook automation) is the fast path
# with full restart mapping, but gitea cannot deliver webhooks to # with full restart mapping, but gitea cannot deliver webhooks to
@@ -6,15 +6,15 @@
# (founder action via Cloudron file manager); this watcher is the # (founder action via Cloudron file manager); this watcher is the
# always-works safety net. # always-works safety net.
rest: rest:
- resource: https://git.knownelement.com/api/v1/repos/KNEL/pfv-bms/commits?limit=1 - resource: https://git.knownelement.com/api/v1/repos/KNEL/pfv-bms/commits?sha=release&limit=1
headers: headers:
Authorization: !secret gitea_auth_header Authorization: !secret gitea_auth_header
User-Agent: pfv-bms-deploy-watch User-Agent: pfv-bms-deploy-watch
scan_interval: 300 scan_interval: 300
verify_ssl: true verify_ssl: true
sensor: sensor:
- name: PFV deploy gitea master head - name: PFV deploy gitea release head
unique_id: pfv_deploy_gitea_master_head unique_id: pfv_deploy_gitea_release_head
value_template: "{{ value_json[0].sha if value_json is list and value_json | length > 0 else (value_json | string | truncate(90)) }}" value_template: "{{ value_json[0].sha if value_json is list and value_json | length > 0 else (value_json | string | truncate(90)) }}"
# E2E self-deploy proof commit [#734] - if you can read this in /config, # E2E self-deploy proof commit [#734] - if you can read this in /config,