bmsdna-devtools 0.43.1__tar.gz → 0.45.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bmsdna_devtools-0.43.1/README.md → bmsdna_devtools-0.45.0/PKG-INFO +108 -55
- bmsdna_devtools-0.43.1/PKG-INFO → bmsdna_devtools-0.45.0/README.md +90 -72
- bmsdna_devtools-0.45.0/bmsdna/devtools/ado_auth.py +28 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/app_service_logs.py +38 -16
- bmsdna_devtools-0.45.0/bmsdna/devtools/azure_arm.py +98 -0
- bmsdna_devtools-0.45.0/bmsdna/devtools/azure_auth.py +30 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/bdt_config.py +5 -1
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/cli.py +121 -101
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/cli_tools.py +5 -8
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/find_repo.py +87 -46
- bmsdna_devtools-0.45.0/bmsdna/devtools/gh_args.py +72 -0
- bmsdna_devtools-0.45.0/bmsdna/devtools/gh_issue.py +719 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/gh_pr.py +421 -175
- bmsdna_devtools-0.45.0/bmsdna/devtools/github_api.py +180 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/issue_do.py +4 -6
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/logs.py +39 -45
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/pr_build.py +206 -37
- bmsdna_devtools-0.45.0/bmsdna/devtools/pr_create_args.py +281 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/worktree.py +40 -1
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/pyproject.toml +2 -1
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/skills/bmsdna-devtools/SKILL.md +16 -15
- bmsdna_devtools-0.45.0/tests/fake_github.py +78 -0
- bmsdna_devtools-0.45.0/tests/test_ado_auth.py +71 -0
- bmsdna_devtools-0.45.0/tests/test_app_service_logs.py +152 -0
- bmsdna_devtools-0.45.0/tests/test_azure_arm.py +136 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/tests/test_bdt_config.py +21 -0
- bmsdna_devtools-0.45.0/tests/test_cli_pr_create.py +461 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/tests/test_cli_pr_id.py +50 -1
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/tests/test_find_repo.py +218 -104
- bmsdna_devtools-0.45.0/tests/test_gh_issue.py +722 -0
- bmsdna_devtools-0.45.0/tests/test_gh_pr.py +1320 -0
- bmsdna_devtools-0.45.0/tests/test_github_api.py +180 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/tests/test_issue_do.py +7 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/tests/test_issue_for_me.py +52 -52
- bmsdna_devtools-0.45.0/tests/test_logs.py +162 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/tests/test_pr_build.py +4 -13
- bmsdna_devtools-0.45.0/tests/test_pr_build_create_options.py +166 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/tests/test_pr_build_flow.py +46 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/tests/test_pr_choice.py +42 -27
- bmsdna_devtools-0.45.0/tests/test_pr_create_args.py +152 -0
- bmsdna_devtools-0.45.0/tests/test_pr_description_not_truncated.py +200 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/tests/test_pr_info.py +21 -16
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/tests/test_worktree_clean.py +66 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/uv.lock +203 -1
- bmsdna_devtools-0.43.1/bmsdna/devtools/ado_auth.py +0 -44
- bmsdna_devtools-0.43.1/bmsdna/devtools/gh_issue.py +0 -541
- bmsdna_devtools-0.43.1/tests/test_ado_auth.py +0 -62
- bmsdna_devtools-0.43.1/tests/test_app_service_logs.py +0 -39
- bmsdna_devtools-0.43.1/tests/test_cli_pr_create.py +0 -569
- bmsdna_devtools-0.43.1/tests/test_gh_issue.py +0 -611
- bmsdna_devtools-0.43.1/tests/test_gh_pr.py +0 -1072
- bmsdna_devtools-0.43.1/tests/test_pr_description_not_truncated.py +0 -138
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/.github/workflows/auto-release.yml +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/.github/workflows/python-publish.yml +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/.github/workflows/python-test.yml +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/.gitignore +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/.python-version +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/__init__.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/_api_dump.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/ado_issue.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/agent_mentions.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/api_usage.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/commit.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/dead_code.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/env_config.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/env_files.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/find_injection.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/gitrepo.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/injection_frontend.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/injection_python.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/issue_show.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/issue_watch.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/lint.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/lint_findings.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/lint_models.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/lint_sql.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/lint_sql_files.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/lint_tooling.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/lint_typescript.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/pr_choice.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/pr_info.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/pr_issue_link.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/pr_labels.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/pr_markdown.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/pull.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/testdb.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/translate.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/usage/__init__.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/usage/cli.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/usage/pipeline.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/usage/pricing.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/usage/report.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/usage/sql/00_schema.sql +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/usage/sql/01_ingest_claude.sql +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/usage/sql/01_ingest_codex.sql +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/usage/sql/02_messages.sql +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/usage/sql/03_markers.sql +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/usage/sql/04_facts.sql +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/bmsdna/devtools/usage/sql/05_views.sql +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/justfile +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/tests/conftest.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/tests/test_ado_issue.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/tests/test_ado_issue_flow.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/tests/test_agent_mentions.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/tests/test_api_usage.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/tests/test_cli_cleanup.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/tests/test_cli_commit.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/tests/test_cli_issue.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/tests/test_cli_pull.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/tests/test_cli_tools.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/tests/test_cli_version.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/tests/test_commit.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/tests/test_env_config.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/tests/test_env_files.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/tests/test_find_injection.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/tests/test_gitrepo.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/tests/test_issue_show.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/tests/test_issue_watch.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/tests/test_lint.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/tests/test_lint_models.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/tests/test_lint_sql.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/tests/test_lint_sql_files.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/tests/test_lint_tooling.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/tests/test_lint_typescript.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/tests/test_pr_issue_link.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/tests/test_pr_labels.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/tests/test_pr_markdown.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/tests/test_pull.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/tests/test_testdb.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/tests/test_translate.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/tests/test_usage.py +0 -0
- {bmsdna_devtools-0.43.1 → bmsdna_devtools-0.45.0}/tests/test_wait_timeout.py +0 -0
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: bmsdna-devtools
|
|
3
|
+
Version: 0.45.0
|
|
4
|
+
Summary: Shared Azure DevOps / GitHub / git / Azure Monitor developer tooling for BMS projects
|
|
5
|
+
Requires-Python: >=3.14
|
|
6
|
+
Requires-Dist: azure-identity>=1.19.0
|
|
7
|
+
Requires-Dist: pgdevkit[db]>=0.7.1
|
|
8
|
+
Requires-Dist: requests>=2.32.0
|
|
9
|
+
Requires-Dist: rich>=13.0.0
|
|
10
|
+
Requires-Dist: tomli-w>=1.0.0
|
|
11
|
+
Requires-Dist: typer>=0.26.7
|
|
12
|
+
Provides-Extra: lint
|
|
13
|
+
Requires-Dist: sqlglot>=27.0.0; extra == 'lint'
|
|
14
|
+
Provides-Extra: usage
|
|
15
|
+
Requires-Dist: duckdb>=1.3.0; extra == 'usage'
|
|
16
|
+
Requires-Dist: plotly>=5.24.0; extra == 'usage'
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
1
19
|
# bmsdna-devtools
|
|
2
20
|
|
|
3
21
|
Shared developer tooling for BMS projects: PR build/check status and a
|
|
@@ -8,16 +26,24 @@ postgres/psycopg (and arrowbricks) SQL rules, pydantic-model placement, hand-wir
|
|
|
8
26
|
TypeScript and baseline tooling, plus `bdt dead-code` for `.sql` files nothing loads and
|
|
9
27
|
backend routes nothing calls.
|
|
10
28
|
`bdt pr *` and `bdt issue *` auto-detect whether the current repo's `origin`
|
|
11
|
-
remote is Azure DevOps or GitHub and
|
|
29
|
+
remote is Azure DevOps or GitHub and call the matching REST API.
|
|
12
30
|
Consolidates near-duplicate scripts that used to be copy-pasted across
|
|
13
31
|
OneSales, ccmt2, and MDMApp into one versioned package with a `bdt` CLI.
|
|
14
32
|
|
|
15
|
-
Requires `git`
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
`
|
|
33
|
+
Requires only `git` on PATH (plus `psql` for `bdt testdb`); the `az` and `gh` CLIs are
|
|
34
|
+
**not** needed — everything talks to GitHub (REST + GraphQL), Azure DevOps and Azure
|
|
35
|
+
(ARM / Application Insights) over raw HTTP (`requests`).
|
|
36
|
+
|
|
37
|
+
Auth:
|
|
38
|
+
- **GitHub**: the `GH_TOKEN` / `GITHUB_TOKEN` environment variable, else (if `gh` happens to be
|
|
39
|
+
installed) the output of `gh auth token`. The token needs the `repo` scope, plus `project` for
|
|
40
|
+
boards and `workflow` for reruns.
|
|
41
|
+
- **Azure DevOps**: `--pat` / `AZURE_DEVOPS_EXT_PAT` / `AZURE_DEVOPS_PAT`, else azure-identity's
|
|
42
|
+
`DefaultAzureCredential` (environment variables, managed identity, a cached `az login`, ...).
|
|
43
|
+
- **Azure (`bdt logs *`)**: the same `DefaultAzureCredential`, against ARM / Application Insights;
|
|
44
|
+
`AZURE_SUBSCRIPTION_ID` selects the subscription, else the only enabled one is used.
|
|
45
|
+
|
|
46
|
+
Works on Windows: output is decoded as UTF-8 rather than
|
|
21
47
|
relying on the console's default codepage, and file arguments accept either
|
|
22
48
|
slash style.
|
|
23
49
|
|
|
@@ -76,17 +102,16 @@ polling forever (a failed policy lookup is ignored and the wait continues).
|
|
|
76
102
|
**Azure DevOps**: org/project/repo are auto-detected from
|
|
77
103
|
`git remote get-url origin` (handles SSH, `dev.azure.com` HTTPS, and
|
|
78
104
|
`*.visualstudio.com` HTTPS forms). Auth is an explicit PAT (`--pat` or
|
|
79
|
-
`AZURE_DEVOPS_EXT_PAT`/`AZURE_DEVOPS_PAT` env var), falling back to a short-lived token from
|
|
80
|
-
caller's own `az login` — never embed a PAT literal in a script or CI file.
|
|
105
|
+
`AZURE_DEVOPS_EXT_PAT`/`AZURE_DEVOPS_PAT` env var), falling back to a short-lived token from
|
|
106
|
+
azure-identity's `DefaultAzureCredential` (e.g. the caller's own `az login`) — never embed a PAT literal in a script or CI file.
|
|
81
107
|
`--target-branch` optionally restricts the lookup to PRs into that branch (default: any).
|
|
82
108
|
|
|
83
|
-
**GitHub**: uses `
|
|
84
|
-
PR opened from the current branch (
|
|
85
|
-
`--target-branch` to that base branch if given; with none open,
|
|
86
|
-
|
|
87
|
-
is shown in the output. Check status is computed from
|
|
88
|
-
`
|
|
89
|
-
since the latter flag isn't available in all `gh` releases.
|
|
109
|
+
**GitHub**: uses the `GH_TOKEN`/`GITHUB_TOKEN` auth above and resolves the open
|
|
110
|
+
PR opened from the current branch (REST pulls filtered by head, narrowed by
|
|
111
|
+
`--target-branch` to that base branch if given; with none open, a merged/closed one
|
|
112
|
+
is looked up); the PR's actual base branch
|
|
113
|
+
is shown in the output. Check status is computed from the PR's
|
|
114
|
+
`statusCheckRollup`, fetched via GraphQL.
|
|
90
115
|
|
|
91
116
|
Pass `--pr-id <number>` to check a specific PR directly instead of resolving
|
|
92
117
|
it from the current branch — useful when that PR's branch isn't checked out
|
|
@@ -135,7 +160,7 @@ anything depending on them) in place — distinct from queuing a brand new
|
|
|
135
160
|
build. One retry call per pipeline that has a failed build for the PR.
|
|
136
161
|
`--target-branch` selects which PR to look at, same as `bdt pr status`.
|
|
137
162
|
|
|
138
|
-
**GitHub**:
|
|
163
|
+
**GitHub**: POSTs `actions/runs/<run-id>/rerun-failed-jobs`, which reruns only the
|
|
139
164
|
failed job(s) (and their dependents) of a workflow run — the run ID(s) are
|
|
140
165
|
found automatically from the PR's failing checks. Checks that aren't backed
|
|
141
166
|
by a GitHub Actions run (e.g. a legacy commit status from an external CI)
|
|
@@ -166,10 +191,10 @@ skipped rather than blocking or crashing `pr status`.
|
|
|
166
191
|
`pr status` uses, just queried by `branchName=refs/heads/<target-branch>`
|
|
167
192
|
instead of the PR's merge/source ref.
|
|
168
193
|
|
|
169
|
-
**GitHub**:
|
|
194
|
+
**GitHub**: lists workflow runs via REST filtered by `branch=<target-branch>&event=push`, which
|
|
170
195
|
selects workflow runs triggered by a push to that branch — as opposed to a
|
|
171
196
|
`pull_request`-triggered run for some still-open PR targeting the same
|
|
172
|
-
branch. Failed steps are printed
|
|
197
|
+
branch. Failed steps are printed as the tail of each failed job's log.
|
|
173
198
|
|
|
174
199
|
## `bdt pr create`
|
|
175
200
|
|
|
@@ -178,17 +203,34 @@ bdt pr create --target dev # or --target main / test
|
|
|
178
203
|
```
|
|
179
204
|
|
|
180
205
|
Creates a PR from the current branch into `--target`. On Azure DevOps,
|
|
181
|
-
a
|
|
182
|
-
`
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
`
|
|
206
|
+
a REST POST to `pullrequests` (org/project/repo inferred from the git remote). On GitHub,
|
|
207
|
+
a REST POST to `pulls`; title/body default to the branch's commit(s) so it never blocks on an
|
|
208
|
+
interactive prompt.
|
|
209
|
+
|
|
210
|
+
Extra arguments after `--` take the union of `gh pr create` and `az repos pr create` spellings on **both**
|
|
211
|
+
hosts (`--opt value` and `--opt=value`; list flags take every value up to the next option, comma-separated
|
|
212
|
+
values work too; boolean flags take an optional `true`/`false`):
|
|
213
|
+
|
|
214
|
+
| Meaning | Flags |
|
|
215
|
+
|---|---|
|
|
216
|
+
| title / body | `--title`/`-t`, `--body`/`--description`/`-b` (`-d` on Azure DevOps), `--body-file`/`-F` (`-` = stdin) |
|
|
217
|
+
| branches | `--base`/`-B`/`--target-branch`, `--head`/`-H`/`--source-branch`/`-s` |
|
|
218
|
+
| draft | `--draft [true\|false]` (`-d` on GitHub) |
|
|
219
|
+
| labels / reviewers | `--label`/`--labels`/`-l`, `--reviewer`/`--reviewers` (`-r` on GitHub), `--required-reviewers` |
|
|
220
|
+
| work items / issues | `--work-items` (same as `--issue`: linked and tagged `pr-available`) |
|
|
221
|
+
| merge | `--auto-complete`/`--auto-merge`, `--squash`, `--delete-source-branch`, `--merge-commit-message`, `--bypass-policy`, `--bypass-policy-reason`, `--transition-work-items` |
|
|
222
|
+
| other | `--assignee`/`-a`, `--milestone`/`-m`, `--project`/`-p` (GitHub board), `--fill`/`-f`, `--web`/`-w`/`--open`, `--dry-run` |
|
|
223
|
+
| Azure DevOps target | `--organization`/`--org`, `--project`/`-p`, `--repository`/`-r` override the remote-derived values |
|
|
224
|
+
| accepted, ignored | `--detect`, `--verbose`, `--debug`, `--subscription`, `--output`/`-o`, `--query` |
|
|
225
|
+
|
|
226
|
+
Azure DevOps resolves reviewers (GUID, e-mail or display name) through the Identities API, sets
|
|
227
|
+
`--auto-complete` as the current user and applies the merge options in a follow-up update, as `az` does.
|
|
228
|
+
A flag the host has no counterpart for (e.g. `--milestone` on Azure DevOps, `--delete-source-branch` on
|
|
229
|
+
GitHub) is reported on stderr (`Note: --milestone is ignored on Azure DevOps`), never dropped silently.
|
|
230
|
+
Unknown flags fail with the list of supported ones.
|
|
186
231
|
|
|
187
232
|
Pass the text with `--title "..."` and `--description "$(cat pr.md)"` rather than as passthrough
|
|
188
|
-
args:
|
|
189
|
-
Azure DevOps it is set over the REST API right after `az` creates the PR, because
|
|
190
|
-
`az repos pr create --description` has been seen cutting long text at ~450 characters (a `--title`/
|
|
191
|
-
`--description` given after `--` is intercepted the same way). Azure DevOps itself allows at most
|
|
233
|
+
args: the description is always sent in full. Azure DevOps itself allows at most
|
|
192
234
|
4000 characters in a PR description: a longer one is rejected up front, and a description the
|
|
193
235
|
server stored shorter than sent is reported as a warning (`bdt pr update` does the same check).
|
|
194
236
|
|
|
@@ -198,15 +240,15 @@ it ready for review. Pass `--no-draft` to open it ready for review
|
|
|
198
240
|
immediately instead.
|
|
199
241
|
|
|
200
242
|
`--label` (repeatable) applies labels to the PR on either host: on GitHub
|
|
201
|
-
these
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
create prints the PR's web/GUI link (not just the REST API URL Azure DevOps
|
|
205
|
-
|
|
243
|
+
these are applied to the PR, and unknown labels are created automatically
|
|
244
|
+
by GitHub (no pre-existing label required); on Azure DevOps labels are freeform and get
|
|
245
|
+
created on the fly. A successful
|
|
246
|
+
create prints the PR's web/GUI link (not just the REST API URL Azure DevOps
|
|
247
|
+
returns).
|
|
206
248
|
|
|
207
249
|
`[tool.bdt.pr.required_labels]` in pyproject.toml can require at least one
|
|
208
250
|
label from each named group before the PR is created — checked locally
|
|
209
|
-
(no
|
|
251
|
+
(no API call happens if a group isn't satisfied):
|
|
210
252
|
|
|
211
253
|
```toml
|
|
212
254
|
[tool.bdt.pr.required_labels]
|
|
@@ -241,6 +283,16 @@ create prints a reminder to run `bdt pr status` afterward to check whether
|
|
|
241
283
|
the CI build passes. This is a best-effort check — failures reading policy
|
|
242
284
|
config (auth, permissions) fail open and simply skip the reminder.
|
|
243
285
|
|
|
286
|
+
## `bdt pr comment`
|
|
287
|
+
|
|
288
|
+
```bash
|
|
289
|
+
bdt pr comment --message "Looks good" [--screenshot shot.png] [--file report.pdf] [--pr-id 123]
|
|
290
|
+
bdt pr comment --message-file plan.md # post a file's text as the comment (`-` = stdin)
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
`--file` links the file as an *attachment* (a download link in the comment); it does not post the file's
|
|
294
|
+
content. To post a markdown document (an implementation plan, review findings) use `--message-file`.
|
|
295
|
+
|
|
244
296
|
## `bdt issue create` / `show` / `update` / `delete`, `bdt issue comment add` / `update` / `delete`
|
|
245
297
|
|
|
246
298
|
```bash
|
|
@@ -336,27 +388,26 @@ On `update`, `--board` only moves the item when you pass it explicitly — it
|
|
|
336
388
|
never falls back to `pyproject.toml`, so an unrelated field update (e.g.
|
|
337
389
|
just `--title`) can't silently relocate the item to a different board.
|
|
338
390
|
|
|
339
|
-
**GitHub**:
|
|
340
|
-
|
|
391
|
+
**GitHub**: REST/GraphQL calls for issue create / edit / delete /
|
|
392
|
+
comment. `--label`/`--tag` add a label on `create`, or add/remove one on
|
|
341
393
|
`update` (paired with `--remove-label`/`--remove-tag`); labels must already
|
|
342
394
|
exist in the repo. `--screenshot` pushes images to a `pr-assets` branch (same trick `bdt
|
|
343
395
|
pr create --screenshot` uses, since GitHub has no API for uploading an image
|
|
344
396
|
into an issue) and appends them to the issue body / comment as Markdown.
|
|
345
397
|
`issue delete` is **permanent** — GitHub has no recycle bin for issues.
|
|
346
|
-
Comment update/delete go through
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
`
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
`--
|
|
353
|
-
|
|
398
|
+
Comment update/delete go through REST. Extra arguments
|
|
399
|
+
to `bdt issue create` accept the `gh issue create` flags `--title`/`-t`, `--body`/`-b`, `--body-file`/`-F`
|
|
400
|
+
(`-` = stdin), `--label`/`-l`, `--assignee`/`-a` (`@me` works), `--milestone`/`-m` (title or number),
|
|
401
|
+
`--project`/`-p` (Projects board, repeatable) and `--web`/`-w`; `--template` / `--recover` are rejected. E.g.
|
|
402
|
+
`bdt issue create --title "..." -- --assignee @me --project Roadmap`.
|
|
403
|
+
|
|
404
|
+
`--parent` / `--blocked-by` use GitHub's sub-issues and issue dependencies REST endpoints; `issue show` reads
|
|
405
|
+
the parent, sub-issues, blocked-by and blocking links the same way. `--iteration` isn't supported: a
|
|
354
406
|
GitHub Projects (v2) iteration is a per-project field, not a property of the issue.
|
|
355
407
|
|
|
356
408
|
`--board <name>` on `create` adds the issue to that GitHub Projects (v2)
|
|
357
|
-
board by title (needs a token with the `project` scope
|
|
358
|
-
|
|
359
|
-
currently on that board; since `gh issue list` has no board filter of its
|
|
409
|
+
board by title (needs a token with the `project` scope). On `search`, `--board` (title or number) scopes results to issues
|
|
410
|
+
currently on that board; since the issue list has no board filter of its
|
|
360
411
|
own, this fetches a wider raw pool and filters it down client-side, so a
|
|
361
412
|
board with few matches may return fewer results than `--limit` asks for. A
|
|
362
413
|
CLI flag beats `[tool.bdt.github].board` in `pyproject.toml`:
|
|
@@ -392,7 +443,7 @@ Claims an issue (GitHub) or work item (Azure DevOps): comments `Taken by <you>`
|
|
|
392
443
|
running coding agent's session (the same `<Agent> Session: <id>` note every bdt comment gets: a
|
|
393
444
|
claude.ai link under a bridged Claude session, otherwise the bare session id; nothing outside an
|
|
394
445
|
agent), so others can see who is on it. `<you>` is the GitHub login
|
|
395
|
-
|
|
446
|
+
the token authenticates as, else the git `user.name`. Without `NUMBER` it takes the issue the current
|
|
396
447
|
branch's PR closes (`Fixes #N`, or an issue/work-item URL in the PR body); it refuses to guess if
|
|
397
448
|
that is none or several. If the issue's newest comment already is a "Taken by ..." claim (by anyone) it
|
|
398
449
|
says so and posts nothing, so it's safe to run repeatedly -- including before `bdt issue do`.
|
|
@@ -413,7 +464,7 @@ addresses the calling agent. It combines with every other `search` filter (keywo
|
|
|
413
464
|
the agent that is running `bdt`, or `@Agent`, which addresses every agent. Matching is case-insensitive and
|
|
414
465
|
on whole words: `@claude`, `@CLAUDE,` and `@Claude.` match, `@Claudette`, `@claude-bot` and `me@claude.ai` do not.
|
|
415
466
|
Only the newest comment counts, so once someone (including the agent, e.g. with a reply) comments after the
|
|
416
|
-
mention, the issue drops out of the list. The newest comment isn't part of the list output of
|
|
467
|
+
mention, the issue drops out of the list. The newest comment isn't part of the list output of GitHub/Azure DevOps,
|
|
417
468
|
so this costs one extra request per candidate; candidates are scanned in recently-updated order (up to 10x
|
|
418
469
|
`--limit` on GitHub, 200 on Azure DevOps) until `--limit` have matched.
|
|
419
470
|
|
|
@@ -518,7 +569,9 @@ scan_jinja = ["backend/print"] # "KEY" | tr in .jinja2/.j2/.html
|
|
|
518
569
|
bdt worktree my-feature [--base dev] [--env-file .local_env] [--no-submodules] [--install "just install"]
|
|
519
570
|
```
|
|
520
571
|
|
|
521
|
-
Creates `.worktrees/<name>` branched from `--base
|
|
572
|
+
Creates `.worktrees/<name>` branched from `--base`. It first fetches `origin/<base>` and, if the local
|
|
573
|
+
`<base>` is strictly behind it, branches from `origin/<base>` instead (so the PR starts on current code);
|
|
574
|
+
a local base that is ahead of or has diverged from the remote is kept as is. Then it initializes submodules
|
|
522
575
|
(unless `--no-submodules`), and copies an env file into the new worktree as
|
|
523
576
|
`.env` (auto-detects `.local_env` then `.env` if `--env-file` isn't given).
|
|
524
577
|
|
|
@@ -544,10 +597,10 @@ Configuration (flags override env vars):
|
|
|
544
597
|
| Local work dir | `--root` | `AZDO_WORK_DIR`, `BMS_WORK_DIR` | `~/projects` (`C:/Projects` on Windows) |
|
|
545
598
|
| Azure DevOps org | `--org` | `AZDO_ORG`, `BMS_ORG` | none (ADO not searched) |
|
|
546
599
|
| GitHub org | `--github-org` | `GITHUB_ORG`, `BMS_GITHUB_ORG` | none (GitHub not searched) |
|
|
547
|
-
| ADO PAT for cloning | `--pat` | `AZURE_DEVOPS_EXT_PAT`, `AZURE_DEVOPS_PAT` | falls back to `
|
|
600
|
+
| ADO PAT for cloning | `--pat` | `AZURE_DEVOPS_EXT_PAT`, `AZURE_DEVOPS_PAT` | falls back to `DefaultAzureCredential` |
|
|
548
601
|
|
|
549
602
|
Set the org env vars in your shell profile to make `bdt find-repo <name>` work anywhere. It replaces
|
|
550
|
-
the `cross-repo-discovery` skill's `ALL_REPOS.md` sync. Needs
|
|
603
|
+
the `cross-repo-discovery` skill's `ALL_REPOS.md` sync. Needs ADO auth / a GitHub token only when
|
|
551
604
|
those orgs are searched.
|
|
552
605
|
|
|
553
606
|
## `bdt cleanup worktrees` / `bdt cleanup orphaned-dbs` / `bdt cleanup db` / `bdt cleanup worktree`
|
|
@@ -702,7 +755,7 @@ sandbox mode too.
|
|
|
702
755
|
## `bdt logs roles` / `bdt logs tail`
|
|
703
756
|
|
|
704
757
|
Query Application Insights (KQL over `traces`/`exceptions`) via
|
|
705
|
-
|
|
758
|
+
the Application Insights REST API (azure-identity credential). No defaults are baked in — pass
|
|
706
759
|
`--resource-group`/`--app-insights` explicitly (or set
|
|
707
760
|
`AZURE_RESOURCE_GROUP`/`AZURE_APP_INSIGHTS`), since which Azure resource
|
|
708
761
|
"this repo" maps to isn't derivable from the git remote.
|
|
@@ -715,7 +768,7 @@ bdt logs tail --resource-group my-rg --app-insights my-app-insights --role my-se
|
|
|
715
768
|
## `bdt logs fetch`
|
|
716
769
|
|
|
717
770
|
Downloads the App Service log archive for a webapp/slot via
|
|
718
|
-
|
|
771
|
+
the App Service Kudu/ARM log download, unzips it, and writes every line matching a
|
|
719
772
|
common error/warning marker (`ERROR`, `CRITICAL`, `WARNING`, tracebacks,
|
|
720
773
|
`4xx`/`5xx`, `FAILED`, `FATAL`) to `<out>/<slot>_errors.log`. Simpler and
|
|
721
774
|
often preferable to the KQL commands above when you just want "what broke
|
|
@@ -723,7 +776,7 @@ recently" rather than a queryable trace stream.
|
|
|
723
776
|
|
|
724
777
|
The webapp/resource-group/slot come from a named environment configured in
|
|
725
778
|
the calling repo's `pyproject.toml`. `slot` is optional — omit it for an
|
|
726
|
-
app's default/production slot (no
|
|
779
|
+
app's default/production slot (no slot is used); set it for a
|
|
727
780
|
named deployment slot:
|
|
728
781
|
|
|
729
782
|
```toml
|
|
@@ -1,20 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.5
|
|
2
|
-
Name: bmsdna-devtools
|
|
3
|
-
Version: 0.43.1
|
|
4
|
-
Summary: Shared Azure DevOps / GitHub / git / Azure Monitor developer tooling for BMS projects
|
|
5
|
-
Requires-Python: >=3.14
|
|
6
|
-
Requires-Dist: pgdevkit[db]>=0.7.1
|
|
7
|
-
Requires-Dist: requests>=2.32.0
|
|
8
|
-
Requires-Dist: rich>=13.0.0
|
|
9
|
-
Requires-Dist: tomli-w>=1.0.0
|
|
10
|
-
Requires-Dist: typer>=0.26.7
|
|
11
|
-
Provides-Extra: lint
|
|
12
|
-
Requires-Dist: sqlglot>=27.0.0; extra == 'lint'
|
|
13
|
-
Provides-Extra: usage
|
|
14
|
-
Requires-Dist: duckdb>=1.3.0; extra == 'usage'
|
|
15
|
-
Requires-Dist: plotly>=5.24.0; extra == 'usage'
|
|
16
|
-
Description-Content-Type: text/markdown
|
|
17
|
-
|
|
18
1
|
# bmsdna-devtools
|
|
19
2
|
|
|
20
3
|
Shared developer tooling for BMS projects: PR build/check status and a
|
|
@@ -25,16 +8,24 @@ postgres/psycopg (and arrowbricks) SQL rules, pydantic-model placement, hand-wir
|
|
|
25
8
|
TypeScript and baseline tooling, plus `bdt dead-code` for `.sql` files nothing loads and
|
|
26
9
|
backend routes nothing calls.
|
|
27
10
|
`bdt pr *` and `bdt issue *` auto-detect whether the current repo's `origin`
|
|
28
|
-
remote is Azure DevOps or GitHub and
|
|
11
|
+
remote is Azure DevOps or GitHub and call the matching REST API.
|
|
29
12
|
Consolidates near-duplicate scripts that used to be copy-pasted across
|
|
30
13
|
OneSales, ccmt2, and MDMApp into one versioned package with a `bdt` CLI.
|
|
31
14
|
|
|
32
|
-
Requires `git`
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
`
|
|
15
|
+
Requires only `git` on PATH (plus `psql` for `bdt testdb`); the `az` and `gh` CLIs are
|
|
16
|
+
**not** needed — everything talks to GitHub (REST + GraphQL), Azure DevOps and Azure
|
|
17
|
+
(ARM / Application Insights) over raw HTTP (`requests`).
|
|
18
|
+
|
|
19
|
+
Auth:
|
|
20
|
+
- **GitHub**: the `GH_TOKEN` / `GITHUB_TOKEN` environment variable, else (if `gh` happens to be
|
|
21
|
+
installed) the output of `gh auth token`. The token needs the `repo` scope, plus `project` for
|
|
22
|
+
boards and `workflow` for reruns.
|
|
23
|
+
- **Azure DevOps**: `--pat` / `AZURE_DEVOPS_EXT_PAT` / `AZURE_DEVOPS_PAT`, else azure-identity's
|
|
24
|
+
`DefaultAzureCredential` (environment variables, managed identity, a cached `az login`, ...).
|
|
25
|
+
- **Azure (`bdt logs *`)**: the same `DefaultAzureCredential`, against ARM / Application Insights;
|
|
26
|
+
`AZURE_SUBSCRIPTION_ID` selects the subscription, else the only enabled one is used.
|
|
27
|
+
|
|
28
|
+
Works on Windows: output is decoded as UTF-8 rather than
|
|
38
29
|
relying on the console's default codepage, and file arguments accept either
|
|
39
30
|
slash style.
|
|
40
31
|
|
|
@@ -93,17 +84,16 @@ polling forever (a failed policy lookup is ignored and the wait continues).
|
|
|
93
84
|
**Azure DevOps**: org/project/repo are auto-detected from
|
|
94
85
|
`git remote get-url origin` (handles SSH, `dev.azure.com` HTTPS, and
|
|
95
86
|
`*.visualstudio.com` HTTPS forms). Auth is an explicit PAT (`--pat` or
|
|
96
|
-
`AZURE_DEVOPS_EXT_PAT`/`AZURE_DEVOPS_PAT` env var), falling back to a short-lived token from
|
|
97
|
-
caller's own `az login` — never embed a PAT literal in a script or CI file.
|
|
87
|
+
`AZURE_DEVOPS_EXT_PAT`/`AZURE_DEVOPS_PAT` env var), falling back to a short-lived token from
|
|
88
|
+
azure-identity's `DefaultAzureCredential` (e.g. the caller's own `az login`) — never embed a PAT literal in a script or CI file.
|
|
98
89
|
`--target-branch` optionally restricts the lookup to PRs into that branch (default: any).
|
|
99
90
|
|
|
100
|
-
**GitHub**: uses `
|
|
101
|
-
PR opened from the current branch (
|
|
102
|
-
`--target-branch` to that base branch if given; with none open,
|
|
103
|
-
|
|
104
|
-
is shown in the output. Check status is computed from
|
|
105
|
-
`
|
|
106
|
-
since the latter flag isn't available in all `gh` releases.
|
|
91
|
+
**GitHub**: uses the `GH_TOKEN`/`GITHUB_TOKEN` auth above and resolves the open
|
|
92
|
+
PR opened from the current branch (REST pulls filtered by head, narrowed by
|
|
93
|
+
`--target-branch` to that base branch if given; with none open, a merged/closed one
|
|
94
|
+
is looked up); the PR's actual base branch
|
|
95
|
+
is shown in the output. Check status is computed from the PR's
|
|
96
|
+
`statusCheckRollup`, fetched via GraphQL.
|
|
107
97
|
|
|
108
98
|
Pass `--pr-id <number>` to check a specific PR directly instead of resolving
|
|
109
99
|
it from the current branch — useful when that PR's branch isn't checked out
|
|
@@ -152,7 +142,7 @@ anything depending on them) in place — distinct from queuing a brand new
|
|
|
152
142
|
build. One retry call per pipeline that has a failed build for the PR.
|
|
153
143
|
`--target-branch` selects which PR to look at, same as `bdt pr status`.
|
|
154
144
|
|
|
155
|
-
**GitHub**:
|
|
145
|
+
**GitHub**: POSTs `actions/runs/<run-id>/rerun-failed-jobs`, which reruns only the
|
|
156
146
|
failed job(s) (and their dependents) of a workflow run — the run ID(s) are
|
|
157
147
|
found automatically from the PR's failing checks. Checks that aren't backed
|
|
158
148
|
by a GitHub Actions run (e.g. a legacy commit status from an external CI)
|
|
@@ -183,10 +173,10 @@ skipped rather than blocking or crashing `pr status`.
|
|
|
183
173
|
`pr status` uses, just queried by `branchName=refs/heads/<target-branch>`
|
|
184
174
|
instead of the PR's merge/source ref.
|
|
185
175
|
|
|
186
|
-
**GitHub**:
|
|
176
|
+
**GitHub**: lists workflow runs via REST filtered by `branch=<target-branch>&event=push`, which
|
|
187
177
|
selects workflow runs triggered by a push to that branch — as opposed to a
|
|
188
178
|
`pull_request`-triggered run for some still-open PR targeting the same
|
|
189
|
-
branch. Failed steps are printed
|
|
179
|
+
branch. Failed steps are printed as the tail of each failed job's log.
|
|
190
180
|
|
|
191
181
|
## `bdt pr create`
|
|
192
182
|
|
|
@@ -195,17 +185,34 @@ bdt pr create --target dev # or --target main / test
|
|
|
195
185
|
```
|
|
196
186
|
|
|
197
187
|
Creates a PR from the current branch into `--target`. On Azure DevOps,
|
|
198
|
-
a
|
|
199
|
-
`
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
`
|
|
188
|
+
a REST POST to `pullrequests` (org/project/repo inferred from the git remote). On GitHub,
|
|
189
|
+
a REST POST to `pulls`; title/body default to the branch's commit(s) so it never blocks on an
|
|
190
|
+
interactive prompt.
|
|
191
|
+
|
|
192
|
+
Extra arguments after `--` take the union of `gh pr create` and `az repos pr create` spellings on **both**
|
|
193
|
+
hosts (`--opt value` and `--opt=value`; list flags take every value up to the next option, comma-separated
|
|
194
|
+
values work too; boolean flags take an optional `true`/`false`):
|
|
195
|
+
|
|
196
|
+
| Meaning | Flags |
|
|
197
|
+
|---|---|
|
|
198
|
+
| title / body | `--title`/`-t`, `--body`/`--description`/`-b` (`-d` on Azure DevOps), `--body-file`/`-F` (`-` = stdin) |
|
|
199
|
+
| branches | `--base`/`-B`/`--target-branch`, `--head`/`-H`/`--source-branch`/`-s` |
|
|
200
|
+
| draft | `--draft [true\|false]` (`-d` on GitHub) |
|
|
201
|
+
| labels / reviewers | `--label`/`--labels`/`-l`, `--reviewer`/`--reviewers` (`-r` on GitHub), `--required-reviewers` |
|
|
202
|
+
| work items / issues | `--work-items` (same as `--issue`: linked and tagged `pr-available`) |
|
|
203
|
+
| merge | `--auto-complete`/`--auto-merge`, `--squash`, `--delete-source-branch`, `--merge-commit-message`, `--bypass-policy`, `--bypass-policy-reason`, `--transition-work-items` |
|
|
204
|
+
| other | `--assignee`/`-a`, `--milestone`/`-m`, `--project`/`-p` (GitHub board), `--fill`/`-f`, `--web`/`-w`/`--open`, `--dry-run` |
|
|
205
|
+
| Azure DevOps target | `--organization`/`--org`, `--project`/`-p`, `--repository`/`-r` override the remote-derived values |
|
|
206
|
+
| accepted, ignored | `--detect`, `--verbose`, `--debug`, `--subscription`, `--output`/`-o`, `--query` |
|
|
207
|
+
|
|
208
|
+
Azure DevOps resolves reviewers (GUID, e-mail or display name) through the Identities API, sets
|
|
209
|
+
`--auto-complete` as the current user and applies the merge options in a follow-up update, as `az` does.
|
|
210
|
+
A flag the host has no counterpart for (e.g. `--milestone` on Azure DevOps, `--delete-source-branch` on
|
|
211
|
+
GitHub) is reported on stderr (`Note: --milestone is ignored on Azure DevOps`), never dropped silently.
|
|
212
|
+
Unknown flags fail with the list of supported ones.
|
|
203
213
|
|
|
204
214
|
Pass the text with `--title "..."` and `--description "$(cat pr.md)"` rather than as passthrough
|
|
205
|
-
args:
|
|
206
|
-
Azure DevOps it is set over the REST API right after `az` creates the PR, because
|
|
207
|
-
`az repos pr create --description` has been seen cutting long text at ~450 characters (a `--title`/
|
|
208
|
-
`--description` given after `--` is intercepted the same way). Azure DevOps itself allows at most
|
|
215
|
+
args: the description is always sent in full. Azure DevOps itself allows at most
|
|
209
216
|
4000 characters in a PR description: a longer one is rejected up front, and a description the
|
|
210
217
|
server stored shorter than sent is reported as a warning (`bdt pr update` does the same check).
|
|
211
218
|
|
|
@@ -215,15 +222,15 @@ it ready for review. Pass `--no-draft` to open it ready for review
|
|
|
215
222
|
immediately instead.
|
|
216
223
|
|
|
217
224
|
`--label` (repeatable) applies labels to the PR on either host: on GitHub
|
|
218
|
-
these
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
create prints the PR's web/GUI link (not just the REST API URL Azure DevOps
|
|
222
|
-
|
|
225
|
+
these are applied to the PR, and unknown labels are created automatically
|
|
226
|
+
by GitHub (no pre-existing label required); on Azure DevOps labels are freeform and get
|
|
227
|
+
created on the fly. A successful
|
|
228
|
+
create prints the PR's web/GUI link (not just the REST API URL Azure DevOps
|
|
229
|
+
returns).
|
|
223
230
|
|
|
224
231
|
`[tool.bdt.pr.required_labels]` in pyproject.toml can require at least one
|
|
225
232
|
label from each named group before the PR is created — checked locally
|
|
226
|
-
(no
|
|
233
|
+
(no API call happens if a group isn't satisfied):
|
|
227
234
|
|
|
228
235
|
```toml
|
|
229
236
|
[tool.bdt.pr.required_labels]
|
|
@@ -258,6 +265,16 @@ create prints a reminder to run `bdt pr status` afterward to check whether
|
|
|
258
265
|
the CI build passes. This is a best-effort check — failures reading policy
|
|
259
266
|
config (auth, permissions) fail open and simply skip the reminder.
|
|
260
267
|
|
|
268
|
+
## `bdt pr comment`
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
bdt pr comment --message "Looks good" [--screenshot shot.png] [--file report.pdf] [--pr-id 123]
|
|
272
|
+
bdt pr comment --message-file plan.md # post a file's text as the comment (`-` = stdin)
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
`--file` links the file as an *attachment* (a download link in the comment); it does not post the file's
|
|
276
|
+
content. To post a markdown document (an implementation plan, review findings) use `--message-file`.
|
|
277
|
+
|
|
261
278
|
## `bdt issue create` / `show` / `update` / `delete`, `bdt issue comment add` / `update` / `delete`
|
|
262
279
|
|
|
263
280
|
```bash
|
|
@@ -353,27 +370,26 @@ On `update`, `--board` only moves the item when you pass it explicitly — it
|
|
|
353
370
|
never falls back to `pyproject.toml`, so an unrelated field update (e.g.
|
|
354
371
|
just `--title`) can't silently relocate the item to a different board.
|
|
355
372
|
|
|
356
|
-
**GitHub**:
|
|
357
|
-
|
|
373
|
+
**GitHub**: REST/GraphQL calls for issue create / edit / delete /
|
|
374
|
+
comment. `--label`/`--tag` add a label on `create`, or add/remove one on
|
|
358
375
|
`update` (paired with `--remove-label`/`--remove-tag`); labels must already
|
|
359
376
|
exist in the repo. `--screenshot` pushes images to a `pr-assets` branch (same trick `bdt
|
|
360
377
|
pr create --screenshot` uses, since GitHub has no API for uploading an image
|
|
361
378
|
into an issue) and appends them to the issue body / comment as Markdown.
|
|
362
379
|
`issue delete` is **permanent** — GitHub has no recycle bin for issues.
|
|
363
|
-
Comment update/delete go through
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
`
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
`--
|
|
370
|
-
|
|
380
|
+
Comment update/delete go through REST. Extra arguments
|
|
381
|
+
to `bdt issue create` accept the `gh issue create` flags `--title`/`-t`, `--body`/`-b`, `--body-file`/`-F`
|
|
382
|
+
(`-` = stdin), `--label`/`-l`, `--assignee`/`-a` (`@me` works), `--milestone`/`-m` (title or number),
|
|
383
|
+
`--project`/`-p` (Projects board, repeatable) and `--web`/`-w`; `--template` / `--recover` are rejected. E.g.
|
|
384
|
+
`bdt issue create --title "..." -- --assignee @me --project Roadmap`.
|
|
385
|
+
|
|
386
|
+
`--parent` / `--blocked-by` use GitHub's sub-issues and issue dependencies REST endpoints; `issue show` reads
|
|
387
|
+
the parent, sub-issues, blocked-by and blocking links the same way. `--iteration` isn't supported: a
|
|
371
388
|
GitHub Projects (v2) iteration is a per-project field, not a property of the issue.
|
|
372
389
|
|
|
373
390
|
`--board <name>` on `create` adds the issue to that GitHub Projects (v2)
|
|
374
|
-
board by title (needs a token with the `project` scope
|
|
375
|
-
|
|
376
|
-
currently on that board; since `gh issue list` has no board filter of its
|
|
391
|
+
board by title (needs a token with the `project` scope). On `search`, `--board` (title or number) scopes results to issues
|
|
392
|
+
currently on that board; since the issue list has no board filter of its
|
|
377
393
|
own, this fetches a wider raw pool and filters it down client-side, so a
|
|
378
394
|
board with few matches may return fewer results than `--limit` asks for. A
|
|
379
395
|
CLI flag beats `[tool.bdt.github].board` in `pyproject.toml`:
|
|
@@ -409,7 +425,7 @@ Claims an issue (GitHub) or work item (Azure DevOps): comments `Taken by <you>`
|
|
|
409
425
|
running coding agent's session (the same `<Agent> Session: <id>` note every bdt comment gets: a
|
|
410
426
|
claude.ai link under a bridged Claude session, otherwise the bare session id; nothing outside an
|
|
411
427
|
agent), so others can see who is on it. `<you>` is the GitHub login
|
|
412
|
-
|
|
428
|
+
the token authenticates as, else the git `user.name`. Without `NUMBER` it takes the issue the current
|
|
413
429
|
branch's PR closes (`Fixes #N`, or an issue/work-item URL in the PR body); it refuses to guess if
|
|
414
430
|
that is none or several. If the issue's newest comment already is a "Taken by ..." claim (by anyone) it
|
|
415
431
|
says so and posts nothing, so it's safe to run repeatedly -- including before `bdt issue do`.
|
|
@@ -430,7 +446,7 @@ addresses the calling agent. It combines with every other `search` filter (keywo
|
|
|
430
446
|
the agent that is running `bdt`, or `@Agent`, which addresses every agent. Matching is case-insensitive and
|
|
431
447
|
on whole words: `@claude`, `@CLAUDE,` and `@Claude.` match, `@Claudette`, `@claude-bot` and `me@claude.ai` do not.
|
|
432
448
|
Only the newest comment counts, so once someone (including the agent, e.g. with a reply) comments after the
|
|
433
|
-
mention, the issue drops out of the list. The newest comment isn't part of the list output of
|
|
449
|
+
mention, the issue drops out of the list. The newest comment isn't part of the list output of GitHub/Azure DevOps,
|
|
434
450
|
so this costs one extra request per candidate; candidates are scanned in recently-updated order (up to 10x
|
|
435
451
|
`--limit` on GitHub, 200 on Azure DevOps) until `--limit` have matched.
|
|
436
452
|
|
|
@@ -535,7 +551,9 @@ scan_jinja = ["backend/print"] # "KEY" | tr in .jinja2/.j2/.html
|
|
|
535
551
|
bdt worktree my-feature [--base dev] [--env-file .local_env] [--no-submodules] [--install "just install"]
|
|
536
552
|
```
|
|
537
553
|
|
|
538
|
-
Creates `.worktrees/<name>` branched from `--base
|
|
554
|
+
Creates `.worktrees/<name>` branched from `--base`. It first fetches `origin/<base>` and, if the local
|
|
555
|
+
`<base>` is strictly behind it, branches from `origin/<base>` instead (so the PR starts on current code);
|
|
556
|
+
a local base that is ahead of or has diverged from the remote is kept as is. Then it initializes submodules
|
|
539
557
|
(unless `--no-submodules`), and copies an env file into the new worktree as
|
|
540
558
|
`.env` (auto-detects `.local_env` then `.env` if `--env-file` isn't given).
|
|
541
559
|
|
|
@@ -561,10 +579,10 @@ Configuration (flags override env vars):
|
|
|
561
579
|
| Local work dir | `--root` | `AZDO_WORK_DIR`, `BMS_WORK_DIR` | `~/projects` (`C:/Projects` on Windows) |
|
|
562
580
|
| Azure DevOps org | `--org` | `AZDO_ORG`, `BMS_ORG` | none (ADO not searched) |
|
|
563
581
|
| GitHub org | `--github-org` | `GITHUB_ORG`, `BMS_GITHUB_ORG` | none (GitHub not searched) |
|
|
564
|
-
| ADO PAT for cloning | `--pat` | `AZURE_DEVOPS_EXT_PAT`, `AZURE_DEVOPS_PAT` | falls back to `
|
|
582
|
+
| ADO PAT for cloning | `--pat` | `AZURE_DEVOPS_EXT_PAT`, `AZURE_DEVOPS_PAT` | falls back to `DefaultAzureCredential` |
|
|
565
583
|
|
|
566
584
|
Set the org env vars in your shell profile to make `bdt find-repo <name>` work anywhere. It replaces
|
|
567
|
-
the `cross-repo-discovery` skill's `ALL_REPOS.md` sync. Needs
|
|
585
|
+
the `cross-repo-discovery` skill's `ALL_REPOS.md` sync. Needs ADO auth / a GitHub token only when
|
|
568
586
|
those orgs are searched.
|
|
569
587
|
|
|
570
588
|
## `bdt cleanup worktrees` / `bdt cleanup orphaned-dbs` / `bdt cleanup db` / `bdt cleanup worktree`
|
|
@@ -719,7 +737,7 @@ sandbox mode too.
|
|
|
719
737
|
## `bdt logs roles` / `bdt logs tail`
|
|
720
738
|
|
|
721
739
|
Query Application Insights (KQL over `traces`/`exceptions`) via
|
|
722
|
-
|
|
740
|
+
the Application Insights REST API (azure-identity credential). No defaults are baked in — pass
|
|
723
741
|
`--resource-group`/`--app-insights` explicitly (or set
|
|
724
742
|
`AZURE_RESOURCE_GROUP`/`AZURE_APP_INSIGHTS`), since which Azure resource
|
|
725
743
|
"this repo" maps to isn't derivable from the git remote.
|
|
@@ -732,7 +750,7 @@ bdt logs tail --resource-group my-rg --app-insights my-app-insights --role my-se
|
|
|
732
750
|
## `bdt logs fetch`
|
|
733
751
|
|
|
734
752
|
Downloads the App Service log archive for a webapp/slot via
|
|
735
|
-
|
|
753
|
+
the App Service Kudu/ARM log download, unzips it, and writes every line matching a
|
|
736
754
|
common error/warning marker (`ERROR`, `CRITICAL`, `WARNING`, tracebacks,
|
|
737
755
|
`4xx`/`5xx`, `FAILED`, `FATAL`) to `<out>/<slot>_errors.log`. Simpler and
|
|
738
756
|
often preferable to the KQL commands above when you just want "what broke
|
|
@@ -740,7 +758,7 @@ recently" rather than a queryable trace stream.
|
|
|
740
758
|
|
|
741
759
|
The webapp/resource-group/slot come from a named environment configured in
|
|
742
760
|
the calling repo's `pyproject.toml`. `slot` is optional — omit it for an
|
|
743
|
-
app's default/production slot (no
|
|
761
|
+
app's default/production slot (no slot is used); set it for a
|
|
744
762
|
named deployment slot:
|
|
745
763
|
|
|
746
764
|
```toml
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""Azure DevOps auth: explicit PAT (`--pat` / AZURE_DEVOPS_EXT_PAT / AZURE_DEVOPS_PAT), or a token from
|
|
2
|
+
`DefaultAzureCredential` (see `azure_auth`)."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import base64
|
|
7
|
+
|
|
8
|
+
from .azure_auth import get_azure_token
|
|
9
|
+
|
|
10
|
+
# Well-known Azure DevOps resource ID; `<id>/.default` is its AAD scope.
|
|
11
|
+
ADO_RESOURCE_ID = "499b84ac-1321-427f-aa17-267ca6975798"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def get_az_devops_token() -> str:
|
|
15
|
+
return get_azure_token(f"{ADO_RESOURCE_ID}/.default")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def auth_header(pat: str | None) -> dict[str, str]:
|
|
19
|
+
"""Basic-auth header for an explicit PAT, else Bearer via `DefaultAzureCredential`.
|
|
20
|
+
|
|
21
|
+
Never hardcode a PAT literal in a caller — pass it in from an env var
|
|
22
|
+
(e.g. AZURE_DEVOPS_EXT_PAT) or a CLI flag, or omit it and let azure-identity supply a
|
|
23
|
+
short-lived token from the operator's own login.
|
|
24
|
+
"""
|
|
25
|
+
if pat:
|
|
26
|
+
token = base64.b64encode(f":{pat}".encode()).decode()
|
|
27
|
+
return {"Authorization": f"Basic {token}"}
|
|
28
|
+
return {"Authorization": f"Bearer {get_az_devops_token()}"}
|