skill(hassio-addon-workflow): record which cont-init scripts see CONFIG_LOCATION values (#3127)

* skill(hassio-addon-workflow): record which cont-init scripts see CONFIG_LOCATION values

Two templates inject environment variables into cont-init scripts and
their globs differ, which cost a round of review on #3125.
00-global_var.sh:344 covers /etc/cont-init.d/*.sh, so add-on options and
env_vars reach every cont-init script. 01-config_yaml.sh:195-200 — the
CONFIG_LOCATION config.yaml file many add-ons document for extra variables
— covers only /entrypoint.sh, /*/entrypoint.sh, /etc/services.d/*/*run*
and /etc/cont-init.d/*run*.

A new script called 89-powersync.sh therefore received options but silently
not file-configured values, while the add-on's existing 90-run.sh received
both: the app saw PS_DATABASE_URI while the new script, validating that
same setting, refused to start because it looked unset. Renaming it
89-powersync-run.sh fixed it in zero lines; the bespoke /.env parser
written first was 35 lines, sourced a user-controlled file six times as
root, and still got precedence wrong for an empty value.

Also record that 01-config_yaml.sh writes /.env unquoted where
00-global_var.sh applies dotenv_quote(), so a password containing $ or a
backtick is expanded and silently truncated on its way to the app.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* skill(hassio-addon-workflow): scope the cont-init injection entry correctly

Two reviewers on #3127 pointed out the entry taught a universal naming rule
that is not universal, and both were right.

01-config_yaml.sh:203 also writes s6's container_environment envdir when it
exists, and ha_entrypoint.sh's candidate_shebangs prefers
"with-contenv bashio" when the base image provides it — so in an s6 add-on
every cont-init script receives the value whatever its name is, and the
*run* glob stops mattering. The glob decides this only in an image with no
s6 at all, which is the wger case the entry came from.

Two smaller corrections in the same pass: 00-global_var.sh targets
/etc/cont-init.d/*.sh, so it misses an extensionless script — the repo has
exactly one, spotweb/rootfs/etc/cont-init.d/90-spotweb, which no glob here
reaches — and both templates append to /.env, which a script can source
explicitly, so "only sees" overstated it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: BirdNET-Go Addon Builder <addon-builder@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
authored and GitHub committed 2026-10-18 10:36:34 +03:00
1 parent 2c61752fb3
commit c30a02970b
1 file changed
+41
@@ -107,6 +107,47 @@ option is safe on upgrade. The `monica` add-on shipped exactly such a rename
tolerated. The base image has an `init-migrations` oneshot reading `/migrations` if a migration
is needed. Confirm before renaming a shipped option.
**Which values a new cont-init script receives depends on its filename, and in an add-on without
s6 that is the whole story.** Three mechanisms carry values in and none of them covers
everything:
- `00-global_var.sh:344` injects an `export` block into `/etc/cont-init.d/*.sh` — add-on options
and `env_vars`. Only `.sh` files: the repo's one extensionless cont-init script,
`spotweb/rootfs/etc/cont-init.d/90-spotweb`, is reached by none of these globs.
- `01-config_yaml.sh:195-200` injects the `CONFIG_LOCATION` config.yaml file's values, but only
into `/entrypoint.sh`, `/*/entrypoint.sh`, `/etc/services.d/*/*run*` and
`/etc/cont-init.d/*run*`.
- `01-config_yaml.sh:203` also writes s6's `container_environment` envdir when it exists, and
`ha_entrypoint.sh`'s `candidate_shebangs` prefers `with-contenv bashio` when the base image
has it. **Where that applies, every cont-init script receives the value whatever its name is**,
and the globs above stop mattering.
Both templates also append to `/.env`, which a script can source explicitly — at the cost of
sourcing a user-controlled file.
So the filename only decides this in an add-on whose image has no s6, and therefore no envdir and
no `with-contenv` (`wger/server` is one). There, a new script called `89-powersync.sh` received
options but silently *not* file-configured values, while the add-on's existing `90-run.sh`
received both: the app saw `PS_DATABASE_URI` while the new script, validating that same setting,
refused to start because it looked unset. It also generated a JWT keypair over a file-configured
one and won, because its pair was appended to `/.env` after the file's own lines and the last
assignment wins.
Renaming the script `89-powersync-run.sh` fixed it in zero lines. The bespoke `/.env` parser
written first was the wrong answer — 35 lines, sourcing that file six times as root, and it still
got precedence wrong for an explicitly empty value. Check a new script's name against both globs,
and check whether the image gives you `with-contenv` at all, before reaching for anything else
(wger #2950, fixed in #3125; the gap was caught by three review bots, the rename by Codex, the
scope of this entry by two more on #3127).
**`01-config_yaml.sh` writes values into `/.env` unquoted.** Line 188 appends the raw
`KEY=VALUE`, where `00-global_var.sh` puts its values through `dotenv_quote()` first. Anything
that sources `/.env` — including a `90-run.sh` that re-exports it into `/data/env.sh` for the app
— then expands `$`, backticks and `$(…)`, so a password like `p@ss$w0rd` arrives as `p@ss`,
truncated and silent, at the app as well as at anything else reading it. Point users at the
`env_vars` option for password-bearing values; it round-trips `$`, backticks, `#`, quotes,
backslashes and spaces intact (#3125).
Whichever mechanism you use, verify the service actually received it:
```bash