Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ obj/
.env
.env.*
!deploy/.env.example
# #458 — VITE_APP_VERSION only, a public string baked into the client bundle
# regardless of whether it's committed; release-please bumps it in lockstep
# with version.txt (see release-please-config.json's extra-files).
!web/.env.production
appsettings.Development.local.json

# OS/editor noise
Expand Down
9 changes: 9 additions & 0 deletions docs/decisions/351-releases.md
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,15 @@ Two stages, deliberately separate: **CI publishes, the release PR versions.**
`.release-please-manifest.json`, and `version.txt`. **Never hand-edit the manifest
or `version.txt`**; release-please owns them and a manual edit desynchronises the
version it believes from the tags that exist.
- **`extra-files` (#458) extends that ownership to `web/.env.production`'s
`VITE_APP_VERSION`** — release-please's `generic` updater bumps it in the same
release PR as `version.txt`/the manifest, anchored by an `x-release-please-version`
marker comment on the line above (not a same-line trailing comment — dotenv-style
parsers don't strip inline `#` comments the way YAML/generic key:value formats do,
so a trailing marker would have become part of the value). Vite loads
`.env.production` automatically for a production build; no Dockerfile/CI plumbing
needed beyond the file itself. Same "never hand-edit" rule applies to the value —
only the marker's presence and the file's existence are this repo's to maintain.
- **The release PR is opened with a GitHub App token, not `GITHUB_TOKEN`** — and
both reasons are load-bearing, so do not "simplify" it back.
1. `GITHUB_TOKEN` **cannot open a pull request at all** unless the repo-wide
Expand Down
6 changes: 6 additions & 0 deletions release-please-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@
"bump-patch-for-minor-pre-major": true,
"changelog-path": "CHANGELOG.md",
"include-component-in-tag": false,
"extra-files": [
{
"type": "generic",
"path": "web/.env.production"
}
],
"changelog-sections": [
{ "type": "feat", "section": "Features" },
{ "type": "fix", "section": "Bug fixes" },
Expand Down
2 changes: 2 additions & 0 deletions web/.env.production
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# x-release-please-version
VITE_APP_VERSION=0.0.3
4 changes: 4 additions & 0 deletions web/src/i18n/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,10 @@ export const en = {
skipToContent: "Skip to main content",
primaryNavAriaLabel: "Primary",
signOut: "Sign out",
// {{version}} is a bare semver string (e.g. "0.0.2"), never itself
// translated — only the surrounding "v" template varies by locale in
// principle, though every catalog currently agrees on it (#458).
versionLabel: "v{{version}}",
farmLoadFailedNeverLoaded:
"Could not load this farm's settings, so dates follow this device rather than the farm.",
farmLoadFailedStale:
Expand Down
1 change: 1 addition & 0 deletions web/src/i18n/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ export const es = {
skipToContent: "Saltar al contenido principal",
primaryNavAriaLabel: "Principal",
signOut: "Cerrar sesión",
versionLabel: "v{{version}}",
farmLoadFailedNeverLoaded:
"No se pudo cargar la configuración de esta granja, así que las "
+ "fechas siguen a este dispositivo en lugar de a la granja.",
Expand Down
1 change: 1 addition & 0 deletions web/src/i18n/tl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ export const tl = {
skipToContent: "Lumaktaw papunta sa pangunahing content",
primaryNavAriaLabel: "Pangunahin",
signOut: "Mag-sign out",
versionLabel: "v{{version}}",
farmLoadFailedNeverLoaded:
"Hindi na-load ang mga setting ng bukid na ito, kaya susundin ng mga "
+ "petsa ang device na ito sa halip na ang bukid.",
Expand Down
15 changes: 15 additions & 0 deletions web/src/routes/AppLayout.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,21 @@ describe("AppLayout sidebar", () => {
expect(screen.queryByRole("alert")).not.toBeInTheDocument();
});

it("shows no version line when VITE_APP_VERSION is unset (#458 — dev/test builds)", () => {
// import.meta.env.VITE_APP_VERSION is read once at module scope
// (AppLayout.tsx), matching errorReport.ts's own "absent in dev builds"
// contract — this test environment never sets it, so this proves the
// component renders nothing rather than a literal "vundefined".
renderWithProviders(<AppLayout />, { token: { sub: "u1", role: "Admin" } });
// Matches either a real version ("v0.0.2") or i18next's literal rendering
// of a missing interpolation (a bare "v", the {{version}} slot rendering
// empty) — both must be absent. Measured directly: i18next does not
// render "vundefined" for a missing var, it renders an empty
// interpolation, so a regex assuming the JS-string-concat shape would
// have silently passed against the exact bug this guards.
expect(screen.queryByText(/^v(\d.*)?$/)).not.toBeInTheDocument();
});

it("toggles light ↔ night, flipping the control and the root data-theme", () => {
renderWithProviders(<AppLayout />, { token: { sub: "u1", role: "Admin" } });
// jsdom has no matchMedia → initial theme resolves to light, so the control
Expand Down
8 changes: 8 additions & 0 deletions web/src/routes/AppLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ import { navGroups, tabEntries } from "./nav";

const ICON = 17;

// #458 — same env var, same "absent in dev" contract errorReport.ts already
// relies on for crash reports; read once at module scope rather than per render.
const APP_VERSION = import.meta.env.VITE_APP_VERSION as string | undefined;

// Authenticated shell (#52 redesign): an aubergine sidebar — the brand's
// navigation spine — with the 15+ destinations grouped by job, each with a
// lucide glyph. Role-tiered (#103): links and whole groups hide per role; the
Expand Down Expand Up @@ -73,6 +77,10 @@ export function AppLayout() {
<button className="link" onClick={onLogout}>
<LogOut size={ICON} aria-hidden /><span>{t("signOut")}</span>
</button>
{/* #458 — set at build time (VITE_APP_VERSION, release-please-owned
via web/.env.production); absent in dev builds, so this line
simply doesn't render rather than showing "vundefined". */}
{APP_VERSION && <p className="sidebar-version">{t("versionLabel", { version: APP_VERSION })}</p>}
</div>
</aside>

Expand Down
8 changes: 8 additions & 0 deletions web/src/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -1223,6 +1223,14 @@ input.cell {
text-decoration: none;
}

.sidebar-version {
margin: 0.35rem 0 0.6rem;
padding: 0 0.25rem;
font-size: 0.75rem;
color: var(--on-brand-mute);
opacity: 0.7;
}

/* -------------------------------------------- Bottom nav (mobile) */

/* The tab bar and its More sheet own navigation below 900px; the sidebar owns
Expand Down
Loading