Skip to content

Commit 6c1a332

Browse files
feat(cli): make init always overwrite; --force bypasses GitHub remote guard (#465)
* feat(cli): exempt githubnext/ado-aw repo from GitHub remote guard Agent-Logs-Url: https://github.com/githubnext/ado-aw/sessions/e8a46b21-15ab-4b8c-abe5-215eff7bd7fe Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com> * feat(cli): allow `init --force` to bypass GitHub remote guard Agent-Logs-Url: https://github.com/githubnext/ado-aw/sessions/894f7a19-183c-4f52-abe7-3fff415b43ed Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com> * feat(cli): make init always overwrite; --force only bypasses GitHub guard Agent-Logs-Url: https://github.com/githubnext/ado-aw/sessions/0ad2fb85-cc7d-49fe-aae4-e43a542f5e08 Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com> --------- 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 64d8aaa commit 6c1a332

4 files changed

Lines changed: 34 additions & 20 deletions

File tree

docs/cli.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Global flags (apply to all subcommands): `--verbose, -v` (enable info-level logg
88

99
- `init` - Initialize a repository for AI-first agentic pipeline authoring
1010
- `--path <path>` - Target directory (defaults to current directory)
11-
- `--force` - Overwrite existing agent file
11+
- `--force` - Bypass the GitHub-remote guard (use when running inside a GitHub-hosted repository like `githubnext/ado-aw` itself)
1212
- Creates `.github/agents/ado-aw.agent.md` — a Copilot dispatcher agent that routes to specialized prompts for creating, updating, and debugging agentic pipelines
1313
- The agent auto-downloads the ado-aw compiler and handles the full lifecycle (create → compile → check)
1414
- `compile [<path>]` - Compile a markdown file to Azure DevOps pipeline YAML. If no path is given, auto-discovers and recompiles all detected agentic pipelines in the current directory.

src/init.rs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,13 @@ const AGENT_TEMPLATE: &str = include_str!("data/init-agent.md");
77
const AGENT_DIR: &str = ".github/agents";
88
const AGENT_FILENAME: &str = "ado-aw.agent.md";
99

10-
pub async fn run(path: Option<&std::path::Path>, force: bool) -> Result<()> {
10+
pub async fn run(path: Option<&std::path::Path>) -> Result<()> {
1111
let base = path.map(PathBuf::from).unwrap_or_else(|| PathBuf::from("."));
1212
let agent_dir = base.join(AGENT_DIR);
1313
let agent_path = agent_dir.join(AGENT_FILENAME);
1414

15-
// Check if file already exists
16-
if agent_path.exists() && !force {
17-
anyhow::bail!(
18-
"{} already exists. Use --force to overwrite.",
19-
agent_path.display()
20-
);
21-
}
15+
// `init` always (re)writes the agent file so it stays in sync with the
16+
// currently installed compiler version.
2217

2318
// Create directory structure
2419
tokio::fs::create_dir_all(&agent_dir)

src/main.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,8 @@ enum Commands {
104104
/// Target directory (defaults to current directory)
105105
#[arg(long)]
106106
path: Option<PathBuf>,
107-
/// Overwrite existing agent file
107+
/// Bypass the GitHub-remote guard (use when running inside a
108+
/// GitHub-hosted repository like `githubnext/ado-aw` itself)
108109
#[arg(long)]
109110
force: bool,
110111
},
@@ -449,8 +450,13 @@ async fn main() -> Result<()> {
449450
}
450451
Commands::Init { path, force } => {
451452
let init_path = path.as_deref().unwrap_or(Path::new("."));
452-
ensure_non_github_remote_for_ado_aw("init", init_path).await?;
453-
init::run(path.as_deref(), force).await?;
453+
// `--force` bypasses the GitHub-remote guard so maintainers can
454+
// run `ado-aw init` inside this repository (or other GitHub-hosted
455+
// forks) for development and example regeneration.
456+
if !force {
457+
ensure_non_github_remote_for_ado_aw("init", init_path).await?;
458+
}
459+
init::run(path.as_deref()).await?;
454460
}
455461
Commands::Configure {
456462
token,

tests/init_tests.rs

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ fn test_init_creates_agent_file() {
3232
);
3333
}
3434

35-
/// Test that `init` refuses to overwrite without --force
35+
/// Test that `init` always overwrites an existing agent file (no --force needed)
3636
#[test]
37-
fn test_init_refuses_overwrite_without_force() {
37+
fn test_init_overwrites_by_default() {
3838
let temp_dir = tempfile::tempdir().expect("Failed to create temp directory");
3939

4040
// First run should succeed
@@ -44,21 +44,34 @@ fn test_init_refuses_overwrite_without_force() {
4444
.expect("Failed to run ado-aw init");
4545
assert!(output.status.success(), "First init should succeed");
4646

47-
// Second run without --force should fail
47+
let agent_path = temp_dir.path().join(".github/agents/ado-aw.agent.md");
48+
49+
// Tamper with the file
50+
fs::write(&agent_path, "tampered content").expect("Should write tampered content");
51+
52+
// Second run without --force should still succeed and restore the template
4853
let output = ado_aw_bin()
4954
.args(["init", "--path", temp_dir.path().to_str().unwrap()])
5055
.output()
5156
.expect("Failed to run ado-aw init");
52-
assert!(!output.status.success(), "Second init without --force should fail");
57+
assert!(
58+
output.status.success(),
59+
"Second init should succeed and overwrite: {}",
60+
String::from_utf8_lossy(&output.stderr)
61+
);
5362

54-
let stderr = String::from_utf8_lossy(&output.stderr);
63+
let content = fs::read_to_string(&agent_path).expect("Should read agent file");
64+
assert!(
65+
content.contains("ADO Agentic Pipelines Agent"),
66+
"Default init should restore the template content"
67+
);
5568
assert!(
56-
stderr.contains("already exists"),
57-
"Error should mention file already exists: {stderr}"
69+
!content.contains("tampered"),
70+
"Tampered content should be overwritten"
5871
);
5972
}
6073

61-
/// Test that `init --force` overwrites an existing agent file
74+
/// Test that `init --force` also overwrites an existing agent file
6275
#[test]
6376
fn test_init_force_overwrites() {
6477
let temp_dir = tempfile::tempdir().expect("Failed to create temp directory");

0 commit comments

Comments
 (0)