refactor: complete Engine abstraction — move install, invocation, and paths behind Engine enum#301
Conversation
The AWF invocation explicitly passes --additional-mcp-config @/tmp/awf-tools/mcp-config.json, so the copy to $HOME/.copilot/ mcp-config.json was never read by copilot inside the AWF container. Removes from both base.yml and 1es-base.yml: - mkdir -p "$HOME/.copilot" - cp/chmod of mcp-config.json to $HOME/.copilot/ Part of #287 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add Engine::log_dir() method returning the engine-specific log
directory (~/.copilot/logs for Copilot). Replace 6 hardcoded
~/.copilot/logs paths in both templates with {{ engine_log_dir }}
marker, substituted at compile time.
Part of #287
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add Engine::install_steps(engine_config) that generates the full
NuGet install YAML for the Copilot CLI. Replaces 4 hardcoded install
blocks (Agent + Detection jobs in both templates) with a single
{{ engine_install_steps }} marker.
- Move COPILOT_CLI_VERSION constant from common.rs to engine.rs
- Wire engine.version front matter field: uses custom version if set,
falls back to COPILOT_CLI_VERSION
- Return empty string when engine.command is set (skip NuGet install)
- Remove {{ copilot_version }} marker (absorbed into install_steps)
- Remove engine.version "not yet wired" warning
Part of #287
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add Engine::invocation() that generates the complete AWF command string
including binary path, prompt delivery flag, and MCP config flag.
The engine now controls how prompts are provided (e.g. --prompt
"$(cat ...)") and how MCP config is referenced.
Template changes:
- Replace hardcoded AWF commands with {{ engine_run }} (Agent job)
and {{ engine_run_detection }} (Detection job)
- Remove {{ copilot_params }} marker (absorbed into invocation)
Code changes:
- Add copilot_invocation() helper in engine.rs
- Replace copilot_params variable with engine_run/engine_run_detection
- Rename 20 test functions: test_copilot_params_* → test_engine_args_*
- Update compiler_tests.rs marker assertion
Part of #287
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…actor - AGENTS.md: document new markers (engine_install_steps, engine_run, engine_run_detection, engine_env, engine_log_dir), mark copilot_version as removed, remove duplicate engine_env section - Version updater: point COPILOT_CLI_VERSION at src/engine.rs - Test docs: rename copilot_params references to engine_args/engine_run - compiler_tests.rs: update assertion messages Part of #287 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
🔍 Rust PR ReviewSummary: Mostly well-executed refactor — the abstraction is clean and the template changes are complete, but there's one security gap and one logic regression worth addressing before merge. Findings🔒 Security Concerns
🐛 Bugs / Logic Issues
|
🔍 Rust PR ReviewSummary: Mostly clean refactor with one functional bug and two stale documentation entries that need fixing before merge. Findings🐛 Bugs / Logic Issues
🔒 Security Concerns
|
…line Wire the five remaining engine front matter fields that were previously parsed but emitted 'not yet wired' warnings: - engine.command: overrides the engine binary path in the AWF invocation; skips NuGet install (already handled). Validated for shell-safe chars. - engine.agent: adds --agent <name> to the Copilot CLI invocation for custom agent file selection from .github/agents/. - engine.api-target: adds --api-target <hostname> for GHES/GHEC endpoints and adds the hostname to the AWF network allowlist. - engine.args: appends user-provided CLI arguments after compiler-generated args. Subject to strict char validation and blocked from overriding compiler-controlled flags (--prompt, --allow-tool, --disable-builtin-mcps, --no-ask-user, --ask-user, --additional-mcp-config, --allow-all-tools, --allow-all-paths). - engine.env: merges user environment variables into the sandbox step env block. Validates key names, blocks compiler-controlled keys (GITHUB_TOKEN, PATH, BASH_ENV, LD_PRELOAD, etc.), and rejects ADO expression/command injection in values. All five warnings are removed. AGENTS.md updated to reflect wired status. 33 new tests added covering happy paths, validation, and security edge cases. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
🔍 Rust PR ReviewSummary: Solid abstraction with good security rigor — one bug to fix before merging, one missing validation worth adding for consistency. Findings🐛 Bugs / Logic Issues
🔒 Security Concerns
✅ What Looks Good
|
- Fix empty env key panic: key.chars().next().unwrap() was called
before the emptiness check, causing a panic on empty YAML keys
("": value). Now uses let-else to bail with a clear error.
- Validate engine.version: the version string was embedded unvalidated
into NuGet command arguments, allowing injection (e.g., spaces,
quotes, additional -Source flags). Now validated with is_valid_version
allowlist [A-Za-z0-9._-] before use. install_steps() returns Result
to propagate the validation error.
- 5 new tests for both fixes.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
🔍 Rust PR ReviewSummary: Solid refactor with good security hygiene — one runtime bug with Findings🐛 Bugs / Logic Issues
|
…ates
- engine.version: "latest" now omits the -Version flag entirely so
NuGet installs the newest available version, instead of passing
"-Version latest" which NuGet rejects at runtime.
- Quote {{ engine_log_dir }} in base.yml and 1es-base.yml shell scripts
to future-proof against paths with spaces.
- Add explanatory comment on the intentionally case-insensitive
BLOCKED_ENV_KEYS comparison (prevents accidental shadowing).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
🔍 Rust PR ReviewSummary: Solid refactor with good security validation throughout — one regression introduced in the template migration that silently breaks log collection. Findings🐛 Bugs / Logic Issues
|
Summary
Completes the Engine abstraction by moving all Copilot-specific artifacts out of pipeline templates and behind
Engineenum methods, then wires all remainingengine.*front matter fields into the pipeline. Adding a new engine now requires only implementing the enum variant — no template changes needed.Closes #287
Changes
New
Enginemethodsinstall_steps(engine_config){{ engine_install_steps }}invocation(fm, exts, prompt, mcp){{ engine_run }},{{ engine_run_detection }}--command stringlog_dir(){{ engine_log_dir }}env(engine_config){{ engine_env }}required_hosts(engine_config)Removed template markers
{{ copilot_version }}— absorbed into{{ engine_install_steps }}{{ copilot_params }}— absorbed into{{ engine_run }}/{{ engine_run_detection }}Engine front matter fields — now fully wired
All five previously "not yet wired" fields are now functional:
command[A-Za-z0-9_./-]agent--agent <name>to Copilot CLI for custom agent selectionapi-target--api-target <host>for GHES/GHEC + adds hostname to AWF allowlistargsenvenv:blockmax-turnsremovedThe
max-turnsfield was specific to Claude Code and not supported by Copilot CLI. Removed fromEngineOptions,EngineConfigaccessors, and all tests.Key design decisions
invocation()generates the complete command including--prompt "$(cat ...)"and--additional-mcp-config @.... Different engines can use different prompt delivery mechanisms.engine.versionwired:install_steps()usesengine_config.version()if set, falling back toCOPILOT_CLI_VERSION. Returns empty whenengine.commandis set.engine.argsmerge strategy: User args are appended after compiler args (last-wins for duplicate flags). Compiler security flags always appear first and cannot be removed. Blocked prefixes:--prompt,--additional-mcp-config,--allow-tool,--allow-all-tools,--allow-all-paths,--disable-builtin-mcps,--no-ask-user,--ask-user.engine.envsecurity: Blocked keys include compiler-controlled (GITHUB_TOKEN,COPILOT_OTEL_*) and dangerous shell/system vars (PATH,BASH_ENV,LD_PRELOAD, etc.). Values reject ADO expressions ($(,${{), pipeline commands (##vso[), and newlines. Values are YAML-quoted with proper escaping.$HOME/.copilot/mcp-config.jsoncopy was redundant — AWF invocation explicitly passes--additional-mcp-config @/tmp/awf-tools/mcp-config.json.COPILOT_CLI_VERSIONmoved fromcompile/common.rstoengine.rs(engine-owned constant).Files changed
src/engine.rs— all 5 field wirings, validation helpers, blocked lists,required_hosts(), 33 new testssrc/compile/common.rs—env()signature update, engine hosts in domain listsrc/compile/standalone.rs— api-target domain allowlist testsrc/compile/types.rs—max_turnsremovalsrc/data/base.yml+src/data/1es-base.yml— template marker replacementsAGENTS.md— updated engine config table (removed "not yet wired"), updated marker docsOut of scope
run.rs(debug-only local dev mode)Testing
All 954 tests pass (33 new). Clippy clean on changed files.