Analytics is silently off in expiro and larda #74

Closed
opened 2026-09-09 10:21:05 +00:00 by ahmad · 2 comments
Owner

Context

ahmad/expiro and ahmad/larda build their web bundles with Umami build
arguments whose source values do not exist, so both ship with
analytics silently disabled. The org conventions require Umami on every
project.

Surveyed 2026-09-09 through the contents and actions APIs across every
repository on the server, resolving each build argument to the
vars.* / secrets.* it actually reads rather than matching on the
argument's own name:

Repository Build argument Reads Exists
ahmad/expiro VITE_UMAMI_SCRIPT_URL secrets.VITE_UMAMI_SCRIPT_URL no
ahmad/expiro VITE_UMAMI_WEBSITE_ID secrets.VITE_UMAMI_WEBSITE_ID no
ahmad/larda VITE_UMAMI_SCRIPT_URL secrets.UMAMI_SCRIPT_URL no
ahmad/larda VITE_UMAMI_WEBSITE_ID secrets.UMAMI_WEBSITE_ID no

Every other repository resolves. ahmad/imamah and ahmad/portfolio
looked broken on a first pass and are not: their VITE_UMAMI_DOMAINS
argument reads vars.VPS_DEPLOY_DOMAIN, which is set. Matching on the
argument name rather than its source is what produced that false
positive, and it is worth avoiding on any recheck.

The same two repositories are the only ones that diverge from the fleet
in two further ways
, which is very likely how this happened:

  • They read from secrets.*. Every other repository reads vars.*. A
    website id and a script URL are public — they are compiled into the
    bundle — so they are variables everywhere else.
  • They call it VITE_UMAMI_SCRIPT_URL. Everywhere else it is
    VITE_UMAMI_SRC.

So the values were probably never provisioned because the name being
looked for never matched the name the fleet provisions.

This is the failure mode #1 names as a rule: a piece that no-ops when
unconfigured must say it no-opped. Both apps degrade to a skip
correctly and neither reports it, so a working install and a silently
disabled one look identical until someone asks why there is no data.

Scope

  • In: aligning both repositories on the fleet's names and store —
    VITE_UMAMI_SRC and VITE_UMAMI_WEBSITE_ID read from vars.* — and
    the docs/CONFIGURATION.md rows that describe them.
  • In: making the web entry point log once when it skips loading the
    script because either value is empty, so the next silent failure is
    visible.
  • Out: provisioning the variable values themselves, which is not a code
    change.
  • Out: VITE_UMAMI_DOMAINS, which neither repository passes today.
  • Out: the other repositories, which resolve correctly.

Acceptance criteria

  • Given either repository, when release.yml is read, then the Umami
    build arguments read vars.VITE_UMAMI_SRC and
    vars.VITE_UMAMI_WEBSITE_ID.
  • Given either repository's docs/CONFIGURATION.md, when the Umami
    rows are read, then their type and "where it lives" say variable
    and match the workflow.
  • Given a build where either value is empty, when the app starts in a
    browser, then it records once that analytics was not loaded and why.
  • Given a build where both are set, when the app starts, then the
    Umami script loads as before.
### Context `ahmad/expiro` and `ahmad/larda` build their web bundles with Umami build arguments whose source values **do not exist**, so both ship with analytics silently disabled. The org conventions require Umami on every project. Surveyed 2026-09-09 through the contents and actions APIs across every repository on the server, resolving each build argument to the `vars.*` / `secrets.*` it actually reads rather than matching on the argument's own name: | Repository | Build argument | Reads | Exists | | --- | --- | --- | --- | | `ahmad/expiro` | `VITE_UMAMI_SCRIPT_URL` | `secrets.VITE_UMAMI_SCRIPT_URL` | **no** | | `ahmad/expiro` | `VITE_UMAMI_WEBSITE_ID` | `secrets.VITE_UMAMI_WEBSITE_ID` | **no** | | `ahmad/larda` | `VITE_UMAMI_SCRIPT_URL` | `secrets.UMAMI_SCRIPT_URL` | **no** | | `ahmad/larda` | `VITE_UMAMI_WEBSITE_ID` | `secrets.UMAMI_WEBSITE_ID` | **no** | Every other repository resolves. `ahmad/imamah` and `ahmad/portfolio` looked broken on a first pass and are not: their `VITE_UMAMI_DOMAINS` argument reads `vars.VPS_DEPLOY_DOMAIN`, which is set. Matching on the argument name rather than its source is what produced that false positive, and it is worth avoiding on any recheck. **The same two repositories are the only ones that diverge from the fleet in two further ways**, which is very likely how this happened: - They read from `secrets.*`. Every other repository reads `vars.*`. A website id and a script URL are public — they are compiled into the bundle — so they are variables everywhere else. - They call it `VITE_UMAMI_SCRIPT_URL`. Everywhere else it is `VITE_UMAMI_SRC`. So the values were probably never provisioned because the name being looked for never matched the name the fleet provisions. This is the failure mode `#1` names as a rule: a piece that no-ops when unconfigured must **say** it no-opped. Both apps degrade to a skip correctly and neither reports it, so a working install and a silently disabled one look identical until someone asks why there is no data. ### Scope - In: aligning both repositories on the fleet's names and store — `VITE_UMAMI_SRC` and `VITE_UMAMI_WEBSITE_ID` read from `vars.*` — and the `docs/CONFIGURATION.md` rows that describe them. - In: making the web entry point log once when it skips loading the script because either value is empty, so the next silent failure is visible. - Out: provisioning the variable values themselves, which is not a code change. - Out: `VITE_UMAMI_DOMAINS`, which neither repository passes today. - Out: the other repositories, which resolve correctly. ### Acceptance criteria - [ ] Given either repository, when `release.yml` is read, then the Umami build arguments read `vars.VITE_UMAMI_SRC` and `vars.VITE_UMAMI_WEBSITE_ID`. - [ ] Given either repository's `docs/CONFIGURATION.md`, when the Umami rows are read, then their type and "where it lives" say variable and match the workflow. - [ ] Given a build where either value is empty, when the app starts in a browser, then it records once that analytics was not loaded and why. - [ ] Given a build where both are set, when the app starts, then the Umami script loads as before.
ahmad self-assigned this 2026-09-09 10:44:33 +00:00
Author
Owner

Taking this. One pull request per repository, and the scope is smaller
than the issue's first framing because of two things I checked rather
than assumed.

The Docker plumbing is intact. Grepping apps/web/Dockerfile found
no ARG, which looked like a third failure on top of the missing values.
That path does not exist — both repositories build the web image from the
root Dockerfile, which declares VITE_UMAMI_SCRIPT_URL and
VITE_UMAMI_WEBSITE_ID and exports them to the Vite build correctly. So
the only thing broken is the source of the values.

Therefore no rename. The issue's scope said "align on the fleet's
names", and the tempting reading is to rename the build arg and the env
var to VITE_UMAMI_SRC throughout — workflow, Dockerfile, main.tsx,
vite-env.d.ts, .env.example. That is seven files a repository for no
behavioural gain. What actually caused this is the Forgejo variable
name: whoever provisioned the fleet set VITE_UMAMI_SRC, and these two
repositories look up something else. So the workflow reads
vars.VITE_UMAMI_SRC into the build arg the app already uses, and the
app is untouched:

VITE_UMAMI_SCRIPT_URL=${{ vars.VITE_UMAMI_SRC }}
VITE_UMAMI_WEBSITE_ID=${{ vars.VITE_UMAMI_WEBSITE_ID }}

The internal name is the app's own business; the external one has to
match what the fleet provisions.

larda's .env.example is wrong in a way that matters for local dev
too
— it lists UMAMI_SCRIPT_URL and UMAMI_WEBSITE_ID with no
VITE_ prefix, which Vite does not expose to browser code at all. So
analytics was unreachable in development as well as production. Fixed in
the same change, since it is the same defect.

The skip becomes visible, per the fourth criterion. loadAnalytics
currently returns silently when either value is empty; it will say so
once, which is what separates "not configured" from "broken". Both
repositories have an identical analytics.ts and neither has a test for
it, so each gains one.

This does not switch analytics back on, and I will not claim it does.
Neither repository has VITE_UMAMI_SRC or VITE_UMAMI_WEBSITE_ID as a
Forgejo variable — creating them is provisioning, which this loop does
not do and which the issue puts out of scope. After these merge, the
builds look up the right names and log plainly that they found nothing.
Setting the two variables on ahmad/expiro and ahmad/larda is yours,
and it is what actually restores analytics.

Taking this. One pull request per repository, and the scope is smaller than the issue's first framing because of two things I checked rather than assumed. **The Docker plumbing is intact.** Grepping `apps/web/Dockerfile` found no `ARG`, which looked like a third failure on top of the missing values. That path does not exist — both repositories build the web image from the **root** `Dockerfile`, which declares `VITE_UMAMI_SCRIPT_URL` and `VITE_UMAMI_WEBSITE_ID` and exports them to the Vite build correctly. So the only thing broken is the source of the values. **Therefore no rename.** The issue's scope said "align on the fleet's names", and the tempting reading is to rename the build arg and the env var to `VITE_UMAMI_SRC` throughout — workflow, `Dockerfile`, `main.tsx`, `vite-env.d.ts`, `.env.example`. That is seven files a repository for no behavioural gain. What actually caused this is the **Forgejo variable** name: whoever provisioned the fleet set `VITE_UMAMI_SRC`, and these two repositories look up something else. So the workflow reads `vars.VITE_UMAMI_SRC` into the build arg the app already uses, and the app is untouched: ```yaml VITE_UMAMI_SCRIPT_URL=${{ vars.VITE_UMAMI_SRC }} VITE_UMAMI_WEBSITE_ID=${{ vars.VITE_UMAMI_WEBSITE_ID }} ``` The internal name is the app's own business; the external one has to match what the fleet provisions. **`larda`'s `.env.example` is wrong in a way that matters for local dev too** — it lists `UMAMI_SCRIPT_URL` and `UMAMI_WEBSITE_ID` with no `VITE_` prefix, which Vite does not expose to browser code at all. So analytics was unreachable in development as well as production. Fixed in the same change, since it is the same defect. **The skip becomes visible**, per the fourth criterion. `loadAnalytics` currently returns silently when either value is empty; it will say so once, which is what separates "not configured" from "broken". Both repositories have an identical `analytics.ts` and neither has a test for it, so each gains one. **This does not switch analytics back on, and I will not claim it does.** Neither repository has `VITE_UMAMI_SRC` or `VITE_UMAMI_WEBSITE_ID` as a Forgejo variable — creating them is provisioning, which this loop does not do and which the issue puts out of scope. After these merge, the builds look up the right names and log plainly that they found nothing. Setting the two variables on `ahmad/expiro` and `ahmad/larda` is yours, and it is what actually restores analytics.
Author
Owner

Done. Both repositories merged:

Repository PR Merge
ahmad/expiro ahmad/expiro#84 7adcd6b
ahmad/larda ahmad/larda#72 e8cec05

Both now read vars.VITE_UMAMI_SRC and vars.VITE_UMAMI_WEBSITE_ID —
the names the fleet actually provisions. larda's .env.example also
gained the VITE_ prefix it never had, so analytics could not have worked
in local development either, for a second and independent reason.

The build arg and env var keep each repository's own spelling, and
that was deliberate: only the external name has to match what is
provisioned. Renaming throughout would have touched the root Dockerfile,
main.tsx, the type declarations and the example env for no behavioural
gain.

loadAnalytics now says which variable was empty instead of returning
silently, which is the part that stops this recurring. The test asserts
the message names the empty variable and not the other one, so an
implementation that simply lists both fails it.

This does not switch analytics back on, and it never claimed to.
Neither repository has VITE_UMAMI_SRC or VITE_UMAMI_WEBSITE_ID as a
Forgejo variable — checked through the actions API, not inferred. Creating
them is provisioning, which this issue put out of scope. What has changed
is that the builds now look up the right names and say plainly that they
found nothing; setting those two variables on ahmad/expiro and
ahmad/larda is what actually restores analytics
, and until then the
only visible difference is that the failure is no longer silent.

That is the one thing left on this issue, and it is not a code change.

A method note worth keeping, because it nearly went the other way: the
first survey behind this issue matched on the build argument's name
rather than the vars.*/secrets.* inside its ${{ }}, and reported
ahmad/imamah and ahmad/portfolio as broken. They are not — their
VITE_UMAMI_DOMAINS reads vars.VPS_DEPLOY_DOMAIN, which is set. Two
false positives that would have shipped as fact in this issue's table.

**Done.** Both repositories merged: | Repository | PR | Merge | |---|---|---| | `ahmad/expiro` | ahmad/expiro#84 | `7adcd6b` | | `ahmad/larda` | ahmad/larda#72 | `e8cec05` | Both now read `vars.VITE_UMAMI_SRC` and `vars.VITE_UMAMI_WEBSITE_ID` — the names the fleet actually provisions. `larda`'s `.env.example` also gained the `VITE_` prefix it never had, so analytics could not have worked in local development either, for a second and independent reason. **The build arg and env var keep each repository's own spelling**, and that was deliberate: only the external name has to match what is provisioned. Renaming throughout would have touched the root `Dockerfile`, `main.tsx`, the type declarations and the example env for no behavioural gain. **`loadAnalytics` now says which variable was empty** instead of returning silently, which is the part that stops this recurring. The test asserts the message names the empty variable **and not the other one**, so an implementation that simply lists both fails it. **This does not switch analytics back on, and it never claimed to.** Neither repository has `VITE_UMAMI_SRC` or `VITE_UMAMI_WEBSITE_ID` as a Forgejo variable — checked through the actions API, not inferred. Creating them is provisioning, which this issue put out of scope. What has changed is that the builds now look up the right names and say plainly that they found nothing; **setting those two variables on `ahmad/expiro` and `ahmad/larda` is what actually restores analytics**, and until then the only visible difference is that the failure is no longer silent. That is the one thing left on this issue, and it is not a code change. **A method note worth keeping**, because it nearly went the other way: the first survey behind this issue matched on the build **argument's** name rather than the `vars.*`/`secrets.*` inside its `${{ }}`, and reported `ahmad/imamah` and `ahmad/portfolio` as broken. They are not — their `VITE_UMAMI_DOMAINS` reads `vars.VPS_DEPLOY_DOMAIN`, which is set. Two false positives that would have shipped as fact in this issue's table.
ahmad 2026-09-09 13:14:26 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
amtronics/platform-actions#74
No description provided.