diff --git a/.dagger/src/edge_proc/main.py b/.dagger/src/edge_proc/main.py index 6c90553..45bdd78 100644 --- a/.dagger/src/edge_proc/main.py +++ b/.dagger/src/edge_proc/main.py @@ -16,10 +16,10 @@ "ghcr.io/astral-sh/uv:0.11.32@sha256:" "df4cae8f3a96d175e2e5f992e597550000edbe78fdc2594d5cd8de1a217f504c" ) -#: The repository as GitHub reports it today; the default keeps existing callers exact. -DEFAULT_REPOSITORY: Final = "hseshadr/edge-proc" -#: The only identities a run may claim: this repository today, and after the org transfer. -ALLOWED_REPOSITORIES: Final = ("hseshadr/edge-proc", "gainratio/edge-proc") +#: The only identities a run may claim: the canonical gainratio owner first, then the +#: pre-transfer identity until the org move finishes. There is deliberately no default: +#: every gate must be handed the run's own `github.repository`. +ALLOWED_REPOSITORIES: Final = ("gainratio/edge-proc", "hseshadr/edge-proc") PROJECT_NAME: Final = "edge-proc" CENTRAL_MODULE_SHA: Final = "a88866232e679b6353d2b75bceb01969be739f67" SOURCE_EXCLUDES: Final = [ @@ -99,21 +99,19 @@ def quality(self) -> dagger.Container: return self._quality(self.source) @function - async def security(self, commit_sha: str, repository: str = DEFAULT_REPOSITORY) -> str: + async def security(self, commit_sha: str, repository: str) -> str: """Run the shared exact-source, workflow, and history guard.""" await self._verified_source(self.source, _allowed_repository(repository), commit_sha) return "EdgeProc shared Dagger security gate passed" @function - def dependency_audit( - self, commit_sha: str, repository: str = DEFAULT_REPOSITORY - ) -> dagger.Container: + def dependency_audit(self, commit_sha: str, repository: str) -> dagger.Container: """Audit the locked graph through the shared Python-package Lego.""" return self._dependency_audit(self.source, _allowed_repository(repository), commit_sha) @function @check - async def ci(self, commit_sha: str, repository: str = DEFAULT_REPOSITORY) -> str: + async def ci(self, commit_sha: str, repository: str) -> str: """Run the canonical exact-source gate sequentially.""" await self._run_ci(self.source, commit_sha, _allowed_repository(repository)) return "EdgeProc canonical Dagger gate passed" @@ -122,7 +120,7 @@ async def ci(self, commit_sha: str, repository: str = DEFAULT_REPOSITORY) -> str @function(cache="never") # type: ignore[call-overload,untyped-decorator] # SDK stub gap async def release_candidate( self, tag: str, commit_sha: str, github_token: dagger.Secret, - repository: str = DEFAULT_REPOSITORY, + repository: str, ) -> dagger.Directory: """Create and reverify one exact attempt-bound Foundation envelope.""" repository = _allowed_repository(repository) @@ -136,7 +134,7 @@ async def release_candidate( @function(cache="never") # type: ignore[call-overload,untyped-decorator] # SDK stub gap async def repin_dependabot( - self, github_token: dagger.Secret, pr_number: int, repository: str = DEFAULT_REPOSITORY + self, github_token: dagger.Secret, pr_number: int, repository: str ) -> str: """Copy uv.lock tool versions into the literals of one Dependabot uv PR.""" repository = _allowed_repository(repository) @@ -155,7 +153,7 @@ async def verify_candidate( commit_sha: str, workflow_run_id: str, run_attempt: int, - repository: str = DEFAULT_REPOSITORY, + repository: str, ) -> dagger.Directory: """Revalidate a closed candidate without source or credentials.""" lineage = _Lineage( @@ -165,9 +163,7 @@ async def verify_candidate( await verified.tag() return verified.envelope() - async def _run_ci( - self, source: dagger.Directory, commit_sha: str, repository: str = DEFAULT_REPOSITORY - ) -> None: + async def _run_ci(self, source: dagger.Directory, commit_sha: str, repository: str) -> None: bound = await self._verified_source(source, repository, commit_sha) await self._product_gate(bound).sync() await self._dependency_audit(bound, repository, commit_sha).sync() diff --git a/.dagger/tests/test_public_contracts.py b/.dagger/tests/test_public_contracts.py index 004f44f..36e6a80 100644 --- a/.dagger/tests/test_public_contracts.py +++ b/.dagger/tests/test_public_contracts.py @@ -18,7 +18,7 @@ ROOT = Path(__file__).resolve().parents[2] CENTRAL_SHA = "a88866232e679b6353d2b75bceb01969be739f67" -REPOSITORY = "hseshadr/edge-proc" +REPOSITORY = "gainratio/edge-proc" PROJECT_NAME = "edge-proc" COMMIT_SHA = "a" * 40 MAX_FUNCTION_LINES = 15 @@ -260,7 +260,7 @@ def test_should_bind_guard_then_run_product_and_closed_audit_on_one_source( edge = _edge(source, events) # When - asyncio.run(edge._run_ci(source, COMMIT_SHA)) + asyncio.run(edge._run_ci(source, COMMIT_SHA, REPOSITORY)) # Then assert events == ["source", "guard", "product", "audit"] @@ -280,7 +280,7 @@ def test_should_query_osv_for_non_pypi_locked_versions( edge = _edge(cast(dagger.Directory, object()), []) # When - edge.dependency_audit(COMMIT_SHA) + edge.dependency_audit(COMMIT_SHA, REPOSITORY) # Then assert package.audit_commands == [list(dagger_module.OSV_AUDIT_COMMAND)] @@ -297,7 +297,7 @@ def test_should_stop_before_product_or_audit_when_shared_guard_rejects( # When / Then with pytest.raises(SharedGuardRejectedError, match="shared guard rejected source"): - asyncio.run(edge._run_ci(source, COMMIT_SHA)) + asyncio.run(edge._run_ci(source, COMMIT_SHA, REPOSITORY)) assert events == ["source"] assert edge.product_source is None assert package.audit_call is None @@ -313,7 +313,7 @@ def test_should_create_and_reverify_one_attempt_bound_closed_candidate( edge = _edge(source, events) # When - result = asyncio.run(edge.release_candidate("v0.4.1", COMMIT_SHA, token)) + result = asyncio.run(edge.release_candidate("v0.4.1", COMMIT_SHA, token, REPOSITORY)) # Then identity = (REPOSITORY, COMMIT_SHA, PROJECT_NAME, CENTRAL_SHA, "6100", 2) @@ -347,7 +347,9 @@ def test_should_reject_manual_tag_mismatch_before_candidate_handoff( # When / Then with pytest.raises(ValueError, match="manual tag differs"): - asyncio.run(edge.release_candidate("v0.4.1", COMMIT_SHA, cast(dagger.Secret, object()))) + asyncio.run( + edge.release_candidate("v0.4.1", COMMIT_SHA, cast(dagger.Secret, object()), REPOSITORY) + ) assert package.verify_call is None @@ -360,7 +362,7 @@ def test_should_verify_candidate_without_source_or_credentials( edge = _edge(cast(dagger.Directory, object()), events) # When - result = asyncio.run(edge.verify_candidate(envelope, COMMIT_SHA, "6100", 2)) + result = asyncio.run(edge.verify_candidate(envelope, COMMIT_SHA, "6100", 2, REPOSITORY)) # Then identity = (REPOSITORY, COMMIT_SHA, PROJECT_NAME, CENTRAL_SHA, "6100", 2) @@ -399,8 +401,8 @@ def test_should_keep_every_adapter_function_within_the_python_quality_contract() assert {name: span for name, span in spans.items() if span > MAX_FUNCTION_LINES} == {} -#: The repository as GitHub reports it today, and after the planned transfer to the org. -ALLOWED = ("hseshadr/edge-proc", "gainratio/edge-proc") +#: The canonical gainratio identity, then the pre-transfer one kept until the org move finishes. +ALLOWED = ("gainratio/edge-proc", "hseshadr/edge-proc") #: A fork, a sibling repository, a look-alike name, a look-alike owner, and nothing. REFUSED = ( @@ -412,12 +414,25 @@ def test_should_keep_every_adapter_function_within_the_python_quality_contract() ) -def test_should_default_to_the_repository_identity_used_today() -> None: +@pytest.mark.parametrize( + "gate", + [ + "ci", + "security", + "dependency_audit", + "release_candidate", + "repin_dependabot", + "verify_candidate", + "_run_ci", + ], +) +def test_should_require_the_runs_repository_with_no_stale_default_owner(gate: str) -> None: # Given / When - parameter = inspect.signature(EdgeProc.ci).parameters["repository"] + parameter = inspect.signature(getattr(EdgeProc, gate)).parameters["repository"] # Then - assert parameter.default == "hseshadr/edge-proc" + assert parameter.default is inspect.Parameter.empty + assert not hasattr(dagger_module, "DEFAULT_REPOSITORY") assert dagger_module.ALLOWED_REPOSITORIES == ALLOWED diff --git a/CITATION.cff b/CITATION.cff index d720e43..af7f2b3 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -12,8 +12,8 @@ authors: - family-names: Seshadri given-names: Harish email: harish.seshadri@gmail.com -repository-code: "https://github.com/hseshadr/edge-proc" -url: "https://github.com/hseshadr/edge-proc" +repository-code: "https://github.com/gainratio/edge-proc" +url: "https://github.com/gainratio/edge-proc" license: MIT version: 0.5.0 keywords: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0ca72c4..118b103 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,7 +10,7 @@ New here? [Getting started for developers](docs/GETTING_STARTED.md) walks you fr clone to a green local build and your first change, with the time each step takes. ```bash -git clone https://github.com/hseshadr/edge-proc.git +git clone https://github.com/gainratio/edge-proc.git cd edge-proc uv sync --all-extras # core + extras + dev tooling uv run poe gate # lint + format-check + typecheck + complexity + test — what CI runs diff --git a/README.md b/README.md index af13361..fef1e7e 100644 --- a/README.md +++ b/README.md @@ -18,9 +18,9 @@ on the device, EdgeProc can also search it there, with no network. **Technical docs:** [Architecture](docs/ARCHITECTURE.md) · [Getting started for developers](docs/GETTING_STARTED.md) · [Full walkthrough with search](docs/QUICKSTART.md) · [Configuration](docs/CONFIGURATION.md) · [Operations and security](docs/OPERATIONS.md) -[![CI](https://github.com/hseshadr/edge-proc/actions/workflows/dagger.yml/badge.svg)](https://github.com/hseshadr/edge-proc/actions/workflows/dagger.yml) +[![CI](https://github.com/gainratio/edge-proc/actions/workflows/dagger.yml/badge.svg)](https://github.com/gainratio/edge-proc/actions/workflows/dagger.yml) [![PyPI](https://img.shields.io/pypi/v/edge-proc)](https://pypi.org/project/edge-proc/) -[![License](https://img.shields.io/github/license/hseshadr/edge-proc)](LICENSE) +[![License](https://img.shields.io/github/license/gainratio/edge-proc)](LICENSE) ## Try it @@ -135,7 +135,7 @@ rather than fetch one. ## How it fits with the related projects -- **[edgeproc-core](https://github.com/hseshadr/edgeproc-core)** (Python, on PyPI) is a +- **[edgeproc-core](https://github.com/gainratio/edgeproc-core)** (Python, on PyPI) is a smaller library underneath this one. EdgeProc uses two things from it: a common interface for vector search indexes, which EdgeProc's FAISS index implements, and the stable error codes, like `bundle.integrity_failed`. It installs automatically with EdgeProc. You would @@ -144,10 +144,10 @@ rather than fetch one. pages) does the checking half of EdgeProc inside a browser tab. It reads the same signed format that `edgeproc publish` writes, so you can publish with this library and load the data in a web page. -- **[edge-reco](https://github.com/hseshadr/edge-reco)** is a demo online store +- **[edge-reco](https://github.com/gainratio/edge-reco)** is a demo online store ([edge-reco.com](https://edge-reco.com)) that runs product search in the shopper's browser. It uses these libraries. -- **[privacy-core](https://github.com/hseshadr/privacy-core)** (published on npm as +- **[privacy-core](https://github.com/gainratio/privacy-core)** (published on npm as `@edgeproc/privacy-core`) is a separate project despite the shared name. It hides card numbers and similar IDs from AI prompts in the browser, and does not use EdgeProc. @@ -196,7 +196,7 @@ This README documents EdgeProc 0.5.0. It needs Python 3.13 or newer and installs ## Develop ```bash -git clone https://github.com/hseshadr/edge-proc.git +git clone https://github.com/gainratio/edge-proc.git cd edge-proc uv sync --all-extras uv run poe gate @@ -229,6 +229,6 @@ setup, a map of the code, a first change, and how to open a pull request. MIT. See [LICENSE](LICENSE). To cite EdgeProc, use [CITATION.cff](CITATION.cff). EdgeProc (also written `edge-proc` and `edgeproc`) lives at -[hseshadr/edge-proc](https://github.com/hseshadr/edge-proc), with a project page at +[gainratio/edge-proc](https://github.com/gainratio/edge-proc), with a project page at [edge-reco.com/edgeproc](https://edge-reco.com/edgeproc). It is not affiliated with any other product or company named "EdgeProc". diff --git a/docs/GETTING_STARTED.md b/docs/GETTING_STARTED.md index 81acded..1618150 100644 --- a/docs/GETTING_STARTED.md +++ b/docs/GETTING_STARTED.md @@ -21,7 +21,7 @@ builds on, installs from PyPI. ## Clone, install, and run it ```bash -git clone https://github.com/hseshadr/edge-proc.git +git clone https://github.com/gainratio/edge-proc.git cd edge-proc uv sync --all-extras ``` diff --git a/docs/QUICKSTART.md b/docs/QUICKSTART.md index 1535dec..9f1e8c2 100644 --- a/docs/QUICKSTART.md +++ b/docs/QUICKSTART.md @@ -27,7 +27,7 @@ Budget more wall-clock on a slow link. The model download happens once; later ru ## 1. Clone and gate ```bash -git clone https://github.com/hseshadr/edge-proc.git +git clone https://github.com/gainratio/edge-proc.git cd edge-proc uv sync --all-extras # core + [localvec] + [bundles] + dev tooling diff --git a/pyproject.toml b/pyproject.toml index 7e4c1c0..154e7e8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -71,9 +71,9 @@ dev = [ edgeproc = "edgeproc.cli:app" [project.urls] -Homepage = "https://github.com/hseshadr/edge-proc" -Repository = "https://github.com/hseshadr/edge-proc" -Issues = "https://github.com/hseshadr/edge-proc/issues" +Homepage = "https://github.com/gainratio/edge-proc" +Repository = "https://github.com/gainratio/edge-proc" +Issues = "https://github.com/gainratio/edge-proc/issues" # edgeproc-core installs from PyPI. Co-developing it locally? Clone it next to # this repo and add a path override (uncomment): diff --git a/tests/test_dagger_release_pipeline.py b/tests/test_dagger_release_pipeline.py index a9d077f..bd2b636 100644 --- a/tests/test_dagger_release_pipeline.py +++ b/tests/test_dagger_release_pipeline.py @@ -166,7 +166,7 @@ def test_should_fetch_only_hosted_identity_fields( monkeypatch.setattr(contract, "urlopen", lambda *_args, **_kwargs: next(responses)) # When - payload = contract.fetch_hosted_payload("hseshadr/edge-proc", SHA, "v1.2.3", "token") + payload = contract.fetch_hosted_payload("gainratio/edge-proc", SHA, "v1.2.3", "token") # Then contract.validate_hosted_eligibility(payload, SHA) diff --git a/tests/test_repin_locked_tools.py b/tests/test_repin_locked_tools.py index d92f629..dd2c2db 100644 --- a/tests/test_repin_locked_tools.py +++ b/tests/test_repin_locked_tools.py @@ -142,7 +142,7 @@ def test_should_build_a_head_bound_commit_request(tmp_path: Path) -> None: # When request = tool.commit_request( - root, "hseshadr/edge-proc", "dependabot/uv/x", "c" * 40, (TEST_SITE,) + root, "gainratio/edge-proc", "dependabot/uv/x", "c" * 40, (TEST_SITE,) ) # Then @@ -161,7 +161,7 @@ def test_should_refuse_a_head_that_is_not_a_full_commit_sha(tmp_path: Path) -> N # When / Then with pytest.raises(tool.RepinError, match="commit sha"): - tool.commit_request(root, "hseshadr/edge-proc", "b", "HEAD", (TEST_SITE,)) + tool.commit_request(root, "gainratio/edge-proc", "b", "HEAD", (TEST_SITE,)) def test_should_print_changed_sites_from_the_cli( @@ -211,7 +211,7 @@ def test_should_exit_one_with_the_refusal_on_stderr( HEAD = "e" * 40 -REPOSITORY = "hseshadr/edge-proc" +REPOSITORY = "gainratio/edge-proc" class FakeGitHub: @@ -293,7 +293,7 @@ def test_should_write_nothing_when_the_head_literals_match_its_lock(tmp_path: Pa ("pull", "reason"), [ (_pull(login="octocat"), "not opened by dependabot"), - (_pull(repo="fork/edge-proc"), "not in hseshadr/edge-proc"), + (_pull(repo="fork/edge-proc"), "not in gainratio/edge-proc"), (_pull(ref="dependabot/npm_and_yarn/x"), "not a Dependabot uv branch"), (_pull(ref=7), "malformed"), ], diff --git a/tests/test_workflow_security.py b/tests/test_workflow_security.py index dfc5ec4..39bbb7f 100644 --- a/tests/test_workflow_security.py +++ b/tests/test_workflow_security.py @@ -155,7 +155,7 @@ def _expand_action_args(args: str, tag: str, cwd: Path) -> list[str]: """Expand args exactly as dagger-for-github's final bash step does, but print them.""" bash = shutil.which("bash") assert bash is not None - env = {"TAG": tag, "GITHUB_SHA": "a" * 40, "GITHUB_REPOSITORY": "hseshadr/edge-proc"} + env = {"TAG": tag, "GITHUB_SHA": "a" * 40, "GITHUB_REPOSITORY": "gainratio/edge-proc"} env["PATH"] = "/usr/bin:/bin" result = subprocess.run( # noqa: S603 [bash, "-c", f"printf '%s\\0' {args}"], env=env, cwd=cwd, capture_output=True, check=True @@ -212,7 +212,7 @@ def test_should_pass_any_dispatched_tag_to_dagger_as_one_inert_argument( # Then Dagger receives the tag verbatim as one argument and no command ran assert words[:2] == ["release-candidate", f"--tag={tag}"] assert words[2] == "--commit-sha=" + "a" * 40 - assert words[4] == "--repository=hseshadr/edge-proc" + assert words[4] == "--repository=gainratio/edge-proc" assert list(tmp_path.iterdir()) == []