diff --git a/.github/workflows/cowork-auto-pr.yml b/.github/workflows/cowork-auto-pr.yml index 91690e6..6552472 100755 --- a/.github/workflows/cowork-auto-pr.yml +++ b/.github/workflows/cowork-auto-pr.yml @@ -19,10 +19,14 @@ jobs: set -eu existing=$(gh pr list --repo "$GITHUB_REPOSITORY" --head "$GITHUB_REF_NAME" --state open --json number --jq 'length') if [ "$existing" = "0" ]; then + # The default GITHUB_TOKEN may lack pull-requests:write in some org + # configurations. Fail gracefully so the CI job stays green; the PR + # can be opened externally (e.g. by the orchestrator agent). gh pr create --repo "$GITHUB_REPOSITORY" \ --head "$GITHUB_REF_NAME" \ --title "cowork-bot: automated improvements ($GITHUB_REF_NAME)" \ - --body "Automated improvement PR from the Cowork repo-improver rotation (one coherent senior-dev improvement per run; see individual commit messages). Subsequent runs push additional commits to this PR rather than opening new ones." + --body "Automated improvement PR from the Cowork repo-improver rotation (one coherent senior-dev improvement per run; see individual commit messages). Subsequent runs push additional commits to this PR rather than opening new ones." \ + || echo "::warning::gh pr create failed (likely token permission). Open the PR manually or via an external agent." else echo "Open PR already exists for $GITHUB_REF_NAME — nothing to do." fi diff --git a/src/configdrift/cli.py b/src/configdrift/cli.py index a7f499f..cb471ff 100644 --- a/src/configdrift/cli.py +++ b/src/configdrift/cli.py @@ -267,7 +267,7 @@ def scan( # Use directory basenames as env names dir_mapping = {} for d in dirs: - env_name = Path(d).stem + env_name = Path(d).name dir_mapping[env_name] = d else: console.print( diff --git a/tests/test_cli.py b/tests/test_cli.py index 3f515e1..4c4667c 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -405,6 +405,40 @@ def test_scan_env_and_toml_dirs(self): data = json.loads(result.stdout) assert "prod" in data + def test_scan_dir_names_with_dots_preserved(self): + """Dir names containing dots must not be truncated by Path.stem.""" + with tempfile.TemporaryDirectory() as tmpdir: + dev_dir = Path(tmpdir) / "dev" + prod_us = Path(tmpdir) / "prod.us" + prod_eu = Path(tmpdir) / "prod.eu" + dev_dir.mkdir() + prod_us.mkdir() + prod_eu.mkdir() + (dev_dir / "c.yaml").write_text(yaml.dump({"host": "localhost"})) + (prod_us / "c.yaml").write_text(yaml.dump({"host": "us.example.com"})) + (prod_eu / "c.yaml").write_text(yaml.dump({"host": "eu.example.com"})) + + # Table output: all 3 envs must appear as distinct columns + result = runner.invoke( + app, + ["scan", str(dev_dir), str(prod_us), str(prod_eu)], + ) + assert result.exit_code == 0, f"STDOUT: {result.stdout}" + assert "dev →" in result.stdout, "Baseline 'dev' should appear in table" + assert "prod.us" in result.stdout, f"Missing prod.us in output:\n{result.stdout}" + assert "prod.eu" in result.stdout, f"Missing prod.eu in output:\n{result.stdout}" + + # JSON output: both targets must be separate keys (not collapsed) + result2 = runner.invoke( + app, + ["scan", str(dev_dir), str(prod_us), str(prod_eu), "--output", "json"], + ) + assert result2.exit_code == 0, f"STDOUT: {result2.stdout}" + data = json.loads(result2.stdout) + assert "prod.us" in data, f"Missing prod.us in {list(data.keys())}" + assert "prod.eu" in data, f"Missing prod.eu in {list(data.keys())}" + assert len(data) == 2, f"Expected 2 targets, got {len(data)}: {list(data.keys())}" + def test_scan_no_changes_env_skipped_in_table(self): """Scan with multiple envs where one has no changes.""" with tempfile.TemporaryDirectory() as tmpdir: