Skip to content

Commit c02b9c5

Browse files
aaspinwallclaude
andcommitted
feat(addon): build the system add-on against a local backend
Add a `:local` build path that produces the system/built-in add-on (system id, dev-mode bundle) wired to a locally running Send backend + client, so the system-add-on code path can be exercised end-to-end against localhost. - build:dev:system:local / sync:builtin:local / dev:builtin:local scripts set ADDON_ENV=local. - scripts/build.sh, on ADDON_ENV=local, builds in Vite development mode (import.meta.env.MODE === 'development' so the frontend probes the local backend for storage type and prod Sentry stays off) and forces localhost URLs (VITE_SEND_SERVER_URL/CLIENT_URL, stage OIDC) as exported VITE_* vars, which Vite's loadEnv prioritizes over the .env prod block. Each is overridable inline for different local ports. - README documents the workflow, that CORS already works (the backend auto-allows moz-extension:// origins), and three ways to get past the tls-dev-proxy self-signed cert (OS trust + enterprise roots, a persistent profile exception, or targeting the plain-HTTP backend on :8080). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 7c4066b commit c02b9c5

3 files changed

Lines changed: 123 additions & 3 deletions

File tree

packages/addon/README.md

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,79 @@ pnpm --filter addon sync:builtin
4040
pnpm --filter addon dev:builtin
4141
```
4242

43+
### Pointing the built-in add-on at a local backend
44+
45+
By default a system build bakes in the prod/stage Send hosts from your `.env`. To build the
46+
**same system add-on** but wired to a locally running Send backend + client, use the `:local`
47+
variants:
48+
49+
```sh
50+
# One-shot: system-id build against localhost + rsync into the comm tree
51+
pnpm --filter addon sync:builtin:local
52+
53+
# …or watch mode
54+
pnpm --filter addon dev:builtin:local
55+
56+
# (or just build, without syncing)
57+
pnpm --filter addon build:dev:system:local
58+
```
59+
60+
These set `ADDON_ENV=local`, which makes `scripts/build.sh` build in Vite's **development** mode
61+
(so `import.meta.env.MODE === 'development'` — the frontend then probes the local backend for its
62+
storage type instead of assuming bucket storage, and prod Sentry stays off) and force the localhost
63+
URLs into the bundle:
64+
65+
- `VITE_SEND_SERVER_URL=https://localhost:8088` (also used for `wss://…/api/ws` uploads)
66+
- `VITE_SEND_CLIENT_URL=http://localhost:5173`
67+
- `VITE_OIDC_ROOT_URL=https://auth-stage.tb.pro/realms/tbpro/` (no local Keycloak needed)
68+
69+
They're exported as `VITE_*` process env vars, which Vite's `loadEnv` prioritizes over `.env`, so
70+
they win over the prod block your `.env` ends with. Override any of them inline if your local ports
71+
differ, e.g. `VITE_SEND_SERVER_URL=https://localhost:9000 pnpm --filter addon build:dev:system:local`.
72+
73+
**CORS is already handled** — the Send backend auto-allows any `moz-extension://` origin
74+
(`packages/send/backend/src/origins.ts`), and the built-in/system add-on still runs under a
75+
`moz-extension://` origin, so its requests pass CORS with no add-on-side change. Just make sure the
76+
local backend has `SEND_BACKEND_CORS_ORIGINS` set (it throws on startup otherwise) and including
77+
`http://localhost:5173`.
78+
79+
**The self-signed cert is the real gotcha.** The Send dev stack serves `https://localhost:8088`
80+
through the tls-dev-proxy (`packages/send/backend/tls-dev-proxy/`) using a self-signed cert.
81+
Thunderbird's extension `fetch` can't skip cert validation and gets no interactive prompt at
82+
startup, so an untrusted cert fails the TLS handshake — and Gecko reports that as
83+
`CORS request did not succeed` with `Status code: (null)` (a misleading message: it is *not* a CORS
84+
rejection, which would carry a real status and an "Access-Control-Allow-Origin missing" error). Pick
85+
one of these:
86+
87+
- **Trust the cert at the OS level (recommended — survives `--temp-profile`, keeps `wss` uploads
88+
working).** Import the proxy cert into the macOS keychain and enable Gecko's enterprise-roots
89+
support per run:
90+
91+
```sh
92+
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain \
93+
packages/send/backend/tls-dev-proxy/certs/localhost.crt
94+
./mach run --temp-profile --setpref security.enterprise_roots.enabled=true
95+
```
96+
97+
`enterprise_roots` makes Gecko read the macOS keychain; passing it as a `--setpref` means it
98+
applies even to a fresh temp profile.
99+
100+
- **Add a permanent cert exception in a persistent profile.** Thunderbird → Settings → Privacy &
101+
Security → Certificates → Manage Certificates → *Servers* → Add Exception → `https://localhost:8088`.
102+
Downside: a `--temp-profile` run discards it, which fights the stale-cache advice below — use a
103+
named profile (`./mach run -P <profile>`) instead.
104+
105+
- **Skip TLS entirely (fastest; breaks `wss` uploads).** Point the add-on at the plain-HTTP backend
106+
on `:8080`. `http://localhost` is a trustworthy secure context in Gecko, so there's no cert and no
107+
mixed-content block, and it works with `--temp-profile`:
108+
109+
```sh
110+
VITE_SEND_SERVER_URL=http://localhost:8080 pnpm --filter addon build:dev:system:local
111+
```
112+
113+
Caveat: the file-upload WebSocket is hardcoded to `wss://` (`send/frontend/src/lib/helpers.ts`), so
114+
uploads fail over plain http; trpc / auth / storage-type / dashboard all work.
115+
43116
Then, in the comm tree, repackage and run:
44117

45118
```sh

packages/addon/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,22 @@
1212
"build": "NODE_ENV=production ./scripts/build.sh",
1313
"build:dev": "./scripts/build.sh",
1414
"build:dev:system": "ADDON_VARIANT=system ./scripts/build.sh",
15+
"build:dev:system:local": "ADDON_VARIANT=system ADDON_ENV=local ./scripts/build.sh",
1516
"build:dev:watch": "nodemon -e vue,ts,js,json --ignore 'dist*/' --ignore manifest.json --watch './{src,public}/**/*' --watch '../send/frontend/src/**/*' --exec 'pnpm build:dev'",
1617
"build:watch": "nodemon -e vue,ts,js,json --watch './{src,public}/**/*' --exec 'pnpm build'",
1718
"ci:validate": "pnpm lint:all; pnpm prettier:all; pnpm run test",
1819
"deploy-xpi": "bun run ./scripts/deploy_xpi.ts",
1920
"dev": "vite --host",
2021
"dev:builtin": "nodemon -e vue,ts,js,json --ignore 'dist*/' --ignore manifest.json --watch './{src,public}/**/*' --watch '../send/frontend/src/**/*' --exec 'pnpm sync:builtin'",
22+
"dev:builtin:local": "nodemon -e vue,ts,js,json --ignore 'dist*/' --ignore manifest.json --watch './{src,public}/**/*' --watch '../send/frontend/src/**/*' --exec 'pnpm sync:builtin:local'",
2123
"lint:all": "pnpx eslint 'src/**/*.{ts,vue,js}' 'public/api/**/*.js' --fix",
2224
"prettier:all": "prettier --write .",
2325
"preview": "vite preview",
2426
"release": "./scripts/release.sh",
2527
"setup": "./scripts/setup.sh",
2628
"sort-package-json": "sort-package-json",
2729
"sync:builtin": "pnpm build:dev:system && ./scripts/sync-to-builtin.sh",
30+
"sync:builtin:local": "pnpm build:dev:system:local && ./scripts/sync-to-builtin.sh",
2831
"test": "VITE_TESTING=true vitest run --silent",
2932
"test:watch": "VITE_TESTING=true vitest --watch",
3033
"test-debug": "VITE_TESTING=true vitest --inspect-brk --single-thread",

packages/addon/scripts/build.sh

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,50 @@ else
77
echo 'Starting development build 🐣'
88
fi
99

10+
# Optional Vite mode forwarded to every `vite build`. Empty by default so the
11+
# existing builds keep running in Vite's default production mode.
12+
MODE_ARG=""
13+
14+
### Local backend target (ADDON_ENV=local, via build:dev:system:local).
15+
### Points the bundle at a locally running Send backend + client instead of the
16+
### prod/stage hosts baked into the developer's .env.
17+
if [ "$ADDON_ENV" = "local" ]; then
18+
echo 'Targeting local backend (localhost) 🏠'
19+
### Vite `build` defaults to production mode regardless of NODE_ENV. Force
20+
### development so import.meta.env.MODE === 'development': that makes the
21+
### frontend probe the local backend for its storage type (api.ts), sets
22+
### IS_DEV, and keeps prod Sentry off (sentry.ts). Production mode would
23+
### instead assume bucket storage and skip that probe.
24+
MODE_ARG="--mode development"
25+
26+
### Force localhost URLs. Exported so Vite's loadEnv reads them from
27+
### process.env, where VITE_* vars take precedence over the .env file — whose
28+
### prod block (send.tb.pro / send-backend.tb.pro) would otherwise win via
29+
### last-key-wins. Assigned with := so a developer can still override any of
30+
### them inline (e.g. a different local port) before invoking the build.
31+
: "${VITE_SEND_SERVER_URL:=https://localhost:8088}"
32+
: "${VITE_SEND_CLIENT_URL:=http://localhost:5173}"
33+
### No local Keycloak: authenticate against the stage identity provider.
34+
: "${VITE_OIDC_CLIENT_ID:=desktop}"
35+
: "${VITE_OIDC_ROOT_URL:=https://auth-stage.tb.pro/realms/tbpro/}"
36+
### Keep local builds out of the shared Sentry / PostHog projects.
37+
: "${VITE_SENTRY_DSN:=}"
38+
: "${VITE_POSTHOG_PROJECT_KEY:=}"
39+
export VITE_SEND_SERVER_URL VITE_SEND_CLIENT_URL \
40+
VITE_OIDC_CLIENT_ID VITE_OIDC_ROOT_URL \
41+
VITE_SENTRY_DSN VITE_POSTHOG_PROJECT_KEY
42+
43+
echo " VITE_SEND_SERVER_URL=$VITE_SEND_SERVER_URL"
44+
echo " VITE_SEND_CLIENT_URL=$VITE_SEND_CLIENT_URL"
45+
echo " VITE_OIDC_ROOT_URL=$VITE_OIDC_ROOT_URL"
46+
### CORS: the Send backend auto-allows any moz-extension:// origin
47+
### (origins.ts), and the built-in/system add-on still runs under a
48+
### moz-extension:// origin, so its requests pass CORS unchanged. The local
49+
### backend must still have SEND_BACKEND_CORS_ORIGINS set (it throws
50+
### otherwise) and include $VITE_SEND_CLIENT_URL, and Thunderbird must trust
51+
### the self-signed https://localhost cert (used for fetch + wss uploads).
52+
fi
53+
1054
# Get version from package.json and replace dots with hyphens
1155
VERSION=$(jq -r .version < package.json | sed 's/\./-/g')
1256

@@ -36,7 +80,7 @@ node scripts/subset-fonts.mjs
3680
echo "================================================================"
3781
echo "=============== extension UI ==================================="
3882
### Extension UI
39-
vite build --config vite.config.extension.js
83+
vite build --config vite.config.extension.js $MODE_ARG
4084
cp -R dist/extension/assets/* dist/assets/
4185
cp -R dist/extension/*.* dist/
4286
if [ -d dist/extension/chunks ]; then
@@ -48,7 +92,7 @@ rm -rf dist/extension
4892
echo "================================================================"
4993
echo "=============== management page================================="
5094
### Management page, commenting out for now
51-
vite build --config vite.config.management.js
95+
vite build --config vite.config.management.js $MODE_ARG
5296
cp -R dist/pages/assets/* dist/assets/
5397
cp -R dist/pages/*.* dist/
5498
if [ -d dist/pages/chunks ]; then
@@ -100,7 +144,7 @@ find dist/assets -name '*.css' -exec perl -pi -e \
100144
echo "================================================================"
101145
echo "=============== background.js =================================="
102146
### Build `background.js` as a library
103-
vite build --config vite.config.background.js
147+
vite build --config vite.config.background.js $MODE_ARG
104148
cp -R dist/background/* dist/
105149
# cp -R dist/background/*.map dist/f
106150
# cp -R dist/background/manifest.json dist/

0 commit comments

Comments
 (0)