generated from coulomb/repo-seed
Some checks failed
ci / validate-registry (push) Has been cancelled
reuse_surface/forge_host.py: parse/derive/rewrite raw index URLs across Gitea and Forgejo (handles both the legacy /raw/<branch>/... form and the canonical /raw/branch/<branch>/... form both forges serve without a 303 redirect). migrate_source_host() verifies the new URL resolves via HTTP HEAD before writing -- refuses to point a repo at a host it hasn't actually migrated to. New CLI: reuse-surface federation migrate-host --repo <slug> --to <base-url> [--from <check>] [--dry-run] [--no-verify] [--update-hub]. Inventory: cross-referenced sources.yaml against each repo's actual git origin. 11/61 repos already on Forgejo; found 2 with stale sources.yaml entries (activity-core, state-hub) despite having migrated. Fixed for real: local sources.yaml + production hub registration (hub update), verified against GET /v1/federated post-migration. config-atlas's WP-0017-T06 303 confirmed NOT a host-transition symptom (already diagnosed there as something else). Also fixed two host-agnostic gaps found while inventorying: registry_update.py and maintain_llm.py only recognized .gitea/workflows/, missing repos already on .forgejo/workflows/. Fixed a stale copy-paste example (state-hub's now-wrong old URL) in docs/RegistryFederation.md, and a pre-existing unrelated port typo (8088 vs the real llm-connect default 8080) in tools/README.md and registry/README.md. 17 new pytest cases (tests/test_forge_host.py), 106 total pass. Recomposed federated.yaml post-migration: still 61 capabilities. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
189 lines
7.7 KiB
Python
189 lines
7.7 KiB
Python
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from reuse_surface.forge_host import (
|
|
derive_raw_url,
|
|
forge_base_url,
|
|
migrate_source_host,
|
|
parse_raw_url,
|
|
rewrite_url_host,
|
|
)
|
|
|
|
|
|
def test_parse_raw_url_legacy_form():
|
|
parts = parse_raw_url("https://gitea.coulomb.social/coulomb/activity-core/raw/main/registry/indexes/capabilities.yaml")
|
|
assert parts.scheme == "https"
|
|
assert parts.host == "gitea.coulomb.social"
|
|
assert parts.org == "coulomb"
|
|
assert parts.repo == "activity-core"
|
|
assert parts.branch == "main"
|
|
assert parts.path == "registry/indexes/capabilities.yaml"
|
|
|
|
|
|
def test_parse_raw_url_branch_qualified_form():
|
|
parts = parse_raw_url("https://forgejo.coulomb.social/coulomb/core-hub/raw/branch/main/registry/indexes/capabilities.yaml")
|
|
assert parts.host == "forgejo.coulomb.social"
|
|
assert parts.repo == "core-hub"
|
|
assert parts.branch == "main"
|
|
|
|
|
|
def test_parse_raw_url_rejects_unrecognized():
|
|
with pytest.raises(ValueError):
|
|
parse_raw_url("https://example.com/not/a/raw/url")
|
|
|
|
|
|
def test_derive_raw_url_uses_branch_qualified_form():
|
|
url = derive_raw_url("https://forgejo.coulomb.social", "coulomb", "core-hub")
|
|
assert url == "https://forgejo.coulomb.social/coulomb/core-hub/raw/branch/main/registry/indexes/capabilities.yaml"
|
|
|
|
|
|
def test_derive_raw_url_strips_trailing_slash():
|
|
url = derive_raw_url("https://forgejo.coulomb.social/", "coulomb", "core-hub")
|
|
assert url.startswith("https://forgejo.coulomb.social/coulomb/")
|
|
|
|
|
|
def test_rewrite_url_host_swaps_host_and_normalizes_form():
|
|
old = "https://gitea.coulomb.social/coulomb/activity-core/raw/main/registry/indexes/capabilities.yaml"
|
|
new = rewrite_url_host(old, "https://forgejo.coulomb.social")
|
|
assert new == "https://forgejo.coulomb.social/coulomb/activity-core/raw/branch/main/registry/indexes/capabilities.yaml"
|
|
|
|
|
|
def test_forge_base_url_env_fallback(monkeypatch):
|
|
monkeypatch.setenv("REUSE_SURFACE_FORGE_BASE_URL", "https://forgejo.coulomb.social")
|
|
assert forge_base_url() == "https://forgejo.coulomb.social"
|
|
|
|
|
|
def test_forge_base_url_none_when_unset(monkeypatch):
|
|
monkeypatch.delenv("REUSE_SURFACE_FORGE_BASE_URL", raising=False)
|
|
assert forge_base_url() is None
|
|
|
|
|
|
def test_forge_base_url_explicit_wins(monkeypatch):
|
|
monkeypatch.setenv("REUSE_SURFACE_FORGE_BASE_URL", "https://env-value")
|
|
assert forge_base_url("https://explicit-value") == "https://explicit-value"
|
|
|
|
|
|
def test_migrate_source_host_mutates_matching_entry(monkeypatch):
|
|
monkeypatch.setattr("reuse_surface.forge_host.probe_url", lambda url, **k: (True, 200))
|
|
sources = [
|
|
{"repo": "activity-core", "url": "https://gitea.coulomb.social/coulomb/activity-core/raw/main/registry/indexes/capabilities.yaml"},
|
|
{"repo": "other-repo", "url": "https://gitea.coulomb.social/coulomb/other-repo/raw/main/registry/indexes/capabilities.yaml"},
|
|
]
|
|
report = migrate_source_host(sources, "activity-core", "https://forgejo.coulomb.social")
|
|
assert report["verified"] is True
|
|
assert sources[0]["url"] == "https://forgejo.coulomb.social/coulomb/activity-core/raw/branch/main/registry/indexes/capabilities.yaml"
|
|
# other entries untouched
|
|
assert sources[1]["url"] == "https://gitea.coulomb.social/coulomb/other-repo/raw/main/registry/indexes/capabilities.yaml"
|
|
|
|
|
|
def test_migrate_source_host_repo_not_found():
|
|
sources = [{"repo": "activity-core", "url": "https://gitea.coulomb.social/coulomb/activity-core/raw/main/registry/indexes/capabilities.yaml"}]
|
|
with pytest.raises(ValueError, match="not found"):
|
|
migrate_source_host(sources, "does-not-exist", "https://forgejo.coulomb.social")
|
|
|
|
|
|
def test_migrate_source_host_refuses_unverified_url(monkeypatch):
|
|
monkeypatch.setattr("reuse_surface.forge_host.probe_url", lambda url, **k: (False, 404))
|
|
sources = [{"repo": "activity-core", "url": "https://gitea.coulomb.social/coulomb/activity-core/raw/main/registry/indexes/capabilities.yaml"}]
|
|
original_url = sources[0]["url"]
|
|
with pytest.raises(ValueError, match="did not resolve"):
|
|
migrate_source_host(sources, "activity-core", "https://forgejo.coulomb.social")
|
|
# must not have mutated the entry when verification failed
|
|
assert sources[0]["url"] == original_url
|
|
|
|
|
|
def test_migrate_source_host_skips_verification_when_disabled():
|
|
sources = [{"repo": "activity-core", "url": "https://gitea.coulomb.social/coulomb/activity-core/raw/main/registry/indexes/capabilities.yaml"}]
|
|
report = migrate_source_host(sources, "activity-core", "https://forgejo.coulomb.social", verify=False)
|
|
assert report["verified"] is None
|
|
assert sources[0]["url"].startswith("https://forgejo.coulomb.social/")
|
|
|
|
|
|
def test_cmd_federation_migrate_host_dry_run(tmp_path, monkeypatch):
|
|
from reuse_surface.cli import main
|
|
|
|
sources_path = tmp_path / "sources.yaml"
|
|
sources_path.write_text(
|
|
"version: 1\n"
|
|
"domain: helix_forge\n"
|
|
"collision_policy: warn\n"
|
|
"sources:\n"
|
|
"- repo: activity-core\n"
|
|
" url: https://gitea.coulomb.social/coulomb/activity-core/raw/main/registry/indexes/capabilities.yaml\n"
|
|
" enabled: true\n"
|
|
)
|
|
monkeypatch.setattr("reuse_surface.cli.migrate_source_host", lambda sources, repo, to, verify=True: {
|
|
"repo": repo, "old_url": sources[0]["url"], "new_url": "https://forgejo.coulomb.social/x", "verified": True,
|
|
})
|
|
exit_code = main([
|
|
"federation", "migrate-host",
|
|
"--repo", "activity-core",
|
|
"--to", "https://forgejo.coulomb.social",
|
|
"--sources", str(sources_path),
|
|
"--dry-run",
|
|
])
|
|
assert exit_code == 0
|
|
# dry-run must not write
|
|
assert "gitea.coulomb.social" in sources_path.read_text()
|
|
|
|
|
|
def test_cmd_federation_migrate_host_writes_file(tmp_path, monkeypatch):
|
|
from reuse_surface.cli import main
|
|
|
|
sources_path = tmp_path / "sources.yaml"
|
|
sources_path.write_text(
|
|
"version: 1\n"
|
|
"domain: helix_forge\n"
|
|
"collision_policy: warn\n"
|
|
"sources:\n"
|
|
"- repo: activity-core\n"
|
|
" url: https://gitea.coulomb.social/coulomb/activity-core/raw/main/registry/indexes/capabilities.yaml\n"
|
|
" enabled: true\n"
|
|
)
|
|
monkeypatch.setattr("reuse_surface.forge_host.probe_url", lambda url, **k: (True, 200))
|
|
exit_code = main([
|
|
"federation", "migrate-host",
|
|
"--repo", "activity-core",
|
|
"--to", "https://forgejo.coulomb.social",
|
|
"--sources", str(sources_path),
|
|
])
|
|
assert exit_code == 0
|
|
written = sources_path.read_text()
|
|
assert "forgejo.coulomb.social" in written
|
|
assert "gitea.coulomb.social" not in written
|
|
|
|
|
|
def test_cmd_federation_migrate_host_repo_not_found_errors(tmp_path):
|
|
from reuse_surface.cli import main
|
|
|
|
sources_path = tmp_path / "sources.yaml"
|
|
sources_path.write_text("version: 1\ndomain: helix_forge\ncollision_policy: warn\nsources: []\n")
|
|
exit_code = main([
|
|
"federation", "migrate-host",
|
|
"--repo", "does-not-exist",
|
|
"--to", "https://forgejo.coulomb.social",
|
|
"--sources", str(sources_path),
|
|
"--dry-run",
|
|
])
|
|
assert exit_code == 1
|
|
|
|
|
|
def test_cmd_federation_migrate_host_from_mismatch_errors(tmp_path):
|
|
from reuse_surface.cli import main
|
|
|
|
sources_path = tmp_path / "sources.yaml"
|
|
sources_path.write_text(
|
|
"version: 1\ndomain: helix_forge\ncollision_policy: warn\n"
|
|
"sources:\n- repo: activity-core\n url: https://forgejo.coulomb.social/coulomb/activity-core/raw/branch/main/registry/indexes/capabilities.yaml\n"
|
|
)
|
|
exit_code = main([
|
|
"federation", "migrate-host",
|
|
"--repo", "activity-core",
|
|
"--to", "https://forgejo.coulomb.social",
|
|
"--from", "gitea.coulomb.social",
|
|
"--sources", str(sources_path),
|
|
"--dry-run",
|
|
])
|
|
assert exit_code == 1
|