Skip to content

Commit 36440ef

Browse files
committed
Added some stuff
1 parent 55e0271 commit 36440ef

8 files changed

Lines changed: 332 additions & 9 deletions

File tree

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,13 @@ If `WEB2API_RECIPE_CATALOG_SOURCE` is unset, Web2API uses the official remote re
159159
`https://github.com/Endogen/web2api-recipes.git`.
160160
`recipes update` works only for recipes tracked in the manifest.
161161

162+
Catalog entries can include optional setup hints:
163+
- `requires_env`: list of required environment variable names (e.g. `["BIRD_AUTH_TOKEN", "BIRD_CT0"]`)
164+
- `docs_url` (or `readme_url`): URL shown in CLI/UI as setup documentation
165+
166+
If `docs_url` is omitted and the recipe source resolves to GitHub, Web2API automatically
167+
links to `<repo>/blob/<ref-or-HEAD>/<subdir>/README.md`.
168+
162169
Recipes installed from untrusted sources (for example git URLs) are blocked from executing
163170
install/healthcheck commands unless `--allow-untrusted` is passed.
164171

tests/integration/test_api.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,16 @@ async def test_recipe_management_api_lifecycle(
371371
) -> None:
372372
recipes_dir = tmp_path / "active-recipes"
373373
catalog_root = tmp_path / "catalog-src"
374-
_write_recipe(catalog_root / "recipes", "gamma")
374+
missing_env = "WEB2API_TEST_GAMMA_TOKEN_UNLIKELY"
375+
monkeypatch.delenv(missing_env, raising=False)
376+
_write_recipe(
377+
catalog_root / "recipes",
378+
"gamma",
379+
plugin={
380+
"version": "1.0.0",
381+
"requires_env": [missing_env],
382+
},
383+
)
375384

376385
catalog_file = catalog_root / "catalog.yaml"
377386
catalog_file.parent.mkdir(parents=True, exist_ok=True)
@@ -383,6 +392,8 @@ async def test_recipe_management_api_lifecycle(
383392
"source": "./recipes/gamma",
384393
"trusted": True,
385394
"description": "Gamma recipe",
395+
"docs_url": "https://example.com/gamma/readme",
396+
"requires_env": [missing_env],
386397
}
387398
}
388399
}
@@ -406,6 +417,9 @@ async def test_recipe_management_api_lifecycle(
406417
assert payload_before["catalog_error"] is None
407418
assert payload_before["catalog"][0]["name"] == "gamma"
408419
assert payload_before["catalog"][0]["installed"] is False
420+
assert payload_before["catalog"][0]["docs_url"] == "https://example.com/gamma/readme"
421+
assert payload_before["catalog"][0]["requires_env"] == [missing_env]
422+
assert payload_before["catalog"][0]["plugin"] is None
409423

410424
install_resp = await client.post("/api/recipes/manage/install/gamma")
411425
assert install_resp.status_code == 200
@@ -426,6 +440,10 @@ async def test_recipe_management_api_lifecycle(
426440
)
427441
assert gamma_catalog["installed"] is True
428442
assert gamma_catalog["enabled"] is False
443+
assert gamma_catalog["docs_url"] == "https://example.com/gamma/readme"
444+
assert gamma_catalog["requires_env"] == [missing_env]
445+
assert gamma_catalog["plugin"] is not None
446+
assert gamma_catalog["plugin"]["status"]["checks"]["env"]["missing"] == [missing_env]
429447

430448
enable_resp = await client.post("/api/recipes/manage/enable/gamma")
431449
assert enable_resp.status_code == 200

tests/unit/test_cli.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,8 @@ def test_recipes_catalog_add_installs_from_catalog(
312312
source_subdir=None,
313313
description=None,
314314
trusted=True,
315+
docs_url="https://example.com/demo/readme",
316+
requires_env=["DEMO_TOKEN"],
315317
)
316318
},
317319
)
@@ -333,3 +335,38 @@ def _fake_add_recipe_from_source(**kwargs):
333335

334336
assert result.exit_code == 0
335337
assert called["record_source_type"] == "catalog"
338+
339+
340+
def test_recipes_catalog_list_prints_docs_and_requires_env(
341+
monkeypatch: pytest.MonkeyPatch,
342+
tmp_path: Path,
343+
) -> None:
344+
monkeypatch.setattr(
345+
"web2api.cli.resolve_catalog_recipes",
346+
lambda **kwargs: {
347+
"x": CatalogRecipeSpec(
348+
name="x",
349+
slug="x",
350+
source="https://github.com/acme/web2api-recipes.git",
351+
source_ref="main",
352+
source_subdir="recipes/x",
353+
description="X recipe",
354+
trusted=True,
355+
docs_url="https://github.com/acme/web2api-recipes/blob/main/recipes/x/README.md",
356+
requires_env=["BIRD_AUTH_TOKEN", "BIRD_CT0"],
357+
)
358+
},
359+
)
360+
361+
catalog_file = tmp_path / "catalog.yaml"
362+
catalog_file.write_text("recipes: {}\n", encoding="utf-8")
363+
364+
runner = CliRunner()
365+
result = runner.invoke(
366+
cli.app,
367+
["recipes", "catalog", "list", "--catalog-source", str(catalog_file)],
368+
)
369+
370+
assert result.exit_code == 0
371+
assert "requires env: BIRD_AUTH_TOKEN, BIRD_CT0" in result.output
372+
assert "docs: https://github.com/acme/web2api-recipes/blob/main/recipes/x/README.md" in result.output

tests/unit/test_recipe_manager.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,8 @@ def test_load_catalog_reads_recipe_entries(tmp_path: Path) -> None:
186186
"source": "./demo",
187187
"trusted": True,
188188
"description": "demo plugin",
189+
"docs_url": "https://example.com/demo/readme",
190+
"requires_env": ["DEMO_TOKEN"],
189191
}
190192
}
191193
}
@@ -197,6 +199,8 @@ def test_load_catalog_reads_recipe_entries(tmp_path: Path) -> None:
197199
assert "demo" in catalog
198200
assert catalog["demo"]["source"] == "./demo"
199201
assert catalog["demo"]["trusted"] is True
202+
assert catalog["demo"]["docs_url"] == "https://example.com/demo/readme"
203+
assert catalog["demo"]["requires_env"] == ["DEMO_TOKEN"]
200204

201205

202206
def test_default_paths_exist(monkeypatch: pytest.MonkeyPatch) -> None:
@@ -219,6 +223,8 @@ def test_resolve_catalog_recipes_from_local_file(tmp_path: Path) -> None:
219223
"demo": {
220224
"source": "./demo-recipe",
221225
"trusted": True,
226+
"docs_url": "https://example.com/demo/setup",
227+
"requires_env": ["DEMO_TOKEN"],
222228
}
223229
}
224230
}
@@ -231,6 +237,30 @@ def test_resolve_catalog_recipes_from_local_file(tmp_path: Path) -> None:
231237
assert specs["demo"].slug == "demo"
232238
assert specs["demo"].source == str(source_recipe.resolve())
233239
assert specs["demo"].trusted is True
240+
assert specs["demo"].docs_url == "https://example.com/demo/setup"
241+
assert specs["demo"].requires_env == ["DEMO_TOKEN"]
242+
243+
244+
def test_resolve_catalog_recipes_derives_github_readme_url(tmp_path: Path) -> None:
245+
catalog_file = tmp_path / "catalog.yaml"
246+
catalog_file.write_text(
247+
yaml.safe_dump(
248+
{
249+
"recipes": {
250+
"x": {
251+
"source": "https://github.com/acme/web2api-recipes.git",
252+
"ref": "main",
253+
"subdir": "recipes/x",
254+
}
255+
}
256+
}
257+
),
258+
encoding="utf-8",
259+
)
260+
261+
specs = resolve_catalog_recipes(catalog_source=str(catalog_file))
262+
assert specs["x"].docs_url == "https://github.com/acme/web2api-recipes/blob/main/recipes/x/README.md"
263+
assert specs["x"].requires_env == []
234264

235265

236266
def test_resolve_catalog_recipes_sparse_checkout_for_remote_catalog(
@@ -271,6 +301,8 @@ def _fake_run(command, check: bool, text: bool, **kwargs): # noqa: ANN001
271301
assert "demo" in specs
272302
assert specs["demo"].source == "https://example.com/catalog.git"
273303
assert specs["demo"].source_subdir == "recipes/demo"
304+
assert specs["demo"].docs_url is None
305+
assert specs["demo"].requires_env == []
274306

275307
assert any(
276308
command[:9]

web2api/cli.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,8 @@ def recipes_catalog_list(
694694
"subdir": spec.source_subdir,
695695
"description": spec.description,
696696
"trusted": spec.trusted,
697+
"docs_url": spec.docs_url,
698+
"requires_env": spec.requires_env,
697699
}
698700
for name, spec in sorted(catalog.items())
699701
}
@@ -717,6 +719,10 @@ def recipes_catalog_list(
717719
)
718720
if spec.description:
719721
typer.echo(f" {spec.description}")
722+
if spec.requires_env:
723+
typer.echo(f" requires env: {', '.join(spec.requires_env)}")
724+
if spec.docs_url:
725+
typer.echo(f" docs: {spec.docs_url}")
720726

721727

722728
@catalog_app.command("add")

web2api/recipe_admin_api.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,12 @@ async def recipes_manage(request: Request) -> JSONResponse:
8787
"source": spec.source,
8888
"source_ref": spec.source_ref,
8989
"source_subdir": spec.source_subdir,
90+
"docs_url": spec.docs_url,
91+
"requires_env": spec.requires_env,
9092
"installed": installed is not None and bool(installed.get("has_recipe")),
9193
"enabled": installed.get("enabled") if installed is not None else None,
9294
"managed": installed.get("managed") if installed is not None else False,
95+
"plugin": installed.get("plugin") if installed is not None else None,
9396
"origin": (
9497
str(installed.get("origin", "unmanaged"))
9598
if installed is not None and bool(installed.get("has_recipe"))

0 commit comments

Comments
 (0)