Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ Every compiled pipeline runs as three sequential jobs:
│ ├── mcp.rs # SafeOutputs MCP server (stdio + HTTP)
│ ├── configure.rs # `configure` CLI command — orchestration shim atop `src/ado/`
│ ├── enable.rs # `enable` CLI command — registers ADO build definitions for compiled pipelines and ensures they are enabled
│ ├── list.rs # `list` CLI command — renders matched ADO definitions with their latest-run state (text or JSON)
│ ├── ado/ # Shared Azure DevOps REST helpers (auth, list/match/PATCH/POST)
│ │ └── mod.rs # Used by `configure` and the lifecycle commands (enable, disable, remove, list, run, status, secrets)
│ ├── detect.rs # Agentic pipeline detection (helper for `configure`)
Expand Down
7 changes: 7 additions & 0 deletions docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,10 @@ Global flags (apply to all subcommands): `--verbose, -v` (enable info-level logg
- `--token <value>` - The token value for `--also-set-token`. Falls back to `$GITHUB_TOKEN`, then to an interactive prompt. Requires `--also-set-token`.

**Source-repo scope (Phase 1):** `enable` requires the local git remote to be an Azure DevOps Git remote (the source repo is what gets registered as the definition's repository). GitHub-hosted source repos are gated on a follow-up.

- `list [PATH]` - Render every ADO build definition that matches a local fixture (under `PATH`) along with its `queueStatus`, ADO folder, and latest-run summary. Pass `--all` to also include definitions with no matching local fixture. Output defaults to a human-readable table; `--json` emits a stable JSON array suitable for scripting.
- `--org <url>` - Override: Azure DevOps organization (URL or bare org name). Inferred from git remote by default.
- `--project <name>` - Override: Azure DevOps project name (inferred from git remote by default).
- `--pat <pat>` / `AZURE_DEVOPS_EXT_PAT` env var - PAT for ADO API authentication (Azure CLI fallback if omitted).
- `--all` - Include ADO definitions that do not match any local fixture.
- `--json` - Emit machine-readable JSON.
51 changes: 46 additions & 5 deletions src/ado/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,10 @@ pub struct DefinitionSummary {
/// [`list_definitions`]). Older/cached responses may omit it.
#[serde(rename = "queueStatus")]
pub queue_status: Option<String>,
/// ADO folder path (e.g. `\smoke`, `\`). Populated when
/// `includeAllProperties=true`. May be absent on older API versions.
#[serde(default)]
pub path: Option<String>,
}

#[derive(Debug, Deserialize)]
Expand Down Expand Up @@ -1065,12 +1069,47 @@ pub async fn get_build(
/// Calls `GET /_apis/build/builds?definitions={id}&$top=1&api-version=7.1`
/// and returns the first result (or `None` if the definition has never run).
pub async fn get_latest_build(
_client: &reqwest::Client,
_ctx: &AdoContext,
_auth: &AdoAuth,
_definition_id: u64,
client: &reqwest::Client,
ctx: &AdoContext,
auth: &AdoAuth,
definition_id: u64,
) -> Result<Option<serde_json::Value>> {
anyhow::bail!("not yet implemented: filled in by PR 5 (ado-aw list) or PR 7 (ado-aw status)")
let url = format!(
"{}/{}/_apis/build/builds?definitions={}&$top=1&api-version=7.1",
ctx.org_url.trim_end_matches('/'),
ctx.project,
definition_id,
);

debug!("GET latest build for definition {}: {}", definition_id, url);

let resp = auth
.apply(client.get(&url))
.send()
.await
.with_context(|| format!("Failed to fetch latest build for definition {}", definition_id))?;

let status = resp.status();
if !status.is_success() {
let body = resp.text().await.unwrap_or_default();
anyhow::bail!(
"ADO API returned {} when fetching latest build for definition {}: {}",
status,
definition_id,
body
);
}

let body: serde_json::Value = resp
.json()
.await
.with_context(|| format!("Failed to parse builds response for {}", definition_id))?;

Ok(body
.get("value")
.and_then(|v| v.as_array())
.and_then(|a| a.first())
.cloned())
}

#[cfg(test)]
Expand Down Expand Up @@ -1185,6 +1224,7 @@ mod tests {
name: name.to_string(),
process: None,
queue_status: None,
path: None,
}
}

Expand All @@ -1196,6 +1236,7 @@ mod tests {
yaml_filename: Some(yaml_filename.to_string()),
}),
queue_status: None,
path: None,
}
}

Expand Down
1 change: 1 addition & 0 deletions src/enable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,7 @@ mod tests {
yaml_filename: Some(y.to_string()),
}),
queue_status: status.map(str::to_string),
path: None,
}
}

Expand Down
Loading