gitgrip 1.5.0__py3-none-any.whl
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.
- gitgrip-1.5.0.dist-info/METADATA +13 -0
- gitgrip-1.5.0.dist-info/RECORD +80 -0
- gitgrip-1.5.0.dist-info/WHEEL +5 -0
- gitgrip-1.5.0.dist-info/entry_points.txt +2 -0
- gitgrip-1.5.0.dist-info/top_level.txt +2 -0
- gr2/__init__.py +0 -0
- gr2/overlay/__init__.py +6 -0
- gr2/overlay/activate.py +196 -0
- gr2/overlay/agent_manifest.py +138 -0
- gr2/overlay/cli.py +181 -0
- gr2/overlay/cross_repo.py +124 -0
- gr2/overlay/drivers.py +113 -0
- gr2/overlay/introspection.py +155 -0
- gr2/overlay/language_drivers.py +115 -0
- gr2/overlay/objects.py +412 -0
- gr2/overlay/perf.py +251 -0
- gr2/overlay/refs.py +36 -0
- gr2/overlay/trust.py +150 -0
- gr2/overlay/types.py +69 -0
- gr2/overlay/units.py +313 -0
- gr2/overlay/workspace_spec.py +59 -0
- gr2/prototypes/__init__.py +0 -0
- gr2/prototypes/cache_materialization_probe.py +190 -0
- gr2/prototypes/concurrent_event_stress.py +199 -0
- gr2/prototypes/concurrent_lease_stress.py +240 -0
- gr2/prototypes/concurrent_workspace_cap_stress.py +231 -0
- gr2/prototypes/contribution_protocol.py +665 -0
- gr2/prototypes/cross_mode_lane_stress.py +986 -0
- gr2/prototypes/jsonl_store.py +158 -0
- gr2/prototypes/lane_workspace_prototype.py +2088 -0
- gr2/prototypes/layout_model_probe.py +139 -0
- gr2/prototypes/propagation_daemon.py +546 -0
- gr2/prototypes/propagation_state_machine.py +1478 -0
- gr2/prototypes/python_exec_playground.py +194 -0
- gr2/prototypes/python_hook_runtime_playground.py +240 -0
- gr2/prototypes/python_migration_playground.py +144 -0
- gr2/prototypes/python_review_checkout_playground.py +242 -0
- gr2/prototypes/python_spec_apply_playground.py +282 -0
- gr2/prototypes/real_git_lane_materialization.py +248 -0
- gr2/prototypes/real_git_playground.py +334 -0
- gr2/prototypes/recall_lane_history.py +274 -0
- gr2/prototypes/repo_maintenance_prototype.py +659 -0
- gr2/prototypes/repo_transport_probe.py +147 -0
- gr2/python_cli/__init__.py +2 -0
- gr2/python_cli/__main__.py +6 -0
- gr2/python_cli/add.py +51 -0
- gr2/python_cli/app.py +2516 -0
- gr2/python_cli/branch.py +67 -0
- gr2/python_cli/channel_bridge.py +131 -0
- gr2/python_cli/clone_exec.py +1019 -0
- gr2/python_cli/commit.py +199 -0
- gr2/python_cli/config.py +291 -0
- gr2/python_cli/env_exec.py +419 -0
- gr2/python_cli/events.py +529 -0
- gr2/python_cli/execops.py +372 -0
- gr2/python_cli/failures.py +98 -0
- gr2/python_cli/file_exec.py +256 -0
- gr2/python_cli/gitops.py +226 -0
- gr2/python_cli/grip.py +1337 -0
- gr2/python_cli/grip_cli.py +493 -0
- gr2/python_cli/hooks.py +450 -0
- gr2/python_cli/launch_exec.py +786 -0
- gr2/python_cli/merge_verification.py +274 -0
- gr2/python_cli/migration.py +985 -0
- gr2/python_cli/open_gr_review.py +699 -0
- gr2/python_cli/platform.py +441 -0
- gr2/python_cli/pr.py +487 -0
- gr2/python_cli/project_review.py +314 -0
- gr2/python_cli/prune.py +365 -0
- gr2/python_cli/push.py +172 -0
- gr2/python_cli/review.py +462 -0
- gr2/python_cli/review_ephemeral.py +143 -0
- gr2/python_cli/review_run.py +621 -0
- gr2/python_cli/spec_apply.py +1285 -0
- gr2/python_cli/staging_cleanup.py +205 -0
- gr2/python_cli/syncops.py +920 -0
- gr2/python_cli/target.py +100 -0
- gr2/python_cli/workspace_snapshot.py +105 -0
- gr2/schemas/gr2-materialization-plan-v1.schema.json +191 -0
- gr2_overlay/__init__.py +37 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Probe whether repo remotes are reachable/authenticated before gr2 apply.
|
|
3
|
+
|
|
4
|
+
This is a UX prototype, not a full transport manager. Its job is to answer:
|
|
5
|
+
|
|
6
|
+
- will this remote likely clone successfully from this environment?
|
|
7
|
+
- if not, is the problem transport reachability or authentication?
|
|
8
|
+
- what should the user try next?
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import argparse
|
|
14
|
+
import json
|
|
15
|
+
import os
|
|
16
|
+
import re
|
|
17
|
+
import subprocess
|
|
18
|
+
import sys
|
|
19
|
+
import tomllib
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
SSH_TIMEOUT_SECONDS = 5
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def parse_args() -> argparse.Namespace:
|
|
27
|
+
parser = argparse.ArgumentParser(description="Probe repo transport/auth readiness")
|
|
28
|
+
parser.add_argument("workspace_spec", type=Path, help="path to workspace_spec.toml")
|
|
29
|
+
parser.add_argument("--json", action="store_true", help="emit structured JSON")
|
|
30
|
+
return parser.parse_args()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def detect_transport(url: str) -> str:
|
|
34
|
+
if url.startswith("git@") or url.startswith("ssh://"):
|
|
35
|
+
return "ssh"
|
|
36
|
+
if url.startswith("https://") or url.startswith("http://"):
|
|
37
|
+
return "https"
|
|
38
|
+
return "unknown"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def classify_failure(stderr: str, transport: str) -> tuple[str, str]:
|
|
42
|
+
text = stderr.lower()
|
|
43
|
+
if "operation timed out" in text or "connection timed out" in text:
|
|
44
|
+
return ("transport-unreachable", f"{transport} transport timed out")
|
|
45
|
+
if "connection refused" in text:
|
|
46
|
+
return ("transport-unreachable", f"{transport} transport refused connection")
|
|
47
|
+
if "permission denied (publickey)" in text:
|
|
48
|
+
return ("auth-failed", "ssh key was rejected")
|
|
49
|
+
if "could not read username" in text or "authentication failed" in text:
|
|
50
|
+
return ("auth-failed", "https authentication is missing or invalid")
|
|
51
|
+
if "repository not found" in text:
|
|
52
|
+
return ("repo-not-found", "repository not found or not visible to current auth")
|
|
53
|
+
if "could not resolve host" in text:
|
|
54
|
+
return ("dns-failed", "host could not be resolved")
|
|
55
|
+
return ("probe-failed", stderr.strip().splitlines()[-1] if stderr.strip() else "unknown error")
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def recommendation(status: str, transport: str) -> str:
|
|
59
|
+
if status == "ok":
|
|
60
|
+
return "selected transport looks usable"
|
|
61
|
+
if status == "transport-unreachable" and transport == "ssh":
|
|
62
|
+
return "try HTTPS or fix SSH network reachability"
|
|
63
|
+
if status == "transport-unreachable":
|
|
64
|
+
return "check network access or remote availability"
|
|
65
|
+
if status == "auth-failed" and transport == "ssh":
|
|
66
|
+
return "load the correct SSH key or switch to HTTPS"
|
|
67
|
+
if status == "auth-failed":
|
|
68
|
+
return "configure GitHub HTTPS auth or switch to SSH"
|
|
69
|
+
if status == "repo-not-found":
|
|
70
|
+
return "verify repo visibility and credentials"
|
|
71
|
+
if status == "dns-failed":
|
|
72
|
+
return "fix host resolution before retrying"
|
|
73
|
+
return "inspect stderr and retry with an explicit transport"
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def probe_url(url: str) -> tuple[str, str]:
|
|
77
|
+
transport = detect_transport(url)
|
|
78
|
+
env = None
|
|
79
|
+
if transport == "ssh":
|
|
80
|
+
env = {
|
|
81
|
+
**dict(os.environ),
|
|
82
|
+
"GIT_SSH_COMMAND": (
|
|
83
|
+
f"ssh -o BatchMode=yes -o ConnectTimeout={SSH_TIMEOUT_SECONDS} "
|
|
84
|
+
"-o StrictHostKeyChecking=accept-new"
|
|
85
|
+
),
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
result = subprocess.run(
|
|
89
|
+
["git", "ls-remote", "--symref", url, "HEAD"],
|
|
90
|
+
text=True,
|
|
91
|
+
capture_output=True,
|
|
92
|
+
env=env,
|
|
93
|
+
)
|
|
94
|
+
if result.returncode == 0:
|
|
95
|
+
head_ref = ""
|
|
96
|
+
for line in result.stdout.splitlines():
|
|
97
|
+
match = re.match(r"ref: refs/heads/(.+)\s+HEAD", line)
|
|
98
|
+
if match:
|
|
99
|
+
head_ref = match.group(1)
|
|
100
|
+
break
|
|
101
|
+
return ("ok", head_ref or "HEAD reachable")
|
|
102
|
+
status, detail = classify_failure(result.stderr, transport)
|
|
103
|
+
return (status, detail)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def load_spec(path: Path) -> list[dict[str, str]]:
|
|
107
|
+
with path.open("rb") as fh:
|
|
108
|
+
doc = tomllib.load(fh)
|
|
109
|
+
return list(doc.get("repos", []))
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def main() -> int:
|
|
113
|
+
args = parse_args()
|
|
114
|
+
repos = load_spec(args.workspace_spec)
|
|
115
|
+
rows: list[dict[str, str]] = []
|
|
116
|
+
exit_code = 0
|
|
117
|
+
for repo in repos:
|
|
118
|
+
url = repo["url"]
|
|
119
|
+
transport = detect_transport(url)
|
|
120
|
+
status, detail = probe_url(url)
|
|
121
|
+
if status != "ok":
|
|
122
|
+
exit_code = 1
|
|
123
|
+
rows.append(
|
|
124
|
+
{
|
|
125
|
+
"repo": repo["name"],
|
|
126
|
+
"url": url,
|
|
127
|
+
"transport": transport,
|
|
128
|
+
"status": status,
|
|
129
|
+
"detail": detail,
|
|
130
|
+
"recommendation": recommendation(status, transport),
|
|
131
|
+
}
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
if args.json:
|
|
135
|
+
print(json.dumps(rows, indent=2))
|
|
136
|
+
else:
|
|
137
|
+
print("REPO\tTRANSPORT\tSTATUS\tDETAIL\tNEXT")
|
|
138
|
+
for row in rows:
|
|
139
|
+
print(
|
|
140
|
+
f'{row["repo"]}\t{row["transport"]}\t{row["status"]}\t'
|
|
141
|
+
f'{row["detail"]}\t{row["recommendation"]}'
|
|
142
|
+
)
|
|
143
|
+
return exit_code
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
if __name__ == "__main__":
|
|
147
|
+
sys.exit(main())
|
gr2/python_cli/add.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""Native single-repository staging for the Python gr2 CLI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from .gitops import git
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class AddError(Exception):
|
|
12
|
+
pass
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass(frozen=True)
|
|
16
|
+
class AddResult:
|
|
17
|
+
requested_paths: tuple[str, ...]
|
|
18
|
+
staged_files: tuple[str, ...]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def stage_files(repo: Path, paths: list[str]) -> AddResult:
|
|
22
|
+
"""Stage only the requested pathspecs and report their observed index rows.
|
|
23
|
+
|
|
24
|
+
``git add --all`` is deliberate. A deleted tracked path does not exist on
|
|
25
|
+
disk, so a filesystem existence precheck turns a valid deletion into a
|
|
26
|
+
false "missing" error. Git's index is the authority for pathspec validity.
|
|
27
|
+
"""
|
|
28
|
+
if not paths:
|
|
29
|
+
raise AddError("at least one path is required")
|
|
30
|
+
|
|
31
|
+
requested = tuple(paths)
|
|
32
|
+
try:
|
|
33
|
+
staged = git(repo, "add", "--all", "--", *requested)
|
|
34
|
+
except OSError as exc:
|
|
35
|
+
raise AddError(f"failed to launch git add in {repo}: {exc}") from exc
|
|
36
|
+
if staged.returncode != 0:
|
|
37
|
+
detail = (staged.stderr or staged.stdout).strip()
|
|
38
|
+
raise AddError(detail or f"git add failed with exit {staged.returncode}")
|
|
39
|
+
|
|
40
|
+
try:
|
|
41
|
+
observed = git(repo, "diff", "--cached", "--name-only", "-z", "--", *requested)
|
|
42
|
+
except OSError as exc:
|
|
43
|
+
raise AddError(
|
|
44
|
+
f"git add succeeded but staged-path evidence was unavailable: {exc}"
|
|
45
|
+
) from exc
|
|
46
|
+
if observed.returncode != 0:
|
|
47
|
+
detail = (observed.stderr or observed.stdout).strip()
|
|
48
|
+
raise AddError(detail or "git add succeeded but staged-path evidence was unavailable")
|
|
49
|
+
|
|
50
|
+
staged_files = tuple(sorted(name for name in observed.stdout.split("\0") if name))
|
|
51
|
+
return AddResult(requested_paths=requested, staged_files=staged_files)
|