@caddis/cli 0.4.51 → 0.4.53
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.
- package/bundles/antigravity-plugin/CADDIS-GLOSSARY.md +1 -0
- package/bundles/antigravity-plugin/plugin.json +1 -1
- package/bundles/antigravity-plugin/repo_freshness.py +132 -5
- package/bundles/antigravity-plugin/scripts/caddis_checkout_guard.py +223 -0
- package/bundles/antigravity-plugin/scripts/caddis_docs_pr.py +284 -0
- package/bundles/antigravity-plugin/scripts/caddis_exit.py +2 -0
- package/bundles/antigravity-plugin/scripts/caddis_frontmatter.py +1 -0
- package/bundles/antigravity-plugin/scripts/caddis_gate.py +226 -195
- package/bundles/antigravity-plugin/scripts/caddis_glossary.py +3 -3
- package/bundles/antigravity-plugin/scripts/caddis_governor.py +48 -4
- package/bundles/antigravity-plugin/scripts/caddis_inventory.py +38 -0
- package/bundles/antigravity-plugin/scripts/caddis_lanes.py +294 -127
- package/bundles/antigravity-plugin/scripts/caddis_lineage_publish.py +458 -0
- package/bundles/antigravity-plugin/scripts/caddis_merge_chain.py +237 -0
- package/bundles/antigravity-plugin/scripts/caddis_notify.py +121 -0
- package/bundles/antigravity-plugin/scripts/caddis_prod_check.py +380 -0
- package/bundles/antigravity-plugin/scripts/caddis_redact.py +99 -0
- package/bundles/antigravity-plugin/scripts/caddis_relay_git.py +2 -2
- package/bundles/antigravity-plugin/scripts/caddis_review.py +1309 -0
- package/bundles/antigravity-plugin/scripts/caddis_roadmap.py +2056 -0
- package/bundles/antigravity-plugin/scripts/caddis_roadmap_run.py +2572 -0
- package/bundles/antigravity-plugin/scripts/caddis_routing.py +3 -2
- package/bundles/antigravity-plugin/scripts/caddis_rows.py +476 -0
- package/bundles/antigravity-plugin/scripts/caddis_spawn.py +8 -9
- package/bundles/antigravity-plugin/scripts/caddis_spend.py +415 -0
- package/bundles/antigravity-plugin/scripts/caddis_tidy.py +357 -20
- package/bundles/antigravity-plugin/scripts/caddis_todo.py +55 -0
- package/bundles/antigravity-plugin/scripts/caddis_workstreams.py +178 -19
- package/bundles/antigravity-plugin/scripts/caddis_worktree_close.py +349 -0
- package/bundles/antigravity-plugin/scripts/check_doc_coverage.py +0 -56
- package/bundles/antigravity-plugin/scripts/claudster_doctor.py +1 -1
- package/bundles/antigravity-plugin/scripts/enforcement_inventory.py +0 -1
- package/bundles/antigravity-plugin/scripts/oss_review.py +2 -2
- package/bundles/antigravity-plugin/scripts/roadmap-recheck.ps1 +89 -0
- package/bundles/antigravity-plugin/scripts/roadmap-run.ps1 +88 -0
- package/bundles/antigravity-plugin/scripts/setup_project_ai.py +86 -2
- package/bundles/antigravity-plugin/session_end_agy.py +3 -3
- package/bundles/antigravity-plugin/skills/_registry.md +2 -6
- package/bundles/antigravity-plugin/skills/best-practices/agents/best-practices-referencer.md +1 -1
- package/bundles/antigravity-plugin/skills/best-practices/agents/codebase-context-builder.md +1 -1
- package/bundles/antigravity-plugin/skills/best-practices/agents/task-intent-analyzer.md +1 -1
- package/bundles/antigravity-plugin/skills/catchup/SKILL.md +7 -0
- package/bundles/antigravity-plugin/skills/cloud-phases/SKILL.md +1 -1
- package/bundles/antigravity-plugin/skills/cross-review/SKILL.md +5 -2
- package/bundles/antigravity-plugin/skills/feature-plan/SKILL.md +29 -83
- package/bundles/antigravity-plugin/skills/handoff/SKILL.md +77 -48
- package/bundles/antigravity-plugin/skills/implement/SKILL.md +87 -147
- package/bundles/antigravity-plugin/skills/migrate-dir/SKILL.md +1 -1
- package/bundles/antigravity-plugin/skills/mmr/SKILL.md +5 -2
- package/bundles/antigravity-plugin/skills/office-documents-official/SKILL.md +24 -0
- package/bundles/antigravity-plugin/skills/park/SKILL.md +0 -1
- package/bundles/antigravity-plugin/skills/pr-merge/SKILL.md +8 -14
- package/bundles/antigravity-plugin/skills/prd/SKILL.md +2 -10
- package/bundles/antigravity-plugin/skills/roadmap/SKILL.md +259 -0
- package/bundles/antigravity-plugin/skills/roadmap/assets/plan-resume-prompt.md +77 -0
- package/bundles/antigravity-plugin/skills/roadmap/assets/render-check.template.mjs +68 -0
- package/bundles/antigravity-plugin/skills/roadmap/assets/review-protocol.md +78 -0
- package/bundles/antigravity-plugin/skills/roadmap/assets/roadmap.template.md +32 -0
- package/bundles/antigravity-plugin/skills/roadmap/assets/value-check.template.py +161 -0
- package/bundles/antigravity-plugin/skills/ship/SKILL.md +14 -14
- package/bundles/antigravity-plugin/skills/ship-merge/SKILL.md +6 -0
- package/bundles/antigravity-plugin/skills/ship-pr/SKILL.md +33 -14
- package/bundles/antigravity-plugin/skills/spawn-session/SKILL.md +4 -8
- package/bundles/antigravity-plugin/warm_start_agy.py +1 -3
- package/bundles/antigravity-plugin-extras/plugin.json +1 -1
- package/bundles/antigravity-plugin-extras/skills/_registry.md +1 -2
- package/bundles/antigravity-plugin-extras/skills/agent-orchestration/SKILL.md +5 -5
- package/bundles/antigravity-plugin-extras/skills/cross-review/SKILL.md +5 -2
- package/bundles/antigravity-plugin-extras/skills/runner-move/SKILL.md +214 -0
- package/bundles/codex/.codex/skills/docs/office-documents-official/SKILL.md +24 -0
- package/bundles/codex/.codex/skills/workflow/best-practices/agents/best-practices-referencer.md +1 -1
- package/bundles/codex/.codex/skills/workflow/best-practices/agents/codebase-context-builder.md +1 -1
- package/bundles/codex/.codex/skills/workflow/best-practices/agents/task-intent-analyzer.md +1 -1
- package/bundles/codex/.codex/skills/workflow/roadmap/SKILL.md +259 -0
- package/bundles/codex/.codex/skills/workflow/roadmap/assets/plan-resume-prompt.md +77 -0
- package/bundles/codex/.codex/skills/workflow/roadmap/assets/render-check.template.mjs +68 -0
- package/bundles/codex/.codex/skills/workflow/roadmap/assets/review-protocol.md +78 -0
- package/bundles/codex/.codex/skills/workflow/roadmap/assets/roadmap.template.md +32 -0
- package/bundles/codex/.codex/skills/workflow/roadmap/assets/value-check.template.py +161 -0
- package/bundles/codex/AGENTS.md +5 -4
- package/bundles/manifest.json +3 -3
- package/package.json +1 -1
- package/bundles/antigravity-plugin/skills/advisory-hub/SKILL.md +0 -233
- package/bundles/antigravity-plugin/skills/advisory-hub/reference/context-template.md +0 -214
- package/bundles/antigravity-plugin/skills/advisory-hub/reference/hub-handbook.md +0 -233
- package/bundles/antigravity-plugin/skills/advisory-hub/reference/report-contract.md +0 -94
- package/bundles/antigravity-plugin/skills/advisory-hub/reference/succession.md +0 -118
- package/bundles/antigravity-plugin/skills/resume/SKILL.md +0 -6
- package/bundles/antigravity-plugin/skills/review/SKILL.md +0 -6
- package/bundles/antigravity-plugin/skills/spawn-hub/SKILL.md +0 -341
- package/bundles/antigravity-plugin/skills/statusline/SKILL.md +0 -6
- package/bundles/antigravity-plugin/skills/validate-phase/SKILL.md +0 -143
- package/bundles/antigravity-plugin-extras/skills/data-lineage/SKILL.md +0 -158
- package/bundles/antigravity-plugin-extras/skills/data-lineage/scripts/convert_doc_to_confluence.py +0 -1430
- package/bundles/antigravity-plugin-extras/skills/golden-plan/SKILL.md +0 -586
- package/bundles/codex/.codex/skills/workflow/advisory-hub/SKILL.md +0 -233
- package/bundles/codex/.codex/skills/workflow/advisory-hub/reference/context-template.md +0 -214
- package/bundles/codex/.codex/skills/workflow/advisory-hub/reference/hub-handbook.md +0 -233
- package/bundles/codex/.codex/skills/workflow/advisory-hub/reference/report-contract.md +0 -94
- package/bundles/codex/.codex/skills/workflow/advisory-hub/reference/succession.md +0 -118
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
"""Write handoff's tracked doc edits in a temp worktree this session owns, then open one docs PR.
|
|
2
|
+
|
|
3
|
+
A shared checkout (see `caddis_checkout_guard.py shared`) may hold another session's uncommitted edits, so
|
|
4
|
+
handoff must not commit there. Instead:
|
|
5
|
+
|
|
6
|
+
caddis_docs_pr.py start --repo <root>
|
|
7
|
+
git fetch origin, then `git worktree add -b docs/handoff-<utc> <tmp> origin/<default>`;
|
|
8
|
+
prints `WORKTREE: <path>`. The worktree is new and belongs to this session only, so no other
|
|
9
|
+
session's edits can enter it.
|
|
10
|
+
caddis_docs_pr.py finish --worktree <path> --title T --body B
|
|
11
|
+
first stages every change in the worktree and saves the full diff (staged, unstaged and
|
|
12
|
+
untracked) to a new patch file. Then it commits, pushes, opens a PR with
|
|
13
|
+
`gh api repos/<owner>/<repo>/pulls`, prints `PR: <url>` and one `FILE: <path>` line per file.
|
|
14
|
+
With no change it prints `PR: none`. On any failure after the patch is saved it prints
|
|
15
|
+
`PATCH: <path>`. It removes the worktree and the local branch only after a PR opened, after the
|
|
16
|
+
patch was saved, or when there was no change. If the patch cannot be saved, it keeps the worktree
|
|
17
|
+
and prints `WORKTREE KEPT: <path>`. It never merges.
|
|
18
|
+
|
|
19
|
+
`finish` acts only on a linked worktree that `start` made: its git dir differs from the common git dir, it is
|
|
20
|
+
not the main worktree, it is under the temp folder, and its branch is `docs/handoff-<utc>[-n]`. Anything else
|
|
21
|
+
is refused (exit 4) and left as it is.
|
|
22
|
+
|
|
23
|
+
Exit codes (caddis_exit): 0 clean (PR opened, or no change), 1 blocked (the PR could not be opened),
|
|
24
|
+
3 could not run (git or the push failed, no GitHub origin), 4 error (bad usage, not a docs-PR worktree).
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from __future__ import annotations
|
|
28
|
+
|
|
29
|
+
import argparse
|
|
30
|
+
import datetime
|
|
31
|
+
import json
|
|
32
|
+
import os
|
|
33
|
+
import re
|
|
34
|
+
import shutil
|
|
35
|
+
import subprocess
|
|
36
|
+
import sys
|
|
37
|
+
import tempfile
|
|
38
|
+
from pathlib import Path
|
|
39
|
+
|
|
40
|
+
try:
|
|
41
|
+
import caddis_exit
|
|
42
|
+
import caddis_checkout_guard
|
|
43
|
+
import caddis_roadmap as roadmap
|
|
44
|
+
except ModuleNotFoundError as exc:
|
|
45
|
+
if exc.name not in {"caddis_exit", "caddis_checkout_guard", "caddis_roadmap"}:
|
|
46
|
+
raise
|
|
47
|
+
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
48
|
+
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "claude-harness" / "scripts"))
|
|
49
|
+
import caddis_exit
|
|
50
|
+
import caddis_checkout_guard
|
|
51
|
+
import caddis_roadmap as roadmap
|
|
52
|
+
|
|
53
|
+
BRANCH_PREFIX = "docs/handoff-"
|
|
54
|
+
DIR_PREFIX = "caddis-docs-pr-"
|
|
55
|
+
BRANCH_RE = re.compile(r"^docs/handoff-\d{14}(?:-\d+)?$")
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class DocsPrError(Exception):
|
|
59
|
+
def __init__(self, message: str, code: int):
|
|
60
|
+
super().__init__(message)
|
|
61
|
+
self.code = code
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _git(root: Path, *args: str, check: bool = True):
|
|
65
|
+
"""git through caddis_roadmap: no credential prompt, and a timeout on fetch and push."""
|
|
66
|
+
return roadmap._git(Path(root), *args, check=check)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _utc() -> str:
|
|
70
|
+
return datetime.datetime.now(datetime.timezone.utc).strftime("%Y%m%d%H%M%S")
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def start(repo: Path, out=print) -> int:
|
|
74
|
+
repo = Path(repo).resolve()
|
|
75
|
+
base = roadmap.fetch(repo)
|
|
76
|
+
stamp = _utc()
|
|
77
|
+
tmp_root = Path(tempfile.gettempdir())
|
|
78
|
+
# Two starts in one second must not collide on the path or the branch.
|
|
79
|
+
suffix = ""
|
|
80
|
+
n = 1
|
|
81
|
+
while (tmp_root / f"{DIR_PREFIX}{stamp}{suffix}").exists() or \
|
|
82
|
+
_git(repo, "rev-parse", "--verify", "--quiet", f"refs/heads/{BRANCH_PREFIX}{stamp}{suffix}",
|
|
83
|
+
check=False).returncode == 0:
|
|
84
|
+
n += 1
|
|
85
|
+
suffix = f"-{n}"
|
|
86
|
+
worktree = tmp_root / f"{DIR_PREFIX}{stamp}{suffix}"
|
|
87
|
+
branch = f"{BRANCH_PREFIX}{stamp}{suffix}"
|
|
88
|
+
_git(repo, "worktree", "add", "--quiet", "-b", branch, str(worktree), f"origin/{base}")
|
|
89
|
+
out(f"WORKTREE: {worktree}")
|
|
90
|
+
return caddis_exit.CLEAN
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _abs(base: Path, raw: str) -> Path:
|
|
94
|
+
path = Path(raw.strip())
|
|
95
|
+
if not path.is_absolute():
|
|
96
|
+
path = base / path
|
|
97
|
+
return path.resolve()
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _main_repo(worktree: Path) -> Path:
|
|
101
|
+
"""The main checkout: the first entry of `git worktree list --porcelain`."""
|
|
102
|
+
for line in _git(worktree, "worktree", "list", "--porcelain").stdout.splitlines():
|
|
103
|
+
if line.startswith("worktree "):
|
|
104
|
+
return Path(line[len("worktree "):]).resolve()
|
|
105
|
+
raise DocsPrError(f"git worktree list gave no main worktree for {worktree}", caddis_exit.ERROR)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _is_under(path: Path, parent: Path) -> bool:
|
|
109
|
+
try:
|
|
110
|
+
path.relative_to(parent)
|
|
111
|
+
except ValueError:
|
|
112
|
+
return False
|
|
113
|
+
return path != parent
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _check_owned(worktree: Path) -> tuple[Path, str]:
|
|
117
|
+
"""(main repo, branch) of a worktree `start` made; DocsPrError(ERROR) for anything else."""
|
|
118
|
+
if not worktree.is_dir():
|
|
119
|
+
raise DocsPrError(f"not a directory: {worktree}", caddis_exit.ERROR)
|
|
120
|
+
proc = _git(worktree, "rev-parse", "--show-toplevel", check=False)
|
|
121
|
+
if proc.returncode != 0:
|
|
122
|
+
raise DocsPrError(f"not a git worktree: {worktree}", caddis_exit.ERROR)
|
|
123
|
+
if Path(proc.stdout.strip()).resolve() != worktree.resolve():
|
|
124
|
+
raise DocsPrError(f"not the top of a worktree: {worktree}", caddis_exit.ERROR)
|
|
125
|
+
git_dir = _abs(worktree, _git(worktree, "rev-parse", "--git-dir").stdout)
|
|
126
|
+
common = _abs(worktree, _git(worktree, "rev-parse", "--git-common-dir").stdout)
|
|
127
|
+
if git_dir == common:
|
|
128
|
+
raise DocsPrError(f"{worktree} is not a linked worktree; nothing done", caddis_exit.ERROR)
|
|
129
|
+
main = _main_repo(worktree)
|
|
130
|
+
if main == worktree.resolve():
|
|
131
|
+
raise DocsPrError(f"{worktree} is the main checkout, not a docs-PR worktree; nothing done",
|
|
132
|
+
caddis_exit.ERROR)
|
|
133
|
+
branch = _git(worktree, "branch", "--show-current").stdout.strip()
|
|
134
|
+
if not BRANCH_RE.match(branch):
|
|
135
|
+
raise DocsPrError(f"{worktree} is on branch '{branch}', not a {BRANCH_PREFIX}<utc> branch; nothing done",
|
|
136
|
+
caddis_exit.ERROR)
|
|
137
|
+
resolved = worktree.resolve()
|
|
138
|
+
if resolved.parent != Path(tempfile.gettempdir()).resolve() or not resolved.name.startswith(DIR_PREFIX):
|
|
139
|
+
raise DocsPrError(f"{worktree} is not a {DIR_PREFIX}* folder directly in the temp folder that start uses; "
|
|
140
|
+
"nothing done", caddis_exit.ERROR)
|
|
141
|
+
return main, branch
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _changed_files(worktree: Path) -> list[str]:
|
|
145
|
+
"""Every path in `git status --porcelain -z`, parsed on NUL, so spaces, ' -> ' and non-ASCII stay exact.
|
|
146
|
+
|
|
147
|
+
A rename or copy entry is followed by one more NUL field, the original path; both paths count.
|
|
148
|
+
"""
|
|
149
|
+
proc = subprocess.run(["git", "-C", str(worktree), "status", "--porcelain=v1", "--untracked-files=all", "-z"],
|
|
150
|
+
capture_output=True, stdin=subprocess.DEVNULL)
|
|
151
|
+
if proc.returncode != 0:
|
|
152
|
+
raise DocsPrError(f"git status failed in {worktree}", caddis_exit.NOT_RUN)
|
|
153
|
+
parts = proc.stdout.decode("utf-8", errors="surrogateescape").split("\0")
|
|
154
|
+
files: list[str] = []
|
|
155
|
+
idx = 0
|
|
156
|
+
while idx < len(parts):
|
|
157
|
+
entry = parts[idx]
|
|
158
|
+
idx += 1
|
|
159
|
+
if len(entry) < 4:
|
|
160
|
+
continue
|
|
161
|
+
xy, paths = entry[:2], [entry[3:]]
|
|
162
|
+
if ("R" in xy or "C" in xy) and idx < len(parts):
|
|
163
|
+
paths.append(parts[idx])
|
|
164
|
+
idx += 1
|
|
165
|
+
for path in paths:
|
|
166
|
+
if path and path not in files:
|
|
167
|
+
files.append(path)
|
|
168
|
+
return files
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def open_pr(gh_repo: str, branch: str, base: str, title: str, body: str) -> str:
|
|
172
|
+
"""Open a PR with the REST API and return its URL. `gh` takes the account from GH_TOKEN when it is set."""
|
|
173
|
+
proc = roadmap.run_bounded(["gh", "api", f"repos/{gh_repo}/pulls", "-f", f"title={title}", "-f",
|
|
174
|
+
f"head={branch}", "-f", f"base={base}", "-f", f"body={body}"],
|
|
175
|
+
roadmap.GH_TIMEOUT, "gh api (open the PR)")
|
|
176
|
+
if proc.returncode != 0:
|
|
177
|
+
raise DocsPrError(f"could not open the PR: {(proc.stderr or proc.stdout).strip()[:300]}",
|
|
178
|
+
caddis_exit.BLOCKED)
|
|
179
|
+
try:
|
|
180
|
+
return str(json.loads(proc.stdout)["html_url"])
|
|
181
|
+
except (ValueError, KeyError, TypeError) as exc:
|
|
182
|
+
raise DocsPrError(f"gh api gave no PR URL: {exc}", caddis_exit.BLOCKED) from exc
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def _save_patch(worktree: Path) -> Path:
|
|
186
|
+
"""Stage every change and write the full staged diff to a new patch file (mkstemp: no predictable path).
|
|
187
|
+
|
|
188
|
+
`git add -A` is safe here: this worktree was made by `start` for this session only (see _check_owned).
|
|
189
|
+
"""
|
|
190
|
+
_git(worktree, "add", "-A")
|
|
191
|
+
proc = subprocess.run(["git", "-C", str(worktree), "diff", "--cached", "--binary"],
|
|
192
|
+
capture_output=True, stdin=subprocess.DEVNULL)
|
|
193
|
+
if proc.returncode != 0:
|
|
194
|
+
raise DocsPrError(f"git diff failed in {worktree}", caddis_exit.NOT_RUN)
|
|
195
|
+
fd, name = tempfile.mkstemp(prefix=DIR_PREFIX, suffix=".patch")
|
|
196
|
+
with os.fdopen(fd, "wb") as handle:
|
|
197
|
+
handle.write(proc.stdout)
|
|
198
|
+
return Path(name)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def _remove(main: Path, worktree: Path, branch: str) -> None:
|
|
202
|
+
for args in (("worktree", "remove", "--force", "--force", str(worktree)), ("worktree", "prune"),
|
|
203
|
+
("branch", "-D", branch)):
|
|
204
|
+
try:
|
|
205
|
+
_git(main, *args, check=False)
|
|
206
|
+
except (roadmap.RoadmapError, OSError):
|
|
207
|
+
pass
|
|
208
|
+
if worktree.exists():
|
|
209
|
+
shutil.rmtree(worktree, ignore_errors=True)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def finish(worktree: Path, title: str, body: str, out=print) -> int:
|
|
213
|
+
worktree = Path(worktree).resolve()
|
|
214
|
+
main, branch = _check_owned(worktree) # refuses before anything is changed or removed
|
|
215
|
+
# Save the session's edits FIRST. Until the patch exists, nothing may remove the worktree.
|
|
216
|
+
try:
|
|
217
|
+
files = _changed_files(worktree)
|
|
218
|
+
patch = _save_patch(worktree) if files else None
|
|
219
|
+
except (DocsPrError, roadmap.RoadmapError, OSError) as exc:
|
|
220
|
+
out(f"docs PR failed: could not save the change: {exc}")
|
|
221
|
+
out(f"WORKTREE KEPT: {worktree}")
|
|
222
|
+
return getattr(exc, "code", caddis_exit.NOT_RUN)
|
|
223
|
+
if patch is None:
|
|
224
|
+
_remove(main, worktree, branch)
|
|
225
|
+
out("PR: none")
|
|
226
|
+
return caddis_exit.CLEAN
|
|
227
|
+
pushed = False
|
|
228
|
+
try:
|
|
229
|
+
gh_repo = roadmap.gh_repo_of(main)
|
|
230
|
+
if gh_repo is None:
|
|
231
|
+
raise DocsPrError("origin is not a GitHub remote; cannot open a docs PR", caddis_exit.NOT_RUN)
|
|
232
|
+
base = roadmap.default_branch(main)
|
|
233
|
+
_git(worktree, "commit", "--quiet", "-m", title)
|
|
234
|
+
_git(worktree, "push", "--quiet", "-u", "origin", branch)
|
|
235
|
+
pushed = True
|
|
236
|
+
url = open_pr(gh_repo, branch, base, title, body)
|
|
237
|
+
except (DocsPrError, roadmap.RoadmapError, OSError) as exc:
|
|
238
|
+
out(f"docs PR failed: {exc}")
|
|
239
|
+
out(f"PATCH: {patch}")
|
|
240
|
+
if pushed:
|
|
241
|
+
try:
|
|
242
|
+
_git(main, "push", "--quiet", "origin", "--delete", branch, check=False)
|
|
243
|
+
except (roadmap.RoadmapError, OSError):
|
|
244
|
+
pass
|
|
245
|
+
_remove(main, worktree, branch)
|
|
246
|
+
return getattr(exc, "code", caddis_exit.NOT_RUN)
|
|
247
|
+
out(f"PR: {url}")
|
|
248
|
+
for path in files:
|
|
249
|
+
out(f"FILE: {path}")
|
|
250
|
+
_remove(main, worktree, branch)
|
|
251
|
+
try:
|
|
252
|
+
patch.unlink()
|
|
253
|
+
except OSError:
|
|
254
|
+
pass
|
|
255
|
+
return caddis_exit.CLEAN
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def main(argv: list[str] | None = None) -> int:
|
|
259
|
+
parser = argparse.ArgumentParser(description="Handoff's docs PR from a temp worktree.")
|
|
260
|
+
sub = parser.add_subparsers(dest="command")
|
|
261
|
+
p_start = sub.add_parser("start", help="make the worktree from origin/<default>")
|
|
262
|
+
p_start.add_argument("--repo", required=True, type=Path)
|
|
263
|
+
p_finish = sub.add_parser("finish", help="save a patch, commit, push and open the PR, then remove the worktree")
|
|
264
|
+
p_finish.add_argument("--worktree", required=True, type=Path)
|
|
265
|
+
p_finish.add_argument("--title", required=True)
|
|
266
|
+
p_finish.add_argument("--body", required=True)
|
|
267
|
+
try:
|
|
268
|
+
args = parser.parse_args(argv)
|
|
269
|
+
except SystemExit:
|
|
270
|
+
return caddis_exit.ERROR
|
|
271
|
+
try:
|
|
272
|
+
if args.command == "start":
|
|
273
|
+
return start(args.repo)
|
|
274
|
+
if args.command == "finish":
|
|
275
|
+
return finish(args.worktree, args.title, args.body)
|
|
276
|
+
except (DocsPrError, roadmap.RoadmapError) as exc:
|
|
277
|
+
print(f"caddis_docs_pr: {exc}", file=sys.stderr)
|
|
278
|
+
return exc.code
|
|
279
|
+
parser.print_help(sys.stderr)
|
|
280
|
+
return caddis_exit.ERROR
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
if __name__ == "__main__":
|
|
284
|
+
sys.exit(main())
|
|
@@ -5,6 +5,7 @@ BLOCKED = 1
|
|
|
5
5
|
ADVISORY = 2
|
|
6
6
|
NOT_RUN = 3
|
|
7
7
|
ERROR = 4
|
|
8
|
+
ROUND_LIMIT = 5 # caddis_review.py --plan: round limit reached, stop, the owner decides
|
|
8
9
|
|
|
9
10
|
MEANING = {
|
|
10
11
|
CLEAN: "clean",
|
|
@@ -12,4 +13,5 @@ MEANING = {
|
|
|
12
13
|
ADVISORY: "advisory",
|
|
13
14
|
NOT_RUN: "could not run",
|
|
14
15
|
ERROR: "error or bad usage",
|
|
16
|
+
ROUND_LIMIT: "round limit reached, the owner decides",
|
|
15
17
|
}
|