From 6dc4d6cfd517c51d49cec8f7e5a55caf7d92a624 Mon Sep 17 00:00:00 2001 From: Anil Belur Date: Mon, 20 Jul 2026 09:46:22 +1000 Subject: [PATCH] docs: rename Add-on to App per HA terminology change Home Assistant renamed Add-ons to Apps; update all user-facing prose, log messages, and CI display names. Platform identifiers unchanged: io.hass.type label, supervisor badge URL, action-addon-linter ref, mermaid node ids, CI job id. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Anil Belur --- .github/workflows/ci.yaml | 6 ++--- README.md | 32 +++++++++++------------ gitops_backup/CHANGELOG.md | 12 ++++----- gitops_backup/DOCS.md | 30 ++++++++++----------- gitops_backup/rootfs/app/gitops_backup.sh | 10 +++---- gitops_backup/translations/en.yaml | 2 +- tests/self_check.sh | 8 +++--- 7 files changed, 50 insertions(+), 50 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 9f103e9..01de06c 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -49,14 +49,14 @@ jobs: run: bash tests/self_check.sh addon-lint: - name: Add-on Lint + name: App Lint runs-on: ubuntu-latest timeout-minutes: 5 steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false - - name: Home Assistant Add-on Lint + - name: Home Assistant App Lint uses: frenck/action-addon-linter@f995494fd84fae6310d23617e66d0e37de4f14eb # v2.21.0 with: path: ./gitops_backup @@ -69,7 +69,7 @@ jobs: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false - - name: Build add-on image + - name: Build app image run: | docker build \ --build-arg BUILD_FROM=ghcr.io/home-assistant/amd64-base:3.21 \ diff --git a/README.md b/README.md index 35ade6b..3d90af2 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# HA GitOps โ€” Home Assistant Add-on +# HA GitOps โ€” Home Assistant App **PR-gated GitHub backup for your Home Assistant config.** Nothing force-pushes, every change is a reviewable pull request, and CI validates your config against @@ -10,10 +10,10 @@ pinned Home Assistant versions *before* it can merge. [![Downloads](https://img.shields.io/github/downloads/askb/ha-gitops/total?label=downloads)](https://github.com/askb/ha-gitops/releases) [![License](https://img.shields.io/github/license/askb/ha-gitops)](LICENSE) -## Why another config-backup add-on? +## Why another config-backup app? Most config-sync tools upload your files straight onto a branch โ€” some even -force-push. That gives you a *copy*, not *control*. This add-on treats your +force-push. That gives you a *copy*, not *control*. This app treats your config like production infrastructure: | | Direct-push sync tools | GitOps Config Backup | @@ -30,7 +30,7 @@ config like production infrastructure: flowchart LR subgraph HA["๐Ÿ  Home Assistant box"] CFG[("/config")] - ADDON["GitOps Backup add-on
two-way sync, every run:
โฌ‡ pull merged โ†’ โฌ† push drift"] + ADDON["GitOps Backup app
two-way sync, every run:
โฌ‡ pull merged โ†’ โฌ† push drift"] CFG -- "โ‘  local edits
(UI, HA upgrades)" --> ADDON ADDON -- "โ‘ค merged changes
applied to /config" --> CFG end @@ -68,8 +68,8 @@ flowchart LR ``` **The loop, numbered:** โ‘  you edit via the HA UI (or an upgrade changes files) โ†’ -โ‘ก the add-on turns that drift into a PR โ€” or you open a config PR yourself โ†’ -โ‘ข CI gates it, you merge โ†’ โ‘ฃ the add-on's next run pulls merged `main` โ†’ +โ‘ก the app turns that drift into a PR โ€” or you open a config PR yourself โ†’ +โ‘ข CI gates it, you merge โ†’ โ‘ฃ the app's next run pulls merged `main` โ†’ โ‘ค and applies it to `/config`. Both directions go through the same run: it **pulls first, then pushes drift**, so box and repo converge on `main`. @@ -82,16 +82,16 @@ it **pulls first, then pushes drift**, so box and repo converge on `main`. a `stable` early-warning leg) **+ a smoke boot** โ€” the pinned HA Core container starts with your config and must answer HTTP with no `Invalid config` โ€” upgrade breakage surfaces in CI, not on your Pi. -3. **You merge** (from your phone, if you like). The next add-on run rebases the +3. **You merge** (from your phone, if you like). The next app run rebases the merged state back onto the box. ## Install -1. Add this repository to your Add-on store (badge above), install +1. Add this repository to your App store (badge above), install **GitOps Config Backup**. 2. Create a GitHub repo (private recommended) and a fine-grained PAT with *Contents: read/write* and *Pull requests: read/write* on it. -3. Configure the add-on: +3. Configure the app: ```yaml github_repo: you/your-ha-config @@ -116,7 +116,7 @@ the initial import; every run after that only ever opens PRs. ## Status sensor (optional) -The add-on writes `.gitops_backup_status` (`status:detail:extra`) to your config +The app writes `.gitops_backup_status` (`status:detail:extra`) to your config dir. Expose it in HA: ```yaml @@ -146,23 +146,23 @@ ssh root@ 'ha core restart' | `base_branch` | `main` | Branch PRs target | | `branch_prefix` | `auto-backup` | Prefix for backup branches | | `interval_hours` | `24` | Hours between runs (1โ€“168) | -| `run_at_start` | `false` | Also run when the add-on starts | +| `run_at_start` | `false` | Also run when the app starts | | `dry_run` | `false` | Log what would change; push nothing | | `commit_name` / `commit_email` | see config | Commit author | | `signoff` | `true` | Add `Signed-off-by` (DCO) to commits | ## Security notes -- The token lives in the add-on options (Supervisor-encrypted), never in git. +- The token lives in the app options (Supervisor-encrypted), never in git. - The seeded `.gitignore` excludes `secrets.yaml`, `.storage/`, `.cloud/`, - databases, logs, and the add-on's own status/log files (committing status + databases, logs, and the app's own status/log files (committing status files creates a PR-per-day feedback loop โ€” learned the hard way). - CI validates with **stub** secrets; your real `secrets.yaml` never leaves the box. -- Nothing in this add-on ever force-pushes or writes to your base branch after +- Nothing in this app ever force-pushes or writes to your base branch after the initial bootstrap import. - **This is change tracking, not disaster recovery** โ€” pair it with HA's - native encrypted backups + the Google Drive Backup add-on for full system - state (`secrets.yaml`, `.storage/`, databases, add-ons). See the add-on + native encrypted backups + the Google Drive Backup app for full system + state (`secrets.yaml`, `.storage/`, databases, apps). See the app DOCS for the full "what is backed up where" table and where secrets belong. ## License diff --git a/gitops_backup/CHANGELOG.md b/gitops_backup/CHANGELOG.md index d73ae9c..fb96a40 100644 --- a/gitops_backup/CHANGELOG.md +++ b/gitops_backup/CHANGELOG.md @@ -3,17 +3,17 @@ ## 0.3.0 - Feature: `apply_after_pull` โ€” after a merged PR is pulled into `/config`, the - add-on now applies it to the running Home Assistant instead of leaving files + app now applies it to the running Home Assistant instead of leaving files on disk unloaded. Default `reload` calls `homeassistant.reload_all` (no restart); `restart` restarts HA (for `configuration.yaml` integration or `custom_components/` changes); `off` keeps the previous behaviour. Only fires when the sync actually advanced `HEAD`, never in `dry_run`. Requires the - add-on's Home Assistant API access (now enabled via `homeassistant_api`). + app's Home Assistant API access (now enabled via `homeassistant_api`). ## 0.2.1 - Security: the never-commit guarantee (secrets, credentials, `.storage/`, - addon runtime files) now holds for **migrated** repos too, not just fresh + app runtime files) now holds for **migrated** repos too, not just fresh ones. Patterns are enforced via `.git/info/exclude` on every run, and any secret a prior run already committed is untracked (the removal flows out through the next backup PR). Previously a repo that arrived with its own @@ -21,18 +21,18 @@ ## 0.2.0 -- Docs: make the two-pillar backup model explicit โ€” config repo (this add-on) +- Docs: make the two-pillar backup model explicit โ€” config repo (this app) + encrypted HA full backup (e.g. Google Drive Backup) = 100% restorable; neither alone is enough. Sharpened the "what is backed up where" table (names exactly what `.storage/` holds and what lives outside `/config`). - Security: seed `.gitignore` now also excludes `*.token` / `.google.token` so OAuth tokens (e.g. Google Calendar) never land in the repo. -- First bootstrap now logs a disaster-recovery warning: this add-on versions +- First bootstrap now logs a disaster-recovery warning: this app versions declarative config only; run HA full backups for a complete restore. ## 0.1.1 -- Fix: keep the addon's own runtime files (`.gitops_backup_status`, +- Fix: keep the app's own runtime files (`.gitops_backup_status`, `gitops_backup.log`) out of backups when migrating a repo that already has a `.gitignore` โ€” they were previously swept into backup PRs and risked a status-file PR feedback loop. Now excluded via `.git/info/exclude` on every diff --git a/gitops_backup/DOCS.md b/gitops_backup/DOCS.md index 31f14a1..27d12d9 100644 --- a/gitops_backup/DOCS.md +++ b/gitops_backup/DOCS.md @@ -13,14 +13,14 @@ PR-gated GitHub backup for your Home Assistant config. ### 1. Create the repository github.com โ†’ **+** โ†’ **New repository** โ†’ name it (e.g. `my-ha-config`) โ†’ -**Private** โ†’ Create. Don't add a README โ€” the add-on bootstraps the content. +**Private** โ†’ Create. Don't add a README โ€” the app bootstraps the content. ### 2. Create the token 1. github.com โ†’ your avatar โ†’ **Settings** โ†’ **Developer settings** โ†’ **Personal access tokens** โ†’ **Fine-grained tokens** โ†’ **Generate new token** 2. **Token name**: `ha-gitops` ยท **Expiration**: 1 year (set a calendar - reminder โ€” the add-on status will show `error` when it expires) + reminder โ€” the app status will show `error` when it expires) 3. **Repository access**: *Only select repositories* โ†’ pick the repo from step 1 4. **Permissions โ†’ Repository permissions**: - **Contents**: Read and write @@ -31,9 +31,9 @@ github.com โ†’ **+** โ†’ **New repository** โ†’ name it (e.g. `my-ha-config`) > The token can only touch that single repo. Even if it leaked, your other > repos and account are untouchable โ€” that's why fine-grained beats classic PATs. -### 3. Configure the add-on +### 3. Configure the app -Settings โ†’ Add-ons โ†’ GitOps Config Backup โ†’ **Configuration**: +Settings โ†’ Apps โ†’ GitOps Config Backup โ†’ **Configuration**: ```yaml github_repo: yourname/my-ha-config @@ -41,11 +41,11 @@ github_token: github_pat_XXXXXXXX dry_run: true # first run: log only, push nothing ``` -Start the add-on, then check `gitops_backup.log` in your config folder to see +Start the app, then check `gitops_backup.log` in your config folder to see what *would* be committed. Happy? Set `dry_run: false` and restart. The first real run pushes the initial import; every run after that only opens PRs. -The token is stored in the add-on options by the Supervisor โ€” it never goes +The token is stored in the app options by the Supervisor โ€” it never goes into git, and the seeded `.gitignore` keeps `secrets.yaml` and other sensitive files out of the repo entirely. @@ -59,30 +59,30 @@ repo โ†’ Settings โ†’ Branches โ†’ require the *HA Config Validation* and ## What is backed up where (best practice) -This add-on is **change tracking for your config**, not disaster recovery. +This app is **change tracking for your config**, not disaster recovery. Run both layers: -| Data | This add-on (GitHub) | HA Backup + Google Drive | +| Data | This app (GitHub) | HA Backup + Google Drive | |------|----------------------|--------------------------| | Config YAML (automations, dashboards, โ€ฆ) | โœ… versioned, reviewable diffs | โœ… inside the archive | | `secrets.yaml` | โŒ **never** (gitignored) | โœ… inside the **encrypted** archive | | `.storage/` โ€” UI-added **integrations**, **logins/tokens**, **device/entity/area registries**, **UI (Lovelace) dashboards**, helpers | โŒ never (holds secrets) | โœ… **most restore-critical item** | | Credentials: `*.token`, `.google.token`, `*.key`, `*.pem`, `.cloud/` | โŒ never | โœ… | | Databases, history, logs | โŒ never | โœ… (recorder DB, if selected) | -| Add-on configs/data, TLS certs (`/ssl`) โ€” live **outside** `/config` | โŒ add-on can't see them | โœ… | +| App configs/data, TLS certs (`/ssl`) โ€” live **outside** `/config` | โŒ app can't see them | โœ… | | Media, camera recordings | โŒ | usually excluded โ€” external drive | > **The 100% guarantee: config repo + encrypted full backup. Neither alone is > enough.** Restore from the git repo *only* and you'd get your YAML back but > have to re-do by hand every UI-added integration, all logins/tokens, device -> and entity names, UI dashboards, `secrets.yaml`, and every add-on โ€” because +> and entity names, UI dashboards, `secrets.yaml`, and every app โ€” because > those live in `.storage/`, `secrets.yaml`, or outside `/config`. That's why a > full backup (Pillar 2) is mandatory, not optional. **Recommended stack:** ```text -GitHub (this add-on) โ†’ config changes: who/what/when, PR review, CI gate +GitHub (this app) โ†’ config changes: who/what/when, PR review, CI gate HA auto backup (daily) โ†’ full system state, encrypted, on the box โ””โ”€ Google Drive Backup โ†’ off-site copies of those archives Password manager โ†’ the three keys to the kingdom (below) @@ -96,13 +96,13 @@ Password manager โ†’ the three keys to the kingdom (below) - Store in your **password manager** (never in the repo, never in a README): 1. the HA **backup encryption password** โ€” without it your off-site backups are unrestorable, - 2. the **GitHub PAT** for this add-on, + 2. the **GitHub PAT** for this app, 3. your HA admin credentials. - CI validates PRs with **stub** secrets (step 4) โ€” real values never leave the box. **Disaster recovery order:** reinstall HAOS โ†’ restore the encrypted HA backup -(brings back `secrets.yaml`, `.storage`, add-ons) โ†’ your config repo is then +(brings back `secrets.yaml`, `.storage`, apps) โ†’ your config repo is then already live on the box and git history simply resumes. ## Options @@ -114,7 +114,7 @@ already live on the box and git history simply resumes. | `base_branch` | `main` | Branch PRs target | | `branch_prefix` | `auto-backup` | Prefix for backup branches | | `interval_hours` | `24` | Hours between runs (1โ€“168) | -| `run_at_start` | `false` | Also run when the add-on starts | +| `run_at_start` | `false` | Also run when the app starts | | `dry_run` | `false` | Log what would change; push nothing | | `commit_name` / `commit_email` | see defaults | Commit author identity | | `signoff` | `true` | Add `Signed-off-by` (DCO) to commits | @@ -124,7 +124,7 @@ already live on the box and git history simply resumes. Everything in your config folder **except** the seeded `.gitignore` exclusions: `secrets.yaml`, `.storage/`, `.cloud/`, databases, logs, `*.token`, -`.google.token`, `known_devices.yaml`, `ip_bans.yaml`, and the add-on's own +`.google.token`, `known_devices.yaml`, `ip_bans.yaml`, and the app's own status/log files. ## Status in Home Assistant diff --git a/gitops_backup/rootfs/app/gitops_backup.sh b/gitops_backup/rootfs/app/gitops_backup.sh index 23d39d0..8597350 100755 --- a/gitops_backup/rootfs/app/gitops_backup.sh +++ b/gitops_backup/rootfs/app/gitops_backup.sh @@ -107,7 +107,7 @@ OZW_Log.txt .uuid .ha_run.lock -# This addon's runtime metadata (committing it creates a PR feedback loop) +# This app's runtime metadata (committing it creates a PR feedback loop) .gitops_backup_status gitops_backup.log @@ -121,7 +121,7 @@ EOF } never_commit_patterns() { - # Secrets, credentials, and the addon's own runtime metadata โ€” must never be + # Secrets, credentials, and the app's own runtime metadata โ€” must never be # committed. Shared by the exclude writer and the untracker. cat <<'EOF' secrets.yaml @@ -139,7 +139,7 @@ EOF write_secret_excludes() { # Write the never-commit patterns to .git/info/exclude (git's local, - # un-shared ignore) so secrets/credentials + addon metadata stay out of + # un-shared ignore) so secrets/credentials + app metadata stay out of # backups even when migrating a repo that already has its own .gitignore # (seed_gitignore only writes one for a fresh repo). Called before the stash # so untracked secrets are never swept in. Idempotent: replace our block. @@ -177,7 +177,7 @@ bootstrap_if_needed() { # Returns 0 if bootstrap ran (caller should stop), 1 if repo already exists if [ -d .git ]; then return 1; fi log "No git repo in ${CONFIG_DIR} โ€” bootstrapping" - log "โš  This add-on versions DECLARATIVE config only. It does NOT back up secrets.yaml, .storage/ (UI-configured integrations, logins, device/entity registries, dashboards), the database, or add-on data โ€” those are excluded on purpose. For a 100% restore you MUST also run HA full backups (e.g. the Google Drive Backup add-on). See this add-on's DOCS โ†’ 'What is backed up where'." + log "โš  This app versions DECLARATIVE config only. It does NOT back up secrets.yaml, .storage/ (UI-configured integrations, logins, device/entity registries, dashboards), the database, or app data โ€” those are excluded on purpose. For a 100% restore you MUST also run HA full backups (e.g. the Google Drive Backup app). See this app's DOCS โ†’ 'What is backed up where'." if [ "$DRY_RUN" = "true" ]; then seed_gitignore log "DRY RUN: would git init, commit initial import, push to ${BASE_BRANCH}" @@ -295,7 +295,7 @@ main() { local pr_url if pr_url=$(create_pull_request "$branch_name" \ "chore: auto backup $(date '+%Y-%m-%d %H:%M')" \ - "Automated Home Assistant config backup โ€” ${changed_count} changed file(s). Opened by the GitOps Config Backup addon; review and merge.") \ + "Automated Home Assistant config backup โ€” ${changed_count} changed file(s). Opened by the GitOps Config Backup app; review and merge.") \ && [ -n "$pr_url" ]; then write_status success pr_created "$pr_url" log "โœ… Backup complete โ€” PR opened: ${pr_url}" diff --git a/gitops_backup/translations/en.yaml b/gitops_backup/translations/en.yaml index c027398..9fab040 100644 --- a/gitops_backup/translations/en.yaml +++ b/gitops_backup/translations/en.yaml @@ -21,7 +21,7 @@ configuration: description: Hours between backup runs. run_at_start: name: Run at start - description: Also run a backup when the add-on starts. + description: Also run a backup when the app starts. dry_run: name: Dry run description: Log what would change without pushing anything. diff --git a/tests/self_check.sh b/tests/self_check.sh index 98edb76..4986015 100755 --- a/tests/self_check.sh +++ b/tests/self_check.sh @@ -62,14 +62,14 @@ git -c safe.bareRepository=all -C "$TMP/origin.git" branch | grep -q "auto-backu status | grep -q "warning:branch_pushed" || { echo "FAIL: drift status = $(status)"; exit 1; } git -C "$TMP/config" branch --show-current | grep -q "^main$" || { echo "FAIL: not back on main"; exit 1; } -# 4. Migration case: repo already had its own .gitignore lacking the addon's -# entries, and was never excluded. The addon's own status file + log AND any +# 4. Migration case: repo already had its own .gitignore lacking the app's +# entries, and was never excluded. The app's own status file + log AND any # secrets/credentials must be kept out of the backup โ€” even an already- # committed secret must be untracked (regression test for seed_gitignore # skip-when-.gitignore-exists + secret-leak on migrated repos). sleep 1 # ensure a distinct auto-backup/ branch name rm -f "$TMP/config/.git/info/exclude" # never-excluded repo -printf 'secrets.yaml\n' > "$TMP/config/.gitignore" # user ignore, no addon/token entries +printf 'secrets.yaml\n' > "$TMP/config/.gitignore" # user ignore, no app/token entries HOME="$TMP" git -C "$TMP/config" add .gitignore HOME="$TMP" git -C "$TMP/config" commit -q -m "user gitignore" echo "success:no_changes:" > "$TMP/config/.gitops_backup_status" @@ -84,7 +84,7 @@ newbranch=$(git -c safe.bareRepository=all -C "$TMP/origin.git" branch \ | grep -o 'auto-backup/[^ ]*' | tail -1) tree=$(git -c safe.bareRepository=all -C "$TMP/origin.git" ls-tree -r --name-only "$newbranch") if echo "$tree" | grep -qE '^\.gitops_backup_status$|^gitops_backup\.log$'; then - echo "FAIL: addon runtime files committed to backup branch"; exit 1 + echo "FAIL: app runtime files committed to backup branch"; exit 1 fi if echo "$tree" | grep -qE '^\.google\.token$|^leaked\.key$|^secrets\.yaml$'; then echo "FAIL: a secret/credential was committed to the backup branch"; exit 1