Skip to content

Commit b25f143

Browse files
feat: auto-detect source from header in check command (#108)
The check command now takes only the pipeline YAML path and discovers the source markdown file from the @ado-aw header. This eliminates a path mismatch bug where local compilation embeds a relative source path in the header but the pipeline check step passed an absolute path, causing the integrity check to always fail at runtime. BREAKING CHANGE: \check <source> <pipeline>\ is now \check <pipeline>\. Update any scripts or pipeline templates that call the old two-arg form. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 963c5f8 commit b25f143

5 files changed

Lines changed: 54 additions & 26 deletions

File tree

AGENTS.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -715,9 +715,9 @@ Global flags (apply to all subcommands): `--verbose, -v` (enable info-level logg
715715
- The generated file includes a placeholder for agent instructions that you edit directly
716716
- `compile <path>` - Compile a markdown file to Azure DevOps pipeline YAML
717717
- `--output, -o <path>` - Optional output path for generated YAML
718-
- `check <source> <pipeline>` - Verify that a compiled pipeline matches its source markdown
719-
- `<source>` - Path to the source markdown file
718+
- `check <pipeline>` - Verify that a compiled pipeline matches its source markdown
720719
- `<pipeline>` - Path to the pipeline YAML file to verify
720+
- The source markdown path is auto-detected from the `@ado-aw` header in the pipeline file
721721
- Useful for CI checks to ensure pipelines are regenerated after source changes
722722
- `mcp <output_directory> <bounding_directory>` - Run as an MCP server for safe outputs
723723
- `execute` - Execute safe outputs from Stage 1 (Stage 2 of pipeline)

src/compile/mod.rs

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -182,21 +182,54 @@ pub async fn compile_all_pipelines() -> Result<()> {
182182

183183
/// Check that a compiled pipeline YAML matches its source markdown.
184184
///
185-
/// Compiles the source markdown fresh and compares (whitespace-normalized)
185+
/// Reads the `@ado-aw` header from `pipeline_path` to discover the source
186+
/// markdown file, compiles it fresh, and compares (whitespace-normalized)
186187
/// against the existing pipeline file. Returns an error if they differ.
187-
pub async fn check_pipeline(source_path: &str, pipeline_path: &str) -> Result<()> {
188-
let source_path = Path::new(source_path);
188+
pub async fn check_pipeline(pipeline_path: &str) -> Result<()> {
189189
let pipeline_path = Path::new(pipeline_path);
190+
191+
// Read existing pipeline and extract header to discover source path
192+
let existing = tokio::fs::read_to_string(pipeline_path)
193+
.await
194+
.with_context(|| {
195+
format!(
196+
"Failed to read pipeline file: {}",
197+
pipeline_path.display()
198+
)
199+
})?;
200+
201+
let header_meta = existing
202+
.lines()
203+
.take(5)
204+
.find_map(|line| crate::detect::parse_header_line(line))
205+
.with_context(|| {
206+
format!(
207+
"No @ado-aw header found in {}. Is this file generated by ado-aw?",
208+
pipeline_path.display()
209+
)
210+
})?;
211+
212+
// Resolve source path relative to the pipeline file's parent directory
213+
let source_path = pipeline_path
214+
.parent()
215+
.unwrap_or_else(|| Path::new("."))
216+
.join(&header_meta.source);
217+
190218
info!(
191-
"Checking pipeline integrity: {} -> {}",
219+
"Checking pipeline integrity: {} -> {} (source from header)",
192220
source_path.display(),
193221
pipeline_path.display()
194222
);
195223

196224
// Compile fresh from source
197-
let content = tokio::fs::read_to_string(source_path)
225+
let content = tokio::fs::read_to_string(&source_path)
198226
.await
199-
.with_context(|| format!("Failed to read source file: {}", source_path.display()))?;
227+
.with_context(|| {
228+
format!(
229+
"Source file '{}' (from header) not found. Has it been moved or deleted?",
230+
source_path.display()
231+
)
232+
})?;
200233

201234
let (front_matter, markdown_body) = parse_markdown(&content)?;
202235

@@ -207,21 +240,18 @@ pub async fn check_pipeline(source_path: &str, pipeline_path: &str) -> Result<()
207240
CompileTarget::Standalone => Box::new(standalone::StandaloneCompiler),
208241
};
209242

243+
// Pass the header's relative source path to compile so the generated
244+
// header embeds the same path that was used during the original compilation.
210245
let pipeline_yaml = compiler
211-
.compile(source_path, pipeline_path, &front_matter, &markdown_body)
246+
.compile(
247+
Path::new(&header_meta.source),
248+
pipeline_path,
249+
&front_matter,
250+
&markdown_body,
251+
)
212252
.await?;
213253
let pipeline_yaml = clean_generated_yaml(&pipeline_yaml);
214254

215-
// Read existing pipeline file
216-
let existing = tokio::fs::read_to_string(pipeline_path)
217-
.await
218-
.with_context(|| {
219-
format!(
220-
"Failed to read pipeline file: {}",
221-
pipeline_path.display()
222-
)
223-
})?;
224-
225255
// Compare ignoring whitespace differences
226256
if normalize_whitespace(&pipeline_yaml) != normalize_whitespace(&existing) {
227257
anyhow::bail!(

src/main.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,7 @@ enum Commands {
4040
},
4141
/// Check that a compiled pipeline matches its source markdown
4242
Check {
43-
/// Path to the source markdown file
44-
source: String,
45-
/// Path to the pipeline YAML file to verify
43+
/// Path to the pipeline YAML file to verify (source auto-detected from header)
4644
pipeline: String,
4745
},
4846
/// Run as an MCP server
@@ -158,8 +156,8 @@ async fn main() -> Result<()> {
158156
compile::compile_all_pipelines().await?
159157
}
160158
},
161-
Commands::Check { source, pipeline } => {
162-
compile::check_pipeline(&source, &pipeline).await?;
159+
Commands::Check { pipeline } => {
160+
compile::check_pipeline(&pipeline).await?;
163161
}
164162
Commands::Mcp {
165163
output_directory,

templates/1es-base.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ extends:
7575
- bash: |
7676
AGENTIC_PIPELINES_PATH="$(Pipeline.Workspace)/agentic-pipeline-compiler/ado-aw"
7777
chmod +x "$AGENTIC_PIPELINES_PATH"
78-
$AGENTIC_PIPELINES_PATH check "{{ source_path }}" "{{ pipeline_path }}"
78+
$AGENTIC_PIPELINES_PATH check "{{ pipeline_path }}"
7979
displayName: "Verify pipeline integrity"
8080
8181
- bash: |

templates/base.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ jobs:
7373
- bash: |
7474
AGENTIC_PIPELINES_PATH="$(Pipeline.Workspace)/agentic-pipeline-compiler/ado-aw"
7575
chmod +x "$AGENTIC_PIPELINES_PATH"
76-
$AGENTIC_PIPELINES_PATH check "{{ source_path }}" "{{ pipeline_path }}"
76+
$AGENTIC_PIPELINES_PATH check "{{ pipeline_path }}"
7777
displayName: "Verify pipeline integrity"
7878
7979
- bash: |

0 commit comments

Comments
 (0)