hyperloop 0.8.0__tar.gz → 0.9.0__tar.gz
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.
- {hyperloop-0.8.0 → hyperloop-0.9.0}/CHANGELOG.md +11 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/PKG-INFO +1 -1
- {hyperloop-0.8.0 → hyperloop-0.9.0}/pyproject.toml +2 -1
- {hyperloop-0.8.0 → hyperloop-0.9.0}/src/hyperloop/adapters/matrix_setup.py +143 -54
- {hyperloop-0.8.0 → hyperloop-0.9.0}/src/hyperloop/config.py +3 -3
- {hyperloop-0.8.0 → hyperloop-0.9.0}/tests/fakes/probe.py +1 -1
- {hyperloop-0.8.0 → hyperloop-0.9.0}/tests/test_compose.py +1 -1
- {hyperloop-0.8.0 → hyperloop-0.9.0}/uv.lock +1 -1
- {hyperloop-0.8.0 → hyperloop-0.9.0}/.github/workflows/ci.yaml +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/.github/workflows/release.yaml +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/.gitignore +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/.pre-commit-config.yaml +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/.python-version +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/CLAUDE.md +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/LICENSE +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/README.md +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/base/implementer.yaml +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/base/kustomization.yaml +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/base/pm.yaml +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/base/process-improver.yaml +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/base/process.yaml +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/base/rebase-resolver.yaml +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/base/verifier.yaml +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/specs/observability.md +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/specs/prompts/checklist.md +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/specs/prompts/checks/check_result_file.sh +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/specs/prompts/rules.md +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/specs/spec.md +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/specs/tasks/task-001.md +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/specs/tasks/task-002.md +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/specs/tasks/task-003.md +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/specs/tasks/task-004.md +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/specs/tasks/task-005.md +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/specs/tasks/task-006.md +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/src/hyperloop/__init__.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/src/hyperloop/__main__.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/src/hyperloop/adapters/__init__.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/src/hyperloop/adapters/git_state.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/src/hyperloop/adapters/local.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/src/hyperloop/adapters/matrix_probe.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/src/hyperloop/adapters/probe.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/src/hyperloop/adapters/serial.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/src/hyperloop/adapters/structlog_probe.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/src/hyperloop/cli.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/src/hyperloop/compose.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/src/hyperloop/domain/__init__.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/src/hyperloop/domain/decide.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/src/hyperloop/domain/deps.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/src/hyperloop/domain/model.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/src/hyperloop/domain/pipeline.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/src/hyperloop/logging.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/src/hyperloop/loop.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/src/hyperloop/ports/__init__.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/src/hyperloop/ports/pr.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/src/hyperloop/ports/probe.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/src/hyperloop/ports/runtime.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/src/hyperloop/ports/serial.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/src/hyperloop/ports/state.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/src/hyperloop/pr.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/tests/__init__.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/tests/fakes/__init__.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/tests/fakes/pr.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/tests/fakes/runtime.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/tests/fakes/serial.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/tests/fakes/state.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/tests/test_cli.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/tests/test_config.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/tests/test_decide.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/tests/test_deps.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/tests/test_e2e.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/tests/test_fakes.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/tests/test_git_state.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/tests/test_local_runtime.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/tests/test_loop.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/tests/test_matrix_probe.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/tests/test_model.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/tests/test_pipeline.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/tests/test_pr.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/tests/test_probe.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/tests/test_serial_agents.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/tests/test_smoke.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/tests/test_state_contract.py +0 -0
- {hyperloop-0.8.0 → hyperloop-0.9.0}/tests/test_structlog_probe.py +0 -0
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- version list -->
|
|
4
4
|
|
|
5
|
+
## v0.9.0 (2026-04-15)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
## v0.8.1 (2026-04-15)
|
|
9
|
+
|
|
10
|
+
### Bug Fixes
|
|
11
|
+
|
|
12
|
+
- **matrix**: Auto-create room independently of registration
|
|
13
|
+
([`cb4c0a6`](https://github.com/jsell-rh/hyperloop/commit/cb4c0a665a018a6739ff0fb70e333e7f69202e54))
|
|
14
|
+
|
|
15
|
+
|
|
5
16
|
## v0.8.0 (2026-04-15)
|
|
6
17
|
|
|
7
18
|
### Features
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "hyperloop"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.9.0"
|
|
4
4
|
description = "Orchestrator that walks tasks through composable process pipelines using AI agents"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.12"
|
|
@@ -57,6 +57,7 @@ ignore = []
|
|
|
57
57
|
pythonVersion = "3.12"
|
|
58
58
|
typeCheckingMode = "strict"
|
|
59
59
|
reportMissingTypeStubs = false
|
|
60
|
+
reportPrivateUsage = false
|
|
60
61
|
|
|
61
62
|
[tool.pytest.ini_options]
|
|
62
63
|
testpaths = ["tests"]
|
|
@@ -49,6 +49,21 @@ def _load_cache(repo_path: Path, homeserver: str) -> dict[str, str] | None:
|
|
|
49
49
|
return None
|
|
50
50
|
|
|
51
51
|
|
|
52
|
+
def _ensure_gitignored(repo_path: Path) -> None:
|
|
53
|
+
"""Ensure .hyperloop/ is in the target repo's .gitignore."""
|
|
54
|
+
gitignore = repo_path / ".gitignore"
|
|
55
|
+
entry = ".hyperloop/"
|
|
56
|
+
if gitignore.is_file():
|
|
57
|
+
content = gitignore.read_text()
|
|
58
|
+
if entry in content.splitlines():
|
|
59
|
+
return
|
|
60
|
+
if not content.endswith("\n"):
|
|
61
|
+
content += "\n"
|
|
62
|
+
gitignore.write_text(content + entry + "\n")
|
|
63
|
+
else:
|
|
64
|
+
gitignore.write_text(entry + "\n")
|
|
65
|
+
|
|
66
|
+
|
|
52
67
|
def _save_cache(
|
|
53
68
|
repo_path: Path,
|
|
54
69
|
*,
|
|
@@ -59,6 +74,7 @@ def _save_cache(
|
|
|
59
74
|
password: str,
|
|
60
75
|
) -> None:
|
|
61
76
|
"""Persist Matrix credentials to the cache file."""
|
|
77
|
+
_ensure_gitignored(repo_path)
|
|
62
78
|
cache_path = repo_path / _CACHE_FILE
|
|
63
79
|
cache_path.parent.mkdir(parents=True, exist_ok=True)
|
|
64
80
|
cache_path.write_text(
|
|
@@ -166,15 +182,18 @@ def _create_room(
|
|
|
166
182
|
homeserver: str,
|
|
167
183
|
access_token: str,
|
|
168
184
|
room_name: str,
|
|
185
|
+
invite_user: str = "",
|
|
169
186
|
) -> str:
|
|
170
|
-
"""Create a private Matrix room. Returns the room_id."""
|
|
187
|
+
"""Create a private Matrix room and optionally invite a user. Returns the room_id."""
|
|
171
188
|
url = f"{homeserver}/_matrix/client/v3/createRoom"
|
|
172
|
-
body = {
|
|
189
|
+
body: dict[str, object] = {
|
|
173
190
|
"name": room_name,
|
|
174
191
|
"topic": "hyperloop orchestrator notifications",
|
|
175
192
|
"visibility": "private",
|
|
176
193
|
"preset": "private_chat",
|
|
177
194
|
}
|
|
195
|
+
if invite_user:
|
|
196
|
+
body["invite"] = [invite_user]
|
|
178
197
|
|
|
179
198
|
resp = client.post(
|
|
180
199
|
url,
|
|
@@ -197,10 +216,15 @@ def ensure_matrix_ready(
|
|
|
197
216
|
) -> tuple[str, str]:
|
|
198
217
|
"""Ensure Matrix credentials and room are available.
|
|
199
218
|
|
|
200
|
-
|
|
201
|
-
1. Explicit ``token_env``
|
|
202
|
-
2.
|
|
203
|
-
|
|
219
|
+
**Access token** resolution:
|
|
220
|
+
1. Explicit ``token_env`` env var → use directly.
|
|
221
|
+
2. ``registration_token_env`` → register a fresh disposable bot
|
|
222
|
+
(``hyperloop-{repo}-{random}``), deactivate the previous one.
|
|
223
|
+
|
|
224
|
+
**Room ID** resolution:
|
|
225
|
+
1. Explicit ``room_id`` in config.
|
|
226
|
+
2. Cached room_id from ``.hyperloop/matrix-state.json``.
|
|
227
|
+
3. Auto-create via Matrix API (invites ``invite_user``).
|
|
204
228
|
|
|
205
229
|
Returns:
|
|
206
230
|
(access_token, room_id) tuple. Either or both may be empty string
|
|
@@ -209,82 +233,147 @@ def ensure_matrix_ready(
|
|
|
209
233
|
import os
|
|
210
234
|
|
|
211
235
|
homeserver = config.homeserver.rstrip("/")
|
|
236
|
+
cache = _load_cache(repo_path, homeserver)
|
|
212
237
|
|
|
213
|
-
#
|
|
238
|
+
# --- Resolve access token ---
|
|
214
239
|
explicit_token = os.environ.get(config.token_env) if config.token_env else ""
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
if explicit_token and explicit_room:
|
|
218
|
-
return explicit_token, explicit_room
|
|
240
|
+
access_token = explicit_token
|
|
219
241
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
cached_token = cache.get("access_token", "")
|
|
224
|
-
cached_room = cache.get("room_id", "")
|
|
225
|
-
# Allow explicit overrides to supplement cache
|
|
226
|
-
token = explicit_token or cached_token
|
|
227
|
-
room_id = explicit_room or cached_room
|
|
228
|
-
if token and room_id:
|
|
229
|
-
return token, room_id
|
|
230
|
-
|
|
231
|
-
# 3. Auto-setup via registration_token from env
|
|
232
|
-
registration_token = (
|
|
233
|
-
os.environ.get(config.registration_token_env) if config.registration_token_env else ""
|
|
234
|
-
)
|
|
235
|
-
if not registration_token:
|
|
236
|
-
_log.warning(
|
|
237
|
-
"Matrix: no access token, no cached credentials, and no registration token — skipping"
|
|
242
|
+
if not access_token:
|
|
243
|
+
registration_token = (
|
|
244
|
+
os.environ.get(config.registration_token_env) if config.registration_token_env else ""
|
|
238
245
|
)
|
|
246
|
+
if registration_token:
|
|
247
|
+
try:
|
|
248
|
+
access_token = _register_disposable_bot(
|
|
249
|
+
config, repo_path, homeserver, registration_token, cache
|
|
250
|
+
)
|
|
251
|
+
except Exception:
|
|
252
|
+
_log.exception("Matrix bot registration failed")
|
|
253
|
+
else:
|
|
254
|
+
_log.warning("Matrix: no access token and no registration token — skipping")
|
|
255
|
+
return "", ""
|
|
256
|
+
|
|
257
|
+
if not access_token:
|
|
239
258
|
return "", ""
|
|
240
259
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
260
|
+
# --- Resolve room_id ---
|
|
261
|
+
cached_room = cache.get("room_id", "") if cache else ""
|
|
262
|
+
room_id = config.room_id or cached_room
|
|
263
|
+
|
|
264
|
+
if not room_id:
|
|
265
|
+
try:
|
|
266
|
+
room_id = _auto_create_room(config, repo_path, homeserver, access_token)
|
|
267
|
+
except Exception:
|
|
268
|
+
_log.exception("Matrix room creation failed")
|
|
269
|
+
return access_token, ""
|
|
270
|
+
|
|
271
|
+
# Ensure the bot has joined the room (it's a new user each run)
|
|
272
|
+
if access_token != explicit_token:
|
|
273
|
+
try:
|
|
274
|
+
_join_room(httpx.Client(timeout=10.0), homeserver, access_token, room_id)
|
|
275
|
+
except Exception:
|
|
276
|
+
_log.exception("Matrix room join failed")
|
|
246
277
|
|
|
278
|
+
return access_token, room_id
|
|
247
279
|
|
|
248
|
-
|
|
280
|
+
|
|
281
|
+
def _register_disposable_bot(
|
|
249
282
|
config: MatrixConfig,
|
|
250
283
|
repo_path: Path,
|
|
251
284
|
homeserver: str,
|
|
252
285
|
registration_token: str,
|
|
253
286
|
cache: dict[str, str] | None,
|
|
254
|
-
) ->
|
|
255
|
-
"""Register
|
|
256
|
-
# Derive bot username
|
|
257
|
-
repo_name = repo_path.name
|
|
258
|
-
username = config.bot_username or f"hyperloop-{repo_name}"
|
|
287
|
+
) -> str:
|
|
288
|
+
"""Register a fresh disposable bot user. Returns access_token.
|
|
259
289
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
290
|
+
Each run gets a new identity (``hyperloop-{repo}-{random}``). The
|
|
291
|
+
previous bot is deactivated best-effort. Only the room_id is cached.
|
|
292
|
+
"""
|
|
293
|
+
repo_name = repo_path.name
|
|
294
|
+
suffix = secrets.token_hex(4)
|
|
295
|
+
username = f"hyperloop-{repo_name}-{suffix}"
|
|
296
|
+
password = secrets.token_urlsafe(32)
|
|
264
297
|
|
|
265
298
|
client = httpx.Client(timeout=30.0)
|
|
266
299
|
try:
|
|
267
|
-
#
|
|
300
|
+
# Deactivate previous bot (best-effort)
|
|
301
|
+
prev_token = cache.get("access_token", "") if cache else ""
|
|
302
|
+
if prev_token:
|
|
303
|
+
_deactivate_user(client, homeserver, prev_token)
|
|
304
|
+
|
|
305
|
+
# Register new bot
|
|
268
306
|
user_id, access_token = _register_bot(
|
|
269
307
|
client, homeserver, registration_token, username, password
|
|
270
308
|
)
|
|
271
309
|
|
|
272
|
-
#
|
|
273
|
-
|
|
274
|
-
if not room_id:
|
|
275
|
-
room_id = _create_room(client, homeserver, access_token, f"hyperloop-{repo_name}")
|
|
276
|
-
|
|
277
|
-
# Cache for next run
|
|
310
|
+
# Cache room_id + new bot credentials
|
|
311
|
+
cached_room = cache.get("room_id", "") if cache else ""
|
|
278
312
|
_save_cache(
|
|
279
313
|
repo_path,
|
|
280
314
|
homeserver=homeserver,
|
|
281
315
|
user_id=user_id,
|
|
282
316
|
access_token=access_token,
|
|
283
|
-
room_id=room_id,
|
|
317
|
+
room_id=config.room_id or cached_room,
|
|
284
318
|
password=password,
|
|
285
319
|
)
|
|
286
320
|
|
|
287
|
-
_log.info("Matrix
|
|
288
|
-
return access_token
|
|
321
|
+
_log.info("Matrix bot registered: %s", user_id)
|
|
322
|
+
return access_token
|
|
323
|
+
finally:
|
|
324
|
+
client.close()
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def _deactivate_user(client: httpx.Client, homeserver: str, access_token: str) -> None:
|
|
328
|
+
"""Deactivate the user associated with the given access token. Best-effort."""
|
|
329
|
+
import contextlib
|
|
330
|
+
|
|
331
|
+
url = f"{homeserver}/_matrix/client/v3/account/deactivate"
|
|
332
|
+
with contextlib.suppress(Exception):
|
|
333
|
+
client.post(
|
|
334
|
+
url,
|
|
335
|
+
json={"auth": {"type": "m.login.password"}},
|
|
336
|
+
headers={"Authorization": f"Bearer {access_token}"},
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def _join_room(client: httpx.Client, homeserver: str, access_token: str, room_id: str) -> None:
|
|
341
|
+
"""Join a room by ID."""
|
|
342
|
+
url = f"{homeserver}/_matrix/client/v3/join/{room_id}"
|
|
343
|
+
resp = client.post(url, json={}, headers={"Authorization": f"Bearer {access_token}"})
|
|
344
|
+
resp.raise_for_status()
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
def _auto_create_room(
|
|
348
|
+
config: MatrixConfig,
|
|
349
|
+
repo_path: Path,
|
|
350
|
+
homeserver: str,
|
|
351
|
+
access_token: str,
|
|
352
|
+
) -> str:
|
|
353
|
+
"""Create a room, invite the configured user, and cache room_id. Returns room_id."""
|
|
354
|
+
repo_name = repo_path.name
|
|
355
|
+
client = httpx.Client(timeout=30.0)
|
|
356
|
+
try:
|
|
357
|
+
room_id = _create_room(
|
|
358
|
+
client,
|
|
359
|
+
homeserver,
|
|
360
|
+
access_token,
|
|
361
|
+
f"hyperloop-{repo_name}",
|
|
362
|
+
invite_user=config.invite_user,
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
# Update cache with the new room_id
|
|
366
|
+
cache = _load_cache(repo_path, homeserver) or {}
|
|
367
|
+
_save_cache(
|
|
368
|
+
repo_path,
|
|
369
|
+
homeserver=homeserver,
|
|
370
|
+
user_id=cache.get("user_id", ""),
|
|
371
|
+
access_token=cache.get("access_token", access_token),
|
|
372
|
+
room_id=room_id,
|
|
373
|
+
password=cache.get("password", ""),
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
_log.info("Matrix room created: %s", room_id)
|
|
377
|
+
return room_id
|
|
289
378
|
finally:
|
|
290
379
|
client.close()
|
|
@@ -31,7 +31,7 @@ class MatrixConfig:
|
|
|
31
31
|
token_env: str
|
|
32
32
|
verbose: bool
|
|
33
33
|
registration_token_env: str # env var holding the registration token
|
|
34
|
-
|
|
34
|
+
invite_user: str # Matrix user ID to invite to auto-created rooms
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
@dataclass(frozen=True)
|
|
@@ -144,7 +144,7 @@ def _flatten_yaml(raw: dict[str, object]) -> dict[str, object]:
|
|
|
144
144
|
flat["matrix_token_env"] = mx.get("token_env", "")
|
|
145
145
|
flat["matrix_verbose"] = mx.get("verbose", False)
|
|
146
146
|
flat["matrix_registration_token_env"] = mx.get("registration_token_env", "")
|
|
147
|
-
flat["
|
|
147
|
+
flat["matrix_invite_user"] = mx.get("invite_user", "")
|
|
148
148
|
|
|
149
149
|
return flat
|
|
150
150
|
|
|
@@ -208,7 +208,7 @@ def load_config(
|
|
|
208
208
|
token_env=token_env,
|
|
209
209
|
verbose=bool(values.get("matrix_verbose", False)),
|
|
210
210
|
registration_token_env=registration_token_env,
|
|
211
|
-
|
|
211
|
+
invite_user=str(values.get("matrix_invite_user", "")),
|
|
212
212
|
)
|
|
213
213
|
|
|
214
214
|
obs_cfg = ObservabilityConfig(
|
|
@@ -343,7 +343,7 @@ class TestCheckKustomize:
|
|
|
343
343
|
def test_raises_when_missing(self, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
344
344
|
from hyperloop.compose import check_kustomize_available
|
|
345
345
|
|
|
346
|
-
monkeypatch.setattr(shutil, "which", lambda _name: None)
|
|
346
|
+
monkeypatch.setattr(shutil, "which", lambda _name: None) # type: ignore[arg-type]
|
|
347
347
|
|
|
348
348
|
with pytest.raises(SystemExit, match="kustomize CLI not found"):
|
|
349
349
|
check_kustomize_available()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|