refactor: eliminate duplicate code blocks (phases 1-3)#1360
refactor: eliminate duplicate code blocks (phases 1-3)#1360sergio-sisternes-epam wants to merge 5 commits into
Conversation
Extract shared utilities for registry package dispatch, env-var resolution, auth/header injection, and compilation rendering. Lower pylint R0801 CI threshold from 50 to 10 lines. - policy/_shared.py: _parse_apm_yml_safe - deps/_shared.py: _validate_and_load_package - runtime/base.py: _stream_subprocess_output - adapters/client/base.py: _fetch_server_info, _determine_config_key, _apply_pypi_homebrew_generic_config, _resolve_env_vars_with_prompting, _apply_auth_and_headers_impl - adapters/client/copilot.py: _apply_auth_and_headers (thin wrapper), _dispatch_package_to_config, _select_and_dispatch_best_package - adapters/client/cursor.py: _apply_auth_and_headers override (for testability via patch(cursor.GitHubTokenManager)) - integration/base_integrator.py: _check_adopt_or_skip - compilation/template_builder.py: build_attributed_instructions - .github/workflows/ci.yml: --min-similarity-lines 50→10 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR continues the duplicate-code reduction effort (phases 1-3) by extracting shared helpers across runtime adapters, MCP client adapters, compilation rendering, integrators, policy checks, and dependency download validation, and lowers the CI pylint R0801 threshold to --min-similarity-lines=10.
Changes:
- Deduplicates repeated logic via new shared helpers (e.g., subprocess streaming, env-var resolution/prompting, package validation/loading, attribution rendering, adopt-or-skip collision logic).
- Refactors multiple adapter/integrator implementations to call the new shared helpers.
- Lowers the CI duplicate-code guardrail threshold from 50 to 10.
Reviewed changes
Copilot reviewed 26 out of 26 changed files in this pull request and generated 15 comments.
Show a summary per file
| File | Description |
|---|---|
src/apm_cli/runtime/base.py |
Adds shared subprocess streaming helper for runtimes. |
src/apm_cli/runtime/copilot_runtime.py |
Uses shared subprocess streaming helper. |
src/apm_cli/runtime/llm_runtime.py |
Uses shared subprocess streaming helper. |
src/apm_cli/adapters/client/base.py |
Adds shared MCP adapter utilities (server lookup, key derivation, env prompting, auth/header injection, package dispatch helpers). |
src/apm_cli/adapters/client/copilot.py |
Refactors server config building to use shared helpers and extracted dispatch/auth logic. |
src/apm_cli/adapters/client/cursor.py |
Refactors to reuse shared fetch/key/auth/package dispatch helpers. |
src/apm_cli/adapters/client/codex.py |
Refactors to reuse shared fetch/key/env prompting and package config helpers. |
src/apm_cli/adapters/client/opencode.py |
Refactors to reuse shared server lookup + config key derivation helpers. |
src/apm_cli/compilation/template_builder.py |
Adds build_attributed_instructions() to dedupe attribution rendering closures. |
src/apm_cli/compilation/claude_formatter.py |
Switches to build_attributed_instructions(). |
src/apm_cli/compilation/distributed_compiler.py |
Switches to build_attributed_instructions(). |
src/apm_cli/integration/base_integrator.py |
Adds _check_adopt_or_skip() helper to dedupe adopt/collision logic. |
src/apm_cli/integration/agent_integrator.py |
Uses _check_adopt_or_skip(). |
src/apm_cli/integration/command_integrator.py |
Uses _check_adopt_or_skip(). |
src/apm_cli/integration/instruction_integrator.py |
Uses _check_adopt_or_skip(). |
src/apm_cli/policy/_shared.py |
Adds shared manifest parsing helper with consistent error reporting. |
src/apm_cli/policy/ci_checks.py |
Uses shared manifest parsing helper. |
src/apm_cli/policy/policy_checks.py |
Uses shared manifest parsing helper. |
src/apm_cli/deps/_shared.py |
Adds shared validation/load helper for downloaded packages. |
src/apm_cli/deps/github_downloader.py |
Uses shared validation/load helper. |
src/apm_cli/deps/artifactory_orchestrator.py |
Uses shared validation/load helper. |
src/apm_cli/commands/_helpers.py |
Moves _scan_installed_packages() into shared deps utils module. |
src/apm_cli/commands/marketplace/__init__.py |
Adds module-level pylint R0801 disable. |
src/apm_cli/marketplace/__init__.py |
Adds module-level pylint R0801 disable. |
src/apm_cli/marketplace/builder.py |
Adds module-level pylint R0801 disable. |
.github/workflows/ci.yml |
Lowers pylint duplicate-code threshold to 10. |
Comments suppressed due to low confidence (2)
src/apm_cli/runtime/base.py:34
- _stream_subprocess_output() assumes process.stdout is non-None and readable. Since subprocess.Popen(stdout=PIPE) is typed as Optional, it would be safer to assert/process.stdout is not None (or handle the None case) before calling .readline(), to avoid an AttributeError if the Popen parameters ever change.
process = subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, # Merge stderr into stdout for streaming
text=True,
encoding="utf-8",
bufsize=1, # Line buffered
)
output_lines = []
for line in iter(process.stdout.readline, ""):
print(line, end="", flush=True)
output_lines.append(line)
src/apm_cli/adapters/client/base.py:287
- _apply_pypi_homebrew_generic_config() changes homebrew/generic dispatch semantics in ways that don't match the previous per-adapter behavior: homebrew ignores processed_runtime_args and doesn't strip to the last path segment, and the generic fallback forces npx even when runtime_hint/package_name should be used. If this helper is meant to preserve behavior, it should mirror the prior branches (homebrew: command=last segment, args=processed_runtime_args+processed_package_args; generic: command=runtime_hint or package_name, args=processed_runtime_args+processed_package_args).
config["args"] = processed_runtime_args + [package_name] + processed_package_args # noqa: RUF005
elif registry_name == "homebrew":
config["command"] = package_name
config["args"] = processed_package_args
else:
# Generic / npm-compatible fallback
config["command"] = "npx"
config["args"] = processed_runtime_args + ["-y", package_name] + processed_package_args # noqa: RUF005
if resolved_env:
The tests call MCPIntegrator.install(…, runtime='vscode') without harness markers on disk. After commit 1e02d12 ('fix: respect targets whitelist for all runtimes during MCP install'), resolve_targets raises NoHarnessError in CI (no .github/copilot-instructions.md). Add _bypass_target_gate fixture that mocks resolve_targets for the three affected test classes so they no longer depend on harness marker files existing in the working directory. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Fix argument order in _apply_pypi_homebrew_generic_config() calls (copilot.py, codex.py) - Restore original PyPI argv ordering (package_name first for uvx) - Fix default_github_env to use values as literal fallbacks - Restore skip_prompting when env_overrides are supplied - Restore password=True masking for token/key-like env vars - Handle TimeoutExpired in _stream_subprocess_output() to avoid orphaned child processes - Remove 7 unnecessary module-level pylint R0801 disables - Replace non-ASCII arrow with ASCII '->' in docstring Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
APM Review Panel:
|
| Persona | B | R | N | Takeaway |
|---|---|---|---|---|
| Python Architect | 0 | 2 | 2 | Refactor is directionally correct; dispatch methods belong on MCPClientAdapter not CopilotClientAdapter; Prompt import should be lazy. |
| CLI Logging Expert | 0 | 2 | 2 | Three raw print() calls bypass the rich* / STATUS_SYMBOLS console layer; removal of APM_E2E_TESTS debug print is a clean improvement. |
| DevX UX Expert | 1 | 1 | 1 | Homebrew tap-formula slash-stripping regression for Codex is a silent runtime breakage; APM_E2E_TESTS removal may stall CI pipelines. |
| Supply Chain Security Expert | 0 | 1 | 1 | Security invariants preserved; os.getenv GITHUB_PERSONAL_ACCESS_TOKEN bypass now amplified to all adapters; APM_E2E_TESTS CI guard dropped. |
| OSS Growth Hacker | 0 | 1 | 1 | Positive contributor-experience signal; CONTRIBUTING.md callout needed or the pattern will be re-duplicated by the next adapter author. |
| Auth Expert | 0 | 1 | 1 | Token_manager_class injection pattern is sound; Authorization-header skip fires unconditionally even when no local token resolved. |
| Doc Writer | 0 | 1 | 0 | No documentation drift; missing CHANGELOG entry per project convention (every code-changing PR requires one). |
| Test Coverage Expert | 0 | 1 | 1 | Homebrew behavioral change (split removed) has no regression-trap test; new base methods lack direct unit coverage. |
B = blocking-severity findings, R = recommended, N = nits.
Counts are signal strength, not gates. The maintainer ships.
Top 5 follow-ups
- [DevX UX Expert] (blocking-severity) Fix homebrew tap-formula slash-stripping regression in
_apply_pypi_homebrew_generic_config-- Silent runtime breakage for any homebrew tap package with/in name. Confirmed missing regression-trap test (test-coverage-expert, outcome: missing). Must fix before merge. - [Auth Expert] Condition Authorization-header skip on whether a local token was actually resolved -- Guard currently fires unconditionally, potentially dropping a registry-supplied credential for GitHub MCP servers when all local token sources are absent.
- [DevX UX Expert] Restore
APM_E2E_TESTS=1(orCIenv var) as a skip-prompting signal in_resolve_env_vars_with_prompting-- Test harnesses using this signal will silently enter interactive prompt path; in PTY-allocated CI sandboxesisatty()can return True and hang the suite. - [CLI Logging Expert] Replace bare
print()calls in_fetch_server_infoand_resolve_env_vars_with_promptingwith_rich_error/_rich_warning-- stdout print corrupts JSON-output consumers and bypasses the established console layer. Two occurrences introduced by this PR. - [Doc Writer] Add CHANGELOG entry for this PR under
Unreleased > Changed-- Project convention requires a changelog entry for every code-changing PR. 24 source files changed and CI threshold tightened; neither is recorded.
Architecture
classDiagram
direction LR
class MCPClientAdapter {
<<Abstract>>
+target_name: str
+mcp_servers_key: str
+supports_user_scope: bool
+configure_mcp_server()*
+get_config_path()*
+update_config()*
+get_current_config()*
+_fetch_server_info(url, cache)
+_determine_config_key(url, name)
+_apply_pypi_homebrew_generic_config(config, ...)
+_apply_auth_and_headers_impl(config, remote, server_info, env_overrides, label, token_manager_class)
+_resolve_env_vars_with_prompting(env_vars, overrides, github_defaults)
+_infer_registry_name(package)
+_warn_input_variables(mapping, name, label)
}
class CopilotClientAdapter {
<<ConcreteAdapter>>
+_supports_runtime_env_substitution: bool
+_apply_auth_and_headers(config, remote, server_info, overrides, label)
+_dispatch_package_to_config(config, name, registry, hint, runtime_args, pkg_args, env)
+_select_and_dispatch_best_package(config, packages, overrides, vars, set_type_stdio)
+_resolve_environment_variables(env_vars, overrides)
+_format_server_config(server_info, overrides, vars)
+configure_mcp_server(url, name, enabled, overrides, cache, vars)
}
class CursorClientAdapter {
<<ConcreteAdapter>>
+_supports_runtime_env_substitution: bool
+_apply_auth_and_headers(config, remote, server_info, overrides, label)
+get_config_path()
+update_config(config_updates)
+get_current_config()
+_format_server_config(server_info, overrides, vars)
+configure_mcp_server(url, name, enabled, overrides, cache, vars)
}
class CodexClientAdapter {
<<ConcreteAdapter>>
+configure_mcp_server(url, name, enabled, overrides, cache, vars)
}
class GitHubTokenManager {
<<Service>>
+get_token_for_purpose(purpose) str
}
MCPClientAdapter <|-- CopilotClientAdapter : extends
CopilotClientAdapter <|-- CursorClientAdapter : extends
MCPClientAdapter <|-- CodexClientAdapter : extends
CopilotClientAdapter ..> GitHubTokenManager : instantiates via token_manager_class
CursorClientAdapter ..> GitHubTokenManager : instantiates via token_manager_class
class CopilotClientAdapter:::touched
class CursorClientAdapter:::touched
class MCPClientAdapter:::touched
classDef touched fill:#fff3b0,stroke:#d47600
flowchart TD
A["apm install / configure_mcp_server(url)"] --> B["_fetch_server_info(url, cache)\nbase.py: registry_client.find_server_by_reference()\n[NET]"]
B -->|not found| ERR1["print error, return False"]
B -->|found| C["_determine_config_key(url, server_name)\nbase.py: static, pure"]
C --> D{"server_info has remotes?"}
D -->|yes| E["_select_remote_with_url(remotes)\ncopilot.py"]
E --> F["_apply_auth_and_headers(config, remote, server_info, env_overrides)\ncopilot.py / cursor.py override"]
F --> G["_apply_auth_and_headers_impl(..., token_manager_class)\nbase.py: injects GitHub token + registry headers"]
G --> H{"is_github_server?"}
H -->|yes| I["token_manager_class().get_token_for_purpose('copilot')\n[ENV] os.getenv GITHUB_PERSONAL_ACCESS_TOKEN"]
I --> J["config['headers']['Authorization'] = Bearer token"]
H -->|no| K["merge remote.headers into config['headers']\n_resolve_env_variable() per header"]
J --> K
K --> L["_warn_input_variables(config.headers)"]
D -->|no remotes| M{"server_info has packages?"}
M -->|yes| N["_select_and_dispatch_best_package(config, packages, overrides, vars)\ncopilot.py"]
N --> O["_select_best_package(packages)"]
O --> P["_infer_registry_name(package)\nbase.py: static"]
P --> Q["_resolve_environment_variables(env_vars, overrides)\ncopilot.py: translate or resolve"]
Q --> R{"registry_name?"}
R -->|npm| S["config command=npx args=[-y pkg ...]\n_dispatch_package_to_config copilot.py"]
R -->|docker| T["DockerArgsProcessor.process_docker_args()\n_dispatch_package_to_config copilot.py"]
R -->|pypi/homebrew/generic| U["_apply_pypi_homebrew_generic_config()\nbase.py: static"]
S --> V["update_config({key: config})\n[FS] write JSON to disk"]
T --> V
U --> V
L --> V
M -->|no| ERR2["raise ValueError: incomplete registry config"]
Recommendation
Merge after fixing the homebrew slash-stripping regression in _apply_pypi_homebrew_generic_config (add the split('/')[-1] guard and a regression-trap test) and restoring the Authorization-header skip guard to fire only when a local token was actually injected. The APM_E2E_TESTS/CI skip-prompting restoration is strongly recommended in the same pass. All other findings are follow-up issues or nits. The refactor direction is sound; these three fixes are the gate.
Full per-persona findings
Python Architect
-
[recommended]
_select_and_dispatch_best_package/_dispatch_package_to_configbelong onMCPClientAdapter, notCopilotClientAdapteratsrc/apm_cli/adapters/client/copilot.py
CursorClientAdapter inherits these methods from CopilotClientAdapter purely because it needs shared package-dispatch logic. This makes cursor's class hierarchy semantically wrong: Cursor IS-A Copilot only because the shared code lives there. If a third adapter needed the same dispatch, it would be forced to inherit from Copilot or duplicate the code.
Suggested: Move_select_and_dispatch_best_packageand_dispatch_package_to_configtoMCPClientAdapterinbase.py. -
[recommended]
rich.prompt.Promptimported on every_resolve_env_vars_with_promptingcall including non-interactive CI paths atsrc/apm_cli/adapters/client/base.py
The import runs beforeskip_promptingis evaluated. In non-interactive flows the prompt is never called but the import always executes, adding overhead and weakening the optional-dependency claim.
Suggested: Movefrom rich.prompt import Promptinside theif not skip_prompting:branch. -
[nit] Redundant
import osinside_apply_auth_and_headers_implatsrc/apm_cli/adapters/client/base.py
osis already imported at module level. The inner import is a no-op but misleads readers into thinking it is a deliberate lazy import. -
[nit]
_write_instruction_blockmentioned in PR body is absent frombase_integrator.pyatsrc/apm_cli/integration/base_integrator.py
The PR description states the extraction but grep finds no such method. Either renamed, merged differently, or PR description is stale.
CLI Logging Expert
-
[recommended]
_fetch_server_infousesprint()for an error message instead of_rich_erroratsrc/apm_cli/adapters/client/base.py
Bareprint()always goes to stdout, corrupting JSON-output consumers. Should use_rich_errorwith the error symbol.
Suggested: Replace with_rich_error(f"MCP server '{server_url}' not found in registry", symbol='error') -
[recommended]
_resolve_env_vars_with_promptinguses bareprint()for two warning messages atsrc/apm_cli/adapters/client/base.py
Same issue: stdout always, no color, no stderr routing. First message also prints a raw Python list.
Suggested: Use_rich_warning()with appropriate messages and comma-joined var names. -
[nit]
_warn_input_variablesusesprint()with manually-prefixed[!]symbol atsrc/apm_cli/adapters/client/base.py
Pre-existing anti-pattern that the refactor copies callers to without fixing. -
[nit] Removal of the
APM_E2E_TESTSdebug print is an improvement atsrc/apm_cli/adapters/client/codex.py
The deleted print was test-internal noise with leading space and no STATUS_SYMBOL. Its removal is correct.
DevX UX Expert
-
[blocking] Homebrew tap-formula slash handling regressed for Codex adapter at
src/apm_cli/adapters/client/base.py
Oldcodex.py:package_name.split('/')[-1] if '/' in package_name. New shared_apply_pypi_homebrew_generic_config:config['command'] = package_nameverbatim. For any homebrew tap package (e.g.user/tap/formula), launched command becomesuser/tap/formula-- a path that does not exist. Silent runtimecommand not founderror only surfaces atapm run, notapm install.
Suggested: In_apply_pypi_homebrew_generic_config, apply the same split:config['command'] = package_name.split('/')[-1] if '/' in package_name else package_name. Add a test. -
[recommended] Removal of
APM_E2E_TESTS=1skip-prompting may silently stall non-TTY Codex CI pipelines atsrc/apm_cli/adapters/client/base.py
Test harnesses that setAPM_E2E_TESTS=1as CI signal will now silently enter the interactive prompt path. In PTY-allocated CI sandboxesisatty()can return True and hang the suite.
Suggested: PreserveAPM_E2E_TESTS=1as an accepted skip-prompting signal, or emit a deprecation warning. -
[nit] Copilot auth-header fix should be called out in the PR description, not buried in a refactor at
src/apm_cli/adapters/client/copilot.py
The fix is a real correctness improvement. Hiding it inside a deduplication PR makes it invisible in the changelog.
Supply Chain Security Expert
-
[recommended] Direct
os.getenv('GITHUB_PERSONAL_ACCESS_TOKEN')fallback bypassesAuthResolveratsrc/apm_cli/adapters/client/base.py
In_apply_auth_and_headers_impl, aftertoken_manager_class().get_token_for_purpose('copilot')the code falls back toos.getenv('GITHUB_PERSONAL_ACCESS_TOKEN')directly. This bypass is now promoted into the single shared implementation used by all adapters.
Suggested: Replaceos.getenvfallback with a secondAuthResolver/GitHubTokenManagercall using an appropriate purpose string. -
[nit]
APM_E2E_TESTSCI guard dropped from TTY detection atsrc/apm_cli/adapters/client/base.py
In sandboxed CI environments that allocate a PTY,isatty()can return True, allowing interactivePrompt.askto hang. Not a security issue but could cause test-suite hangs.
Suggested: Addos.environ.get('CI')orAPM_E2E_TESTSto theskip_promptingguard.
OSS Growth Hacker
-
[recommended] Add a
CONTRIBUTING.mdcallout for the new base-class /_shared.pypattern atCONTRIBUTING.md
The refactor introduces a clear adapter inheritance contract but that pattern is invisible to first-time contributors. Without guidance, the next adapter author will copy-paste again, negating the work.
Suggested: Add a section: "Adding an adapter -- use the base class and pull shared utilities from_shared.pyrather than copying code. The pylint R0801 threshold is set to 10 lines; a new PR that triggers it will fail CI." -
[nit] CHANGELOG entry could frame this as a contributor-experience milestone
"eliminate duplicate code" is maintainer-facing. "adapter authors now inherit shared auth/env/registry helpers" is a contributor recruitment signal.
Auth Expert
-
[recommended] Authorization skip for GitHub servers fires even when no local token found, silently dropping registry-supplied Authorization header at
src/apm_cli/adapters/client/base.py
In_apply_auth_and_headers_impl,if header_name == "Authorization" and is_github_server: continueexecutes regardless of whethergithub_tokenwas resolved. If all token sources are absent, no Authorization header is set from the token manager -- yet any registry-supplied Authorization header is still skipped. Net result: unauthenticated request to a GitHub MCP server that would otherwise have received a registry-provided credential.
Suggested:if header_name == "Authorization" and is_github_server and config.get("headers", {}).get("Authorization"): continue -
[nit]
os.getenv("GITHUB_PERSONAL_ACCESS_TOKEN")direct call bypassesAuthResolver/GitHubTokenManagerprecedence chain atsrc/apm_cli/adapters/client/base.py
GITHUB_PERSONAL_ACCESS_TOKENis not inTOKEN_PRECEDENCE["copilot"], so it sits outside the managed precedence chain. Pre-existing behavior preserved by the refactor.
Suggested: AddGITHUB_PERSONAL_ACCESS_TOKENtoTOKEN_PRECEDENCE["copilot"]intoken_manager.pyand remove theor os.getenv(...)fallback.
Doc Writer
- [recommended] Missing CHANGELOG entry for this code-changing PR at
CHANGELOG.md
The projectchangelog.instructions.mdstates every merged PR that changes code must have a changelog entry. This PR changes 24 source files and the CI workflow (pylint similarity threshold 50->10); neither is recorded.
Suggested: Add: "- Eliminated duplicate code blocks across adapter layer (phases 1-3); pylint similarity threshold tightened from 50 to 10 lines. (refactor: eliminate duplicate code blocks (phases 1-3) #1360)"
Test Coverage Expert
-
[recommended] Codex homebrew dispatch:
package_name.split('/')removed with no regression-trap test atsrc/apm_cli/adapters/client/base.py
Old Codex homebrew branch split the package name to extract the binary. New shared_apply_pypi_homebrew_generic_configusespackage_nameverbatim. Both are silent behavioral changes for homebrew packages with/in their name. grep oftests/forhomebrew,registry_name.*homebrew, and_apply_pypi_homebrewreturned zero hits.
Proof (missing):tests/unit/test_copilot_adapter.py::test_apply_pypi_homebrew_generic_config_strips_org_prefix-- proves: apm install of a homebrew package with an org-prefixed name invokes the binary correctly [devx]
assert config['command'] == 'formula' # not 'org/formula' -
[nit] New shared base methods have no direct unit tests; coverage relies solely on indirect adapter tests at
src/apm_cli/adapters/client/base.py
Five new methods inbase.pyhave no dedicated unit tests. Existing adapter tests exercise them indirectly. Newtest_transitive_mcp.pycovers MCP transitive dependency deduplication, not these helpers.
Proof (missing):tests/unit/test_mcp_client_base.py::test_determine_config_key_uses_url_tail_when_name_absent-- proves: server config key resolution uses the URL path tail when no explicit name is given [devx]
assert MCPClientAdapter._determine_config_key('(host/redacted), '') == 'path'
This panel is advisory. It does not block merge. Re-apply the
panel-review label after addressing feedback to re-run.
Generated by PR Review Panel for issue #1360 · ● 3.4M · ◷
- Fix homebrew tap-formula slash-stripping regression (BLOCKING) - Fix Authorization header skip to only fire when local token injected - Add CI/APM_E2E_TESTS env vars as skip-prompting signals - Replace bare print() calls with _rich_warning/_rich_error helpers - Extract iter_semver_tags into marketplace._shared (R0801 fix) - Add narrow pylint disables for intentional structural similarity - Add CHANGELOG entry for PR #1360 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Remove redundant `import os` inside _apply_auth_and_headers_impl (already at module level) - Move `from rich.prompt import Prompt` inside the prompting branch to avoid unnecessary import in CI/non-interactive paths - Remove redundant '[!] Warning:' prefix from _warn_input_variables message since _rich_warning already adds the [!] symbol - Add CONTRIBUTING.md section on adapter base-class pattern and _shared.py convention to prevent future duplication - Add regression-trap tests for homebrew tap-formula slash-stripping and pypi/homebrew config generation Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Panel findings -- resolution summaryAll blocking and recommended findings have been addressed. Nits were fixed where the change was low-risk and reduced tech debt. Two recommended items were intentionally deferred (pre-existing behaviour, high risk of regression). Blocking (1/1 fixed)
Recommended (8/10 fixed, 2 deferred)
Nits (7/9 fixed, 2 deferred)
Commits
|
Eliminate duplicate code blocks (Phases 1-3)
TL;DR
Lowers the pylint R0801 (duplicate-code) CI guardrail from
--min-similarity-lines=50to--min-similarity-lines=10by extracting shared utilities across adapter clients, compilation, integrators, policy, runtime, and marketplace modules.Problem (WHY)
Phase 0 (PR #1320) added a pylint R0801 guardrail at threshold 50, catching only the largest clones. Running at threshold 10 revealed 20 duplicate-code violations spanning registry package dispatch, env-var resolution, auth/header injection, compilation rendering, and numerous smaller clones across the codebase. These clones increase maintenance burden and divergence risk.
Approach (WHAT)
Progressive strangling-fig strategy -- fix violations at each intermediate threshold level:
Implementation (HOW)
_apply_auth_and_headers_impl()on base class; each adapter passes its ownGitHubTokenManagernamespacebase.py,copilot.py,cursor.pycopilot.py,cursor.py,codex.pyrender_instructions_with_footer()intotemplate_builder.pyclaude_formatter.py,distributed_compiler.py,template_builder.py_write_instruction_block()intobase_integrator.pyagent_integrator.py,command_integrator.py,instruction_integrator.py_shared.pymodule for common check patternsci_checks.py,policy_checks.py,policy/_shared.pyruntime/base.pycopilot_runtime.py,llm_runtime.py_shared.pyfor common download patternsartifactory_orchestrator.py,github_downloader.py,deps/_shared.pycommands/_helpers.py,marketplace/__init__.py,marketplace/builder.pyValidation
pylint --enable=R0801 --min-similarity-lines=10exits 0 (score: 10.00/10)ruff check && ruff format --check-- all cleanpytest tests/unit tests/test_console.py-- 8498 passed, 0 failed.github/workflows/ci.ymlTrade-offs
_shared.pymodules:deps/_shared.pyandpolicy/_shared.py-- preferred over making base classes overly broad_apply_auth_and_headers_implusestoken_manager_classparameter: necessary because Python resolves names in the module where a method is defined, not where the subclass lives -- passing the class explicitly allows test mocking to work correctlyHow to test