Skip to content

Commit 0ce9238

Browse files
fix(compile): use workingDirectory for integrity check instead of absolute path (#346)
Agent-Logs-Url: https://github.com/githubnext/ado-aw/sessions/aa180f1e-5a9d-4660-ae43-941dae017d48 Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com>
1 parent e156c8f commit 0ce9238

2 files changed

Lines changed: 34 additions & 23 deletions

File tree

AGENTS.md

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -807,26 +807,24 @@ Used by the execute command's --source parameter. The agent markdown only ever l
807807

808808
## {{ pipeline_path }}
809809

810-
Should be replaced with the path to the compiled pipeline YAML file for runtime integrity checking. The path is derived from the output path (preserving any directory structure) and is anchored at the **trigger ("self") repository** via `{{ trigger_repo_directory }}` (see below), independent of the user's `workspace:` setting:
811-
- No additional checkouts: `$(Build.SourcesDirectory)/<relative-path>.yml`
812-
- Additional checkouts present: `$(Build.SourcesDirectory)/$(Build.Repository.Name)/<relative-path>.yml`
810+
Should be replaced with the path to the compiled pipeline YAML file for runtime integrity checking. The path is **relative** to the trigger repository root (e.g. `agents/ctf.yml`, `pipelines/production/review.lock.yml`). The integrity check step itself sets `workingDirectory: {{ trigger_repo_directory }}` so the relative path resolves correctly regardless of whether additional repositories are checked out, and so that `ado-aw check`'s recompile step has access to the trigger repo's `.git` directory (required to infer the ADO org for `tools.azure-devops`).
813811

814-
For example, an output path of `pipelines/production/review.lock.yml` resolves to `$(Build.SourcesDirectory)/pipelines/production/review.lock.yml` when no additional repositories are checked out.
815-
816-
Used by the pipeline's integrity check step to verify the pipeline hasn't been modified outside the compilation process. The compiled yaml only ever lives in the trigger repo, so this is intentionally not affected by `workspace:` pointing at a non-self alias.
812+
Used by the pipeline's integrity check step to verify the pipeline hasn't been modified outside the compilation process.
817813

818814
## {{ trigger_repo_directory }}
819815

820816
Should be replaced with the directory where the trigger ("self") repository is checked out. This is independent of the `workspace:` setting and depends only on whether any additional repositories are listed in `checkout:`:
821817
- No additional checkouts → `$(Build.SourcesDirectory)` (ADO checks `self` into the root)
822818
- One or more additional checkouts → `$(Build.SourcesDirectory)/$(Build.Repository.Name)` (ADO puts each checked-out repo, including `self`, into a subfolder named after the repository)
823819

824-
Use this marker (rather than `{{ working_directory }}` / `{{ workspace }}`) for any path that refers to a file shipped in the trigger repo (e.g. the agent markdown source and the compiled pipeline yaml itself).
820+
Use this marker (rather than `{{ working_directory }}` / `{{ workspace }}`) for any path that refers to a file shipped in the trigger repo (e.g. the agent markdown source) or as a `workingDirectory:` for steps that need access to the trigger repo's `.git` (e.g. the integrity check step).
825821

826822
## {{ integrity_check }}
827823

828824
Generates the "Verify pipeline integrity" pipeline step that downloads the released ado-aw compiler and runs `ado-aw check` against the compiled pipeline YAML. This step ensures the pipeline file hasn't been modified outside the compilation process.
829825

826+
The step sets `workingDirectory: {{ trigger_repo_directory }}` so that the relative `{{ pipeline_path }}` argument resolves correctly when `checkout:` produces a multi-repo `$(Build.SourcesDirectory)` layout, and so `ado-aw check`'s internal recompile can infer the ADO org from the trigger repo's git remote.
827+
830828
When the compiler is built with `--skip-integrity` (debug builds only), this placeholder is replaced with an empty string and the integrity step is omitted from the generated pipeline.
831829

832830
## {{ mcpg_debug_flags }}

src/compile/common.rs

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,13 @@ pub fn generate_source_path(input_path: &std::path::Path) -> String {
648648
/// downloads the ado-aw compiler and runs `ado-aw check` against the
649649
/// pipeline path.
650650
///
651+
/// The step sets `workingDirectory: {{ trigger_repo_directory }}` so that:
652+
/// 1. The relative `{{ pipeline_path }}` argument resolves correctly when
653+
/// `checkout:` produces a multi-repo `$(Build.SourcesDirectory)` layout.
654+
/// 2. `ado-aw check`'s recompile step has access to the trigger repo's
655+
/// `.git` directory, which is required to infer the ADO org from the
656+
/// git remote (used by `tools.azure-devops`).
657+
///
651658
/// When `skip` is `true` (developer builds with `--skip-integrity`),
652659
/// returns an empty string and the step is omitted from the pipeline.
653660
pub fn generate_integrity_check(skip: bool) -> String {
@@ -660,6 +667,7 @@ pub fn generate_integrity_check(skip: bool) -> String {
660667
AGENTIC_PIPELINES_PATH="$(Pipeline.Workspace)/agentic-pipeline-compiler/ado-aw"
661668
chmod +x "$AGENTIC_PIPELINES_PATH"
662669
$AGENTIC_PIPELINES_PATH check "{{ pipeline_path }}"
670+
workingDirectory: {{ trigger_repo_directory }}
663671
displayName: "Verify pipeline integrity""#
664672
.to_string()
665673
}
@@ -755,9 +763,11 @@ pub fn generate_debug_pipeline_replacements(debug: bool) -> Vec<(String, String)
755763

756764
/// Generate the pipeline YAML path for integrity checking at ADO runtime.
757765
///
758-
/// Returns a path using `{{ trigger_repo_directory }}` as the base. The
759-
/// compiled pipeline yaml ships in the trigger ("self") repo, so this anchor
760-
/// is independent of the user's `workspace:` setting.
766+
/// Returns the path **relative** to the trigger repository root. The integrity
767+
/// check step itself sets `workingDirectory: {{ trigger_repo_directory }}` so
768+
/// that the path resolves correctly and so that `ado-aw check`'s recompile
769+
/// step has access to the trigger repo's `.git` directory (needed to infer
770+
/// the ADO org for `tools.azure-devops`).
761771
///
762772
/// The full relative path is preserved so that pipelines compiled into
763773
/// subdirectories (e.g. `agents/ctf.yml`) produce a correct runtime path
@@ -766,15 +776,13 @@ pub fn generate_debug_pipeline_replacements(debug: bool) -> Vec<(String, String)
766776
/// Absolute paths fall back to using only the filename to avoid embedding
767777
/// machine-specific paths in the generated pipeline.
768778
pub fn generate_pipeline_path(output_path: &std::path::Path) -> String {
769-
let relative = normalize_relative_path(output_path).unwrap_or_else(|| {
779+
normalize_relative_path(output_path).unwrap_or_else(|| {
770780
output_path
771781
.file_name()
772782
.and_then(|n| n.to_str())
773783
.unwrap_or("pipeline.yml")
774784
.to_string()
775-
});
776-
777-
format!("{{{{ trigger_repo_directory }}}}/{}", relative)
785+
})
778786
}
779787

780788
/// Normalize a path for embedding in a generated pipeline.
@@ -2646,33 +2654,32 @@ mod tests {
26462654
fn test_generate_pipeline_path_preserves_directory() {
26472655
// The original bug: compiling agents/ctf.md produced agents/ctf.yml as
26482656
// output, but the embedded path was only ctf.yml (missing agents/).
2657+
// Pipeline path is relative to the integrity check's workingDirectory
2658+
// ({{ trigger_repo_directory }}), so no prefix is embedded here.
26492659
let path = std::path::Path::new("agents/ctf.yml");
26502660
let result = generate_pipeline_path(path);
2651-
assert_eq!(result, "{{ trigger_repo_directory }}/agents/ctf.yml");
2661+
assert_eq!(result, "agents/ctf.yml");
26522662
}
26532663

26542664
#[test]
26552665
fn test_generate_pipeline_path_nested_directory() {
26562666
let path = std::path::Path::new("pipelines/production/review.yml");
26572667
let result = generate_pipeline_path(path);
2658-
assert_eq!(
2659-
result,
2660-
"{{ trigger_repo_directory }}/pipelines/production/review.yml"
2661-
);
2668+
assert_eq!(result, "pipelines/production/review.yml");
26622669
}
26632670

26642671
#[test]
26652672
fn test_generate_pipeline_path_strips_dot_slash() {
26662673
let path = std::path::Path::new("./agents/my-agent.yml");
26672674
let result = generate_pipeline_path(path);
2668-
assert_eq!(result, "{{ trigger_repo_directory }}/agents/my-agent.yml");
2675+
assert_eq!(result, "agents/my-agent.yml");
26692676
}
26702677

26712678
#[test]
26722679
fn test_generate_pipeline_path_filename_only() {
26732680
let path = std::path::Path::new("pipeline.yml");
26742681
let result = generate_pipeline_path(path);
2675-
assert_eq!(result, "{{ trigger_repo_directory }}/pipeline.yml");
2682+
assert_eq!(result, "pipeline.yml");
26762683
}
26772684

26782685
#[test]
@@ -2693,7 +2700,7 @@ mod tests {
26932700
let tmp = tempfile::TempDir::new().unwrap();
26942701
let abs_path = tmp.path().join("agents").join("ctf.yml");
26952702
let result = generate_pipeline_path(&abs_path);
2696-
assert_eq!(result, "{{ trigger_repo_directory }}/ctf.yml");
2703+
assert_eq!(result, "ctf.yml");
26972704
}
26982705

26992706
#[test]
@@ -2721,7 +2728,7 @@ mod tests {
27212728
fs::write(tmp.path().join(".git"), "gitdir: fake").unwrap();
27222729
let abs_path = agents_dir.join("ctf.yml");
27232730
let result = generate_pipeline_path(&abs_path);
2724-
assert_eq!(result, "{{ trigger_repo_directory }}/agents/ctf.yml");
2731+
assert_eq!(result, "agents/ctf.yml");
27252732
}
27262733

27272734
// ─── generate_trigger_repo_directory ─────────────────────────────────────
@@ -2791,6 +2798,12 @@ mod tests {
27912798
result.contains("{{ pipeline_path }}"),
27922799
"Should contain the pipeline_path placeholder for later resolution"
27932800
);
2801+
assert!(
2802+
result.contains("workingDirectory: {{ trigger_repo_directory }}"),
2803+
"Should set workingDirectory to the trigger repo so `ado-aw check` \
2804+
can recompile from a directory that contains .git (needed for \
2805+
ADO org inference when tools.azure-devops is enabled)"
2806+
);
27942807
}
27952808

27962809
#[test]

0 commit comments

Comments
 (0)