diff --git a/docs/tooling/workspace-cli.md b/docs/tooling/workspace-cli.md index 1a74e51..866cc28 100644 --- a/docs/tooling/workspace-cli.md +++ b/docs/tooling/workspace-cli.md @@ -249,9 +249,11 @@ Notes - Publish-time GHCR credentials can be split by purpose. Private base image reads prefer `GHCR_READ_TOKEN`, artifact image pushes prefer `GHCR_TOKEN`, and private source checkout secrets still belong in the transient runtime - payload as `GITHUB_TOKEN`. This lets CI use a repo-scoped package-write token - for the tenant artifact while using a separate read token for shared private - base images. + payload as `GITHUB_TOKEN`. For selector resolution before the build context + exists, CI can also provide `ODOO_DEVKIT_SOURCE_GITHUB_TOKEN` or + `ODOO_SOURCE_GITHUB_TOKEN`. This lets CI use a repo-scoped package-write + token for the tenant artifact while using separate credentials for shared + private source repositories and private base images. - When a repo-owned `artifact-inputs.toml` lives beside `workspace.toml`, `platform runtime` commands use it as the repo-owned source-input contract. Runtime and publish do not fall back to `stack.toml` source selector fields. diff --git a/odoo_devkit/local_runtime.py b/odoo_devkit/local_runtime.py index 02bc99e..bec2754 100644 --- a/odoo_devkit/local_runtime.py +++ b/odoo_devkit/local_runtime.py @@ -30,6 +30,7 @@ DEFAULT_ARTIFACT_IMAGE_PLATFORMS = ("linux/amd64", "linux/arm64") GIT_SHA_PATTERN = re.compile(r"[0-9a-fA-F]{7,40}") ARTIFACT_SOURCE_ENV_KEYS = ("ODOO_ADDON_REPOSITORIES", "OPENUPGRADE_ADDON_REPOSITORY") +SOURCE_GITHUB_TOKEN_ENV_KEYS = ("ODOO_DEVKIT_SOURCE_GITHUB_TOKEN", "ODOO_SOURCE_GITHUB_TOKEN") RUNTIME_ENVIRONMENT_PAYLOAD_ENV_VAR = "ODOO_DEVKIT_RUNTIME_ENVIRONMENT_JSON" ODOO_INSTANCE_OVERRIDES_PAYLOAD_ENV_KEY = "ODOO_INSTANCE_OVERRIDES_PAYLOAD_B64" LEGACY_CONFIG_PARAM_PREFIX = "ENV_OVERRIDE_CONFIG_PARAM__" @@ -2087,9 +2088,7 @@ def resolve_artifact_runtime_source_repository_refs( runtime_values: dict[str, str], ) -> tuple[dict[str, str], tuple[dict[str, str], ...]]: resolved_values = dict(runtime_values) - github_token = clean_optional_value(runtime_values.get("GITHUB_TOKEN")) or first_clean_optional_value( - (os.environ.get("GITHUB_TOKEN"), os.environ.get("GH_TOKEN")) - ) + github_token = resolve_source_github_token(runtime_values) selector_metadata: list[dict[str, str]] = [] for env_key in ARTIFACT_SOURCE_ENV_KEYS: raw_value = runtime_values.get(env_key, "") @@ -2117,6 +2116,12 @@ def resolve_artifact_runtime_source_repository_refs( return resolved_values, tuple(selector_metadata) +def resolve_source_github_token(runtime_values: dict[str, str]) -> str | None: + return clean_optional_value(runtime_values.get("GITHUB_TOKEN")) or first_clean_optional_value( + os.environ.get(environment_key) for environment_key in (*SOURCE_GITHUB_TOKEN_ENV_KEYS, "GITHUB_TOKEN", "GH_TOKEN") + ) + + def resolve_source_repository_ref_to_git_sha(*, repository: str, ref: str, github_token: str | None = None) -> str: normalized_repository = repository.strip() normalized_ref = ref.strip() diff --git a/tests/test_runtime.py b/tests/test_runtime.py index 17986f2..372c3c4 100644 --- a/tests/test_runtime.py +++ b/tests/test_runtime.py @@ -1619,10 +1619,8 @@ def test_resolve_artifact_runtime_source_refs_uses_environment_token_fallback(se "odoo_devkit.local_runtime.resolve_source_repository_ref_to_git_sha", return_value="411f6b8e85cac72dc7aa2e2dc5540001043c327d", ) as resolve_ref_mock: - resolved_values, selector_metadata = ( - local_runtime.resolve_artifact_runtime_source_repository_refs( - runtime_values=runtime_values - ) + resolved_values, selector_metadata = local_runtime.resolve_artifact_runtime_source_repository_refs( + runtime_values=runtime_values ) resolve_ref_mock.assert_called_once_with( @@ -1645,6 +1643,52 @@ def test_resolve_artifact_runtime_source_refs_uses_environment_token_fallback(se ), ) + def test_resolve_artifact_runtime_source_refs_uses_dedicated_source_token_env(self) -> None: + runtime_values = { + "ODOO_ADDON_REPOSITORIES": "cbusillo/disable_odoo_online@main", + } + with mock.patch.dict( + os.environ, + { + "ODOO_DEVKIT_SOURCE_GITHUB_TOKEN": "source-env-token", + "GITHUB_TOKEN": "github-env-token", + }, + ): + with mock.patch( + "odoo_devkit.local_runtime.resolve_source_repository_ref_to_git_sha", + return_value="411f6b8e85cac72dc7aa2e2dc5540001043c327d", + ) as resolve_ref_mock: + local_runtime.resolve_artifact_runtime_source_repository_refs(runtime_values=runtime_values) + + resolve_ref_mock.assert_called_once_with( + repository="cbusillo/disable_odoo_online", + ref="main", + github_token="source-env-token", + ) + + def test_resolve_artifact_runtime_source_refs_supports_ci_source_token_env(self) -> None: + runtime_values = { + "ODOO_ADDON_REPOSITORIES": "cbusillo/disable_odoo_online@main", + } + with mock.patch.dict( + os.environ, + { + "ODOO_SOURCE_GITHUB_TOKEN": "ci-source-token", + "GITHUB_TOKEN": "github-env-token", + }, + ): + with mock.patch( + "odoo_devkit.local_runtime.resolve_source_repository_ref_to_git_sha", + return_value="411f6b8e85cac72dc7aa2e2dc5540001043c327d", + ) as resolve_ref_mock: + local_runtime.resolve_artifact_runtime_source_repository_refs(runtime_values=runtime_values) + + resolve_ref_mock.assert_called_once_with( + repository="cbusillo/disable_odoo_online", + ref="main", + github_token="ci-source-token", + ) + def test_resolve_artifact_runtime_source_refs_prefers_runtime_github_token(self) -> None: runtime_values = { "GITHUB_TOKEN": "source-token", @@ -1655,9 +1699,7 @@ def test_resolve_artifact_runtime_source_refs_prefers_runtime_github_token(self) "odoo_devkit.local_runtime.resolve_source_repository_ref_to_git_sha", return_value="411f6b8e85cac72dc7aa2e2dc5540001043c327d", ) as resolve_ref_mock: - local_runtime.resolve_artifact_runtime_source_repository_refs( - runtime_values=runtime_values - ) + local_runtime.resolve_artifact_runtime_source_repository_refs(runtime_values=runtime_values) resolve_ref_mock.assert_called_once_with( repository="cbusillo/disable_odoo_online",