@simon-says-build/harbormaster 0.11.1 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -0
- package/harbormaster +46 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -137,3 +137,20 @@ stale marker means the service is gone, and clients spawn as before. Without
|
|
|
137
137
|
this, the broker belonged to whichever client happened to start it: on
|
|
138
138
|
simon-linux-1 that was a CI job, whose orphan cleanup killed the broker and
|
|
139
139
|
every emulator it ran for other jobs mid-suite. `python3 test/managed.py`.
|
|
140
|
+
|
|
141
|
+
## A project's own suite env (0.12.0)
|
|
142
|
+
|
|
143
|
+
`--fn-env` is per caller, so a setting every suite of a project needs had to
|
|
144
|
+
be repeated by every caller (session tests, e2e, agent runs), and the one that
|
|
145
|
+
forgot ran against production. A project now names it once, in
|
|
146
|
+
`harbormaster.env` next to its `firebase.json`:
|
|
147
|
+
|
|
148
|
+
# every emulator suite of this project runs under the TEST service account
|
|
149
|
+
GOOGLE_APPLICATION_CREDENTIALS=./impulse-test-bq.json
|
|
150
|
+
|
|
151
|
+
`KEY=VALUE` lines, `#` comments. A value starting with `./` or `../` is made
|
|
152
|
+
absolute against the project, since the emulator runs functions (and
|
|
153
|
+
extensions) in other directories. `acquire`: on top of the daemon's scrubbed
|
|
154
|
+
env, under the caller's explicit `--fn-env`. `exec`: on top of the caller's
|
|
155
|
+
ambient env, so a key exported in some shell can't leak into the suite.
|
|
156
|
+
`python3 test/project_env.py`.
|
package/harbormaster
CHANGED
|
@@ -84,7 +84,7 @@ import uuid
|
|
|
84
84
|
from datetime import datetime, timezone
|
|
85
85
|
from pathlib import Path
|
|
86
86
|
|
|
87
|
-
VERSION = "0.
|
|
87
|
+
VERSION = "0.12.0"
|
|
88
88
|
|
|
89
89
|
# Overridable for tests / parallel installs; default is the shared user daemon.
|
|
90
90
|
# The dir keeps the old "firebase-ports" name: pre-rename binaries still installed
|
|
@@ -699,7 +699,7 @@ class Broker:
|
|
|
699
699
|
# daemon's env — the functions emulator runtime inherits it, which is
|
|
700
700
|
# the only way a CALLER can set per-suite env (e.g. LLM_CACHE_DIR)
|
|
701
701
|
# when the daemon, not the caller, spawns the emulator.
|
|
702
|
-
spawn_env = {**os.environ, "TMPDIR": str(suite_tmp)}
|
|
702
|
+
spawn_env = {**os.environ, **project_env(cwd), "TMPDIR": str(suite_tmp)}
|
|
703
703
|
if extra_env:
|
|
704
704
|
spawn_env.update({str(k): str(v) for k, v in dict(extra_env).items()})
|
|
705
705
|
with open(log, "w") as lf:
|
|
@@ -1333,6 +1333,46 @@ def _daemon_version():
|
|
|
1333
1333
|
return None
|
|
1334
1334
|
|
|
1335
1335
|
|
|
1336
|
+
# A PROJECT'S OWN SUITE ENV: <cwd>/harbormaster.env.
|
|
1337
|
+
#
|
|
1338
|
+
# --fn-env is per CALLER, so a setting every suite of a project needs (the
|
|
1339
|
+
# credentials its emulator must run under, say) had to be repeated by every
|
|
1340
|
+
# caller — session tests, e2e, agent runs — and the one that forgot ran
|
|
1341
|
+
# against production. impulse-functions (2026-10-16): its BigQuery export
|
|
1342
|
+
# extensions run under the Extensions emulator and stream to whatever project
|
|
1343
|
+
# the ambient credentials can reach; only a test service account keeps that
|
|
1344
|
+
# test-only. So the project names it once, in the repo:
|
|
1345
|
+
#
|
|
1346
|
+
# # harbormaster.env, next to firebase.json
|
|
1347
|
+
# GOOGLE_APPLICATION_CREDENTIALS=./impulse-test-bq.json
|
|
1348
|
+
#
|
|
1349
|
+
# KEY=VALUE lines; blank lines and `#` comments skipped; a value starting
|
|
1350
|
+
# with ./ or ../ is a path relative to the project, made absolute (the
|
|
1351
|
+
# emulator's functions run in other directories). acquire: on top of the
|
|
1352
|
+
# daemon's (scrubbed) env, UNDER the caller's explicit --fn-env. exec: on top
|
|
1353
|
+
# of the caller's ambient env, which is not a choice. Pure; missing file = {}.
|
|
1354
|
+
PROJECT_ENV_FILE = "harbormaster.env"
|
|
1355
|
+
|
|
1356
|
+
|
|
1357
|
+
def project_env(cwd):
|
|
1358
|
+
f = Path(cwd) / PROJECT_ENV_FILE
|
|
1359
|
+
if not f.is_file():
|
|
1360
|
+
return {}
|
|
1361
|
+
out = {}
|
|
1362
|
+
for raw in f.read_text().splitlines():
|
|
1363
|
+
line = raw.strip()
|
|
1364
|
+
if not line or line.startswith("#") or "=" not in line:
|
|
1365
|
+
continue
|
|
1366
|
+
k, v = line.split("=", 1)
|
|
1367
|
+
k, v = k.strip(), v.strip()
|
|
1368
|
+
if len(v) >= 2 and v[0] == v[-1] and v[0] in "'\"":
|
|
1369
|
+
v = v[1:-1]
|
|
1370
|
+
if v.startswith("./") or v.startswith("../"):
|
|
1371
|
+
v = str((Path(cwd) / v).resolve())
|
|
1372
|
+
out[k] = v
|
|
1373
|
+
return out
|
|
1374
|
+
|
|
1375
|
+
|
|
1336
1376
|
def _ver_tuple(v):
|
|
1337
1377
|
try:
|
|
1338
1378
|
return tuple(int(x) for x in str(v).split("."))
|
|
@@ -1626,7 +1666,10 @@ def main():
|
|
|
1626
1666
|
if only:
|
|
1627
1667
|
run += ["--only", ",".join(only)]
|
|
1628
1668
|
run.append(script)
|
|
1629
|
-
|
|
1669
|
+
# The project's env beats the caller's AMBIENT env here: a key
|
|
1670
|
+
# exported in some shell for a one-off script is exactly what
|
|
1671
|
+
# harbormaster.env exists to keep out of the suite (project_env).
|
|
1672
|
+
rc = subprocess.call(run, cwd=cwd, env={**os.environ, **project_env(cwd)})
|
|
1630
1673
|
finally:
|
|
1631
1674
|
try:
|
|
1632
1675
|
_call("POST", "/release", {"leaseId": lid})
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@simon-says-build/harbormaster",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "A localhost daemon that leases each caller its own port block — isolated Firebase emulator sandboxes, Metro/Expo ports, private iOS simulators — reaped when the caller exits. No port collisions, no shared instances, no pkill.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"harbormaster": "harbormaster",
|