roadmodel 0.2.82__tar.gz → 0.2.83__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.
- {roadmodel-0.2.82 → roadmodel-0.2.83}/PKG-INFO +1 -1
- {roadmodel-0.2.82 → roadmodel-0.2.83}/pyproject.toml +1 -1
- {roadmodel-0.2.82 → roadmodel-0.2.83}/scripts/update_projects.py +132 -29
- {roadmodel-0.2.82 → roadmodel-0.2.83}/src/roadmodel/__init__.py +1 -1
- {roadmodel-0.2.82 → roadmodel-0.2.83}/.github/README.md +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/.gitignore +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/LICENSE +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/NOTICE +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/README.md +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/docs/benchmarks.json +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/docs/catalog.json +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/docs/claude-commands/roadmap-phase.md +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/docs/claude-commands/roadmap-project.md +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/docs/claude-commands/roadmap-refresh.md +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/docs/claude-commands/roadmap-step.md +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/docs/claude-commands/roadmodel-upgrade.md +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/docs/context-windows.json +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/docs/effort-tokens.json +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/docs/model-selector.txt +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/docs/model-tier-cost-scale.md +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/docs/settings-display.md +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/docs/templates/phase-roadmap-template.md +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/docs/templates/planning-kit-how-to-use.md +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/docs/templates/project-roadmap-template.md +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/docs/templates/prompt-phase-roadmap.md +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/docs/templates/prompt-project-roadmap.md +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/docs/user-context.example.md +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/hatch_build.py +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/infra/README.md +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/infra/gcp-killswitch/README.md +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/infra/supabase/README.md +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/roadmodel/data/benchmarks.json +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/roadmodel/data/catalog.json +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/roadmodel/data/context-windows.json +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/roadmodel/data/effort-tokens.json +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/roadmodel/data/model-selector.txt +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/roadmodel/data/model-tier-cost-scale.md +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/roadmodel/data/phase-roadmap-template.md +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/roadmodel/data/planning-kit-how-to-use.md +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/roadmodel/data/project-roadmap-template.md +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/roadmodel/data/prompt-phase-roadmap.md +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/roadmodel/data/prompt-project-roadmap.md +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/roadmodel/data/roadmap-phase.md +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/roadmodel/data/roadmap-project.md +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/roadmodel/data/roadmap-refresh.md +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/roadmodel/data/roadmap-step.md +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/roadmodel/data/roadmodel-upgrade.md +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/roadmodel/data/settings-display.md +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/roadmodel/data/user-context.example.md +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/scripts/agent_context.py +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/scripts/agent_rules.py +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/scripts/updater_lock.py +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/scripts/upgrade_fleet.py +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/scripts/worktree_memory.py +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/src/roadmodel/__main__.py +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/src/roadmodel/cli.py +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/src/roadmodel/config.py +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/src/roadmodel/cost.py +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/src/roadmodel/errors.py +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/src/roadmodel/mcp_server.py +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/src/roadmodel/private_files.py +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/src/roadmodel/providers/__init__.py +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/src/roadmodel/providers/_safety.py +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/src/roadmodel/providers/anthropic.py +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/src/roadmodel/providers/google.py +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/src/roadmodel/providers/openai.py +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/src/roadmodel/providers/openai_compatible.py +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/src/roadmodel/providers/registry.py +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/src/roadmodel/recommend.py +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/src/roadmodel/scoring.py +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/src/roadmodel/usage.py +0 -0
- {roadmodel-0.2.82 → roadmodel-0.2.83}/src/roadmodel/user_context.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: roadmodel
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.83
|
|
4
4
|
Summary: BYO-key CLI that recommends the right AI model, platform, and settings for a prompt.
|
|
5
5
|
Project-URL: Homepage, https://roadmodel.ai
|
|
6
6
|
Project-URL: Repository, https://github.com/nathanramoscfa/roadmodel
|
|
@@ -49,6 +49,7 @@ from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
|
49
49
|
from dataclasses import dataclass, field
|
|
50
50
|
from pathlib import Path
|
|
51
51
|
from typing import Optional
|
|
52
|
+
from urllib.parse import urlsplit
|
|
52
53
|
from xml.sax.saxutils import escape
|
|
53
54
|
|
|
54
55
|
COMMANDS = (
|
|
@@ -1975,7 +1976,8 @@ def _parse_time(value: str) -> tuple[int, int]:
|
|
|
1975
1976
|
def launchd_plist(python: Path, script: Path, hour: int, minute: int, log: Path) -> str:
|
|
1976
1977
|
# stdout goes nowhere: `--log` already writes the run to the log, and
|
|
1977
1978
|
# launchd capturing stdout into the same file recorded every run twice.
|
|
1978
|
-
#
|
|
1979
|
+
# --log also captures stderr after its safe open. launchd must not pre-open
|
|
1980
|
+
# a path outside that validation; failures before log setup are discarded.
|
|
1979
1981
|
return f"""<?xml version="1.0" encoding="UTF-8"?>
|
|
1980
1982
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
1981
1983
|
<plist version="1.0">
|
|
@@ -1994,7 +1996,7 @@ def launchd_plist(python: Path, script: Path, hour: int, minute: int, log: Path)
|
|
|
1994
1996
|
<dict><key>Hour</key><integer>{hour}</integer><key>Minute</key><integer>{minute}</integer></dict>
|
|
1995
1997
|
<key>RunAtLoad</key><false/>
|
|
1996
1998
|
<key>StandardOutPath</key><string>/dev/null</string>
|
|
1997
|
-
<key>StandardErrorPath</key><string
|
|
1999
|
+
<key>StandardErrorPath</key><string>/dev/null</string>
|
|
1998
2000
|
<key>EnvironmentVariables</key>
|
|
1999
2001
|
<dict><key>PATH</key><string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string></dict>
|
|
2000
2002
|
</dict>
|
|
@@ -2248,7 +2250,90 @@ CONTEXT_SYNC_CONFIG = CONFIG_DIR / "context-sync.json"
|
|
|
2248
2250
|
CONTEXT_CLONE = CONFIG_DIR / "context-repo"
|
|
2249
2251
|
USER_CONTEXT = CONFIG_DIR / "user-context.md"
|
|
2250
2252
|
CONTEXT_FILE = "user-context.md"
|
|
2251
|
-
_REPO_SLUG_RE = re.compile(r"
|
|
2253
|
+
_REPO_SLUG_RE = re.compile(r"[A-Za-z0-9][A-Za-z0-9-]{0,38}/[A-Za-z0-9_][A-Za-z0-9_.-]{0,99}")
|
|
2254
|
+
|
|
2255
|
+
|
|
2256
|
+
def _context_repo(value: str) -> str:
|
|
2257
|
+
"""One GitHub repository identity, with no local paths or URL credentials."""
|
|
2258
|
+
if not isinstance(value, str) or any(ord(c) < 33 or ord(c) == 127 for c in value):
|
|
2259
|
+
raise ValueError("Unsupported context repository.")
|
|
2260
|
+
candidate = value
|
|
2261
|
+
if value.startswith("git@github.com:"):
|
|
2262
|
+
candidate = value[len("git@github.com:") :]
|
|
2263
|
+
elif "://" in value:
|
|
2264
|
+
parsed = urlsplit(value)
|
|
2265
|
+
if (
|
|
2266
|
+
parsed.hostname != "github.com"
|
|
2267
|
+
or parsed.port is not None
|
|
2268
|
+
or parsed.query
|
|
2269
|
+
or parsed.fragment
|
|
2270
|
+
):
|
|
2271
|
+
raise ValueError("Unsupported context repository.")
|
|
2272
|
+
if parsed.scheme == "https":
|
|
2273
|
+
if parsed.username is not None or parsed.password is not None:
|
|
2274
|
+
raise ValueError("Unsupported context repository.")
|
|
2275
|
+
elif parsed.scheme == "ssh":
|
|
2276
|
+
if parsed.username != "git" or parsed.password is not None:
|
|
2277
|
+
raise ValueError("Unsupported context repository.")
|
|
2278
|
+
else:
|
|
2279
|
+
raise ValueError("Unsupported context repository.")
|
|
2280
|
+
candidate = parsed.path.removeprefix("/")
|
|
2281
|
+
if candidate.endswith(".git"):
|
|
2282
|
+
candidate = candidate[:-4]
|
|
2283
|
+
if not _REPO_SLUG_RE.fullmatch(candidate):
|
|
2284
|
+
raise ValueError("Unsupported context repository.")
|
|
2285
|
+
return candidate.lower()
|
|
2286
|
+
|
|
2287
|
+
|
|
2288
|
+
def _context_visibility(repo: str) -> bool:
|
|
2289
|
+
"""Require authenticated, exact-identity PRIVATE metadata before carrying bytes."""
|
|
2290
|
+
try:
|
|
2291
|
+
gh = shutil.which("gh")
|
|
2292
|
+
if not gh:
|
|
2293
|
+
return False
|
|
2294
|
+
result = subprocess.run( # noqa: S603 — fixed gh API, canonical repository
|
|
2295
|
+
[gh, "api", "--hostname", "github.com", f"repos/{repo}"],
|
|
2296
|
+
capture_output=True,
|
|
2297
|
+
text=True,
|
|
2298
|
+
timeout=30,
|
|
2299
|
+
)
|
|
2300
|
+
if result.returncode != 0:
|
|
2301
|
+
return False
|
|
2302
|
+
data = json.loads(result.stdout)
|
|
2303
|
+
return (
|
|
2304
|
+
isinstance(data, dict)
|
|
2305
|
+
and data.get("private") is True
|
|
2306
|
+
and data.get("visibility") == "private"
|
|
2307
|
+
and isinstance(data.get("full_name"), str)
|
|
2308
|
+
and data["full_name"].lower() == repo
|
|
2309
|
+
)
|
|
2310
|
+
except (OSError, ValueError, subprocess.SubprocessError):
|
|
2311
|
+
return False
|
|
2312
|
+
|
|
2313
|
+
|
|
2314
|
+
def _context_destination(repo: str) -> Optional[str]:
|
|
2315
|
+
"""Validate effective Git URL rewrites, configured transports and current branch."""
|
|
2316
|
+
for args in (
|
|
2317
|
+
("remote", "get-url", "--all", "origin"),
|
|
2318
|
+
("remote", "get-url", "--push", "--all", "origin"),
|
|
2319
|
+
):
|
|
2320
|
+
result = _git(*args, cwd=CONTEXT_CLONE)
|
|
2321
|
+
values = result.stdout.splitlines()
|
|
2322
|
+
if result.returncode or len(values) != 1 or _context_repo(values[0]) != repo:
|
|
2323
|
+
return None
|
|
2324
|
+
result = _git("symbolic-ref", "--quiet", "--short", "HEAD", cwd=CONTEXT_CLONE)
|
|
2325
|
+
branch = result.stdout.strip()
|
|
2326
|
+
if result.returncode or not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9._/-]*", branch):
|
|
2327
|
+
return None
|
|
2328
|
+
if _git("check-ref-format", "--branch", branch, cwd=CONTEXT_CLONE).returncode:
|
|
2329
|
+
return None
|
|
2330
|
+
for name in (f"branch.{branch}.remote", f"branch.{branch}.pushRemote", "remote.pushDefault"):
|
|
2331
|
+
result = _git("config", "--get-all", name, cwd=CONTEXT_CLONE)
|
|
2332
|
+
if result.returncode not in (0, 1) or any(
|
|
2333
|
+
v != "origin" for v in result.stdout.splitlines()
|
|
2334
|
+
):
|
|
2335
|
+
return None
|
|
2336
|
+
return branch
|
|
2252
2337
|
|
|
2253
2338
|
|
|
2254
2339
|
def _git(*args: str, cwd: Path) -> subprocess.CompletedProcess[str]:
|
|
@@ -2270,42 +2355,37 @@ def _read_context_config() -> Optional[dict[str, str]]:
|
|
|
2270
2355
|
|
|
2271
2356
|
def configure_context_sync(repo: str, source: Optional[str], dry_run: bool = False) -> str:
|
|
2272
2357
|
"""Record this machine's role. `source` given → it publishes that file;
|
|
2273
|
-
omitted → it pulls. `repo`
|
|
2274
|
-
|
|
2275
|
-
|
|
2358
|
+
omitted → it pulls. `repo` identifies a GitHub repository."""
|
|
2359
|
+
try:
|
|
2360
|
+
repo = _context_repo(repo)
|
|
2361
|
+
except ValueError:
|
|
2362
|
+
raise SystemExit("--context-sync: expected a canonical GitHub repository.") from None
|
|
2276
2363
|
cfg: dict[str, str] = {"repo": repo, "role": "replica"}
|
|
2277
2364
|
if source:
|
|
2278
2365
|
src = Path(source).expanduser().resolve()
|
|
2279
2366
|
if not src.is_file():
|
|
2280
|
-
raise SystemExit(
|
|
2367
|
+
raise SystemExit("--context-source: source file is unavailable.")
|
|
2281
2368
|
cfg.update(role="source", source=str(src))
|
|
2282
2369
|
if not dry_run:
|
|
2283
2370
|
_private_dir(CONFIG_DIR)
|
|
2284
2371
|
_replace_bytes(CONTEXT_SYNC_CONFIG, (json.dumps(cfg, indent=2) + "\n").encode())
|
|
2285
|
-
|
|
2286
|
-
return f"this machine is the user-context {cfg['role'].upper()}, {where}, via {repo}"
|
|
2372
|
+
return f"this machine is the user-context {cfg['role'].upper()}; privacy verification required at each sync"
|
|
2287
2373
|
|
|
2288
2374
|
|
|
2289
2375
|
def _ensure_clone(repo: str) -> Optional[str]:
|
|
2290
|
-
"""
|
|
2291
|
-
carries the operator's GitHub auth for a PRIVATE repo); a URL or path
|
|
2292
|
-
clones with plain git."""
|
|
2376
|
+
"""Clone only the independently verified GitHub repository through gh."""
|
|
2293
2377
|
if (CONTEXT_CLONE / ".git").is_dir():
|
|
2294
2378
|
_private_dir(CONTEXT_CLONE)
|
|
2295
2379
|
return None
|
|
2296
2380
|
_private_dir(CONTEXT_CLONE.parent)
|
|
2297
|
-
|
|
2298
|
-
cmd = ["gh", "repo", "clone", repo, str(CONTEXT_CLONE)]
|
|
2299
|
-
else:
|
|
2300
|
-
url = f"https://github.com/{repo}.git" if _REPO_SLUG_RE.match(repo) else repo
|
|
2301
|
-
cmd = ["git", "clone", url, str(CONTEXT_CLONE)]
|
|
2381
|
+
cmd = ["gh", "repo", "clone", repo, str(CONTEXT_CLONE)]
|
|
2302
2382
|
done = subprocess.run( # noqa: S603 — literal argv built above from a validated repo, no shell
|
|
2303
2383
|
cmd, capture_output=True, text=True, timeout=180
|
|
2304
2384
|
)
|
|
2305
2385
|
if done.returncode == 0:
|
|
2306
2386
|
_private_dir(CONTEXT_CLONE)
|
|
2307
2387
|
return None
|
|
2308
|
-
return
|
|
2388
|
+
return "clone failed"
|
|
2309
2389
|
|
|
2310
2390
|
|
|
2311
2391
|
def _digest(data: bytes) -> str:
|
|
@@ -2318,33 +2398,49 @@ def sync_user_context(dry_run: bool = False) -> list[str]:
|
|
|
2318
2398
|
"""Publish (source) or pull (replica) the user-context. Silent when this
|
|
2319
2399
|
machine was never configured, so an operator with one machine sees
|
|
2320
2400
|
nothing new."""
|
|
2401
|
+
try:
|
|
2402
|
+
return _sync_user_context(dry_run)
|
|
2403
|
+
except (OSError, ValueError, KeyError, subprocess.SubprocessError):
|
|
2404
|
+
return ["user-context sync: SKIPPED (private destination could not be verified)"]
|
|
2405
|
+
|
|
2406
|
+
|
|
2407
|
+
def _sync_user_context(dry_run: bool) -> list[str]:
|
|
2321
2408
|
cfg = _read_context_config()
|
|
2322
2409
|
if cfg is None:
|
|
2323
2410
|
return []
|
|
2324
2411
|
if dry_run:
|
|
2325
|
-
return [
|
|
2326
|
-
|
|
2412
|
+
return ["user-context sync: dry run; privacy unverified, nothing sent"]
|
|
2413
|
+
repo = _context_repo(cfg["repo"])
|
|
2414
|
+
if not _context_visibility(repo):
|
|
2415
|
+
return ["user-context sync: SKIPPED (private destination could not be verified)"]
|
|
2416
|
+
if CONTEXT_CLONE.exists() and (
|
|
2417
|
+
not (CONTEXT_CLONE / ".git").is_dir() or not _context_destination(repo)
|
|
2418
|
+
):
|
|
2419
|
+
return ["user-context sync: SKIPPED (clone destination could not be verified)"]
|
|
2420
|
+
failed = _ensure_clone(repo)
|
|
2327
2421
|
if failed:
|
|
2328
|
-
return [
|
|
2422
|
+
return ["user-context sync: SKIPPED (clone failed)"]
|
|
2423
|
+
branch = _context_destination(repo)
|
|
2424
|
+
if branch is None:
|
|
2425
|
+
return ["user-context sync: SKIPPED (clone destination could not be verified)"]
|
|
2329
2426
|
has_history = _git("rev-parse", "--verify", "HEAD", cwd=CONTEXT_CLONE).returncode == 0
|
|
2330
2427
|
if has_history:
|
|
2331
|
-
pulled = _git("pull", "--ff-only", "--quiet", cwd=CONTEXT_CLONE)
|
|
2428
|
+
pulled = _git("pull", "--ff-only", "--quiet", "origin", branch, cwd=CONTEXT_CLONE)
|
|
2332
2429
|
if pulled.returncode != 0:
|
|
2333
|
-
return [
|
|
2430
|
+
return ["user-context sync: SKIPPED (pull failed)"]
|
|
2334
2431
|
carried = CONTEXT_CLONE / CONTEXT_FILE
|
|
2335
2432
|
|
|
2336
2433
|
if cfg["role"] == "source":
|
|
2337
2434
|
src = Path(cfg["source"])
|
|
2338
2435
|
if not src.is_file():
|
|
2339
|
-
return [
|
|
2436
|
+
return ["user-context sync: SKIPPED (source file is unavailable)"]
|
|
2340
2437
|
out: list[str] = []
|
|
2341
2438
|
# An edit to a separate copy at the default path would never publish.
|
|
2342
2439
|
if USER_CONTEXT.exists() and not USER_CONTEXT.is_symlink():
|
|
2343
2440
|
if USER_CONTEXT.read_bytes() != src.read_bytes():
|
|
2344
2441
|
out.append(
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
f"{src}."
|
|
2442
|
+
"user-context sync: WARNING default context differs from the source; "
|
|
2443
|
+
"edits to that separate copy are NOT published."
|
|
2348
2444
|
)
|
|
2349
2445
|
data = src.read_bytes()
|
|
2350
2446
|
if carried.exists() and carried.read_bytes() == data:
|
|
@@ -2354,14 +2450,21 @@ def sync_user_context(dry_run: bool = False) -> list[str]:
|
|
|
2354
2450
|
steps = (
|
|
2355
2451
|
("add", CONTEXT_FILE),
|
|
2356
2452
|
("commit", "--quiet", "-m", f"sync user-context {_digest(data)}"),
|
|
2357
|
-
("push", "--quiet", "-u", "origin", "HEAD"),
|
|
2453
|
+
("push", "--quiet", "-u", "origin", f"HEAD:refs/heads/{branch}"),
|
|
2358
2454
|
)
|
|
2359
2455
|
for step in steps:
|
|
2456
|
+
if step[0] == "push" and (
|
|
2457
|
+
not _context_visibility(repo) or _context_destination(repo) != branch
|
|
2458
|
+
):
|
|
2459
|
+
return [
|
|
2460
|
+
*out,
|
|
2461
|
+
"user-context sync: SKIPPED (private destination changed before push)",
|
|
2462
|
+
]
|
|
2360
2463
|
done = _git(*step, cwd=CONTEXT_CLONE)
|
|
2361
2464
|
if done.returncode != 0:
|
|
2362
2465
|
return [
|
|
2363
2466
|
*out,
|
|
2364
|
-
f"user-context sync: FAILED at git {step[0]}
|
|
2467
|
+
f"user-context sync: FAILED at git {step[0]}",
|
|
2365
2468
|
]
|
|
2366
2469
|
return [*out, f"user-context sync: published ({_digest(data)})"]
|
|
2367
2470
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
# src/roadmodel/__init__.py
|
|
2
|
-
__version__ = "0.2.
|
|
2
|
+
__version__ = "0.2.83"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|