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.
Files changed (80) hide show
  1. gitgrip-1.5.0.dist-info/METADATA +13 -0
  2. gitgrip-1.5.0.dist-info/RECORD +80 -0
  3. gitgrip-1.5.0.dist-info/WHEEL +5 -0
  4. gitgrip-1.5.0.dist-info/entry_points.txt +2 -0
  5. gitgrip-1.5.0.dist-info/top_level.txt +2 -0
  6. gr2/__init__.py +0 -0
  7. gr2/overlay/__init__.py +6 -0
  8. gr2/overlay/activate.py +196 -0
  9. gr2/overlay/agent_manifest.py +138 -0
  10. gr2/overlay/cli.py +181 -0
  11. gr2/overlay/cross_repo.py +124 -0
  12. gr2/overlay/drivers.py +113 -0
  13. gr2/overlay/introspection.py +155 -0
  14. gr2/overlay/language_drivers.py +115 -0
  15. gr2/overlay/objects.py +412 -0
  16. gr2/overlay/perf.py +251 -0
  17. gr2/overlay/refs.py +36 -0
  18. gr2/overlay/trust.py +150 -0
  19. gr2/overlay/types.py +69 -0
  20. gr2/overlay/units.py +313 -0
  21. gr2/overlay/workspace_spec.py +59 -0
  22. gr2/prototypes/__init__.py +0 -0
  23. gr2/prototypes/cache_materialization_probe.py +190 -0
  24. gr2/prototypes/concurrent_event_stress.py +199 -0
  25. gr2/prototypes/concurrent_lease_stress.py +240 -0
  26. gr2/prototypes/concurrent_workspace_cap_stress.py +231 -0
  27. gr2/prototypes/contribution_protocol.py +665 -0
  28. gr2/prototypes/cross_mode_lane_stress.py +986 -0
  29. gr2/prototypes/jsonl_store.py +158 -0
  30. gr2/prototypes/lane_workspace_prototype.py +2088 -0
  31. gr2/prototypes/layout_model_probe.py +139 -0
  32. gr2/prototypes/propagation_daemon.py +546 -0
  33. gr2/prototypes/propagation_state_machine.py +1478 -0
  34. gr2/prototypes/python_exec_playground.py +194 -0
  35. gr2/prototypes/python_hook_runtime_playground.py +240 -0
  36. gr2/prototypes/python_migration_playground.py +144 -0
  37. gr2/prototypes/python_review_checkout_playground.py +242 -0
  38. gr2/prototypes/python_spec_apply_playground.py +282 -0
  39. gr2/prototypes/real_git_lane_materialization.py +248 -0
  40. gr2/prototypes/real_git_playground.py +334 -0
  41. gr2/prototypes/recall_lane_history.py +274 -0
  42. gr2/prototypes/repo_maintenance_prototype.py +659 -0
  43. gr2/prototypes/repo_transport_probe.py +147 -0
  44. gr2/python_cli/__init__.py +2 -0
  45. gr2/python_cli/__main__.py +6 -0
  46. gr2/python_cli/add.py +51 -0
  47. gr2/python_cli/app.py +2516 -0
  48. gr2/python_cli/branch.py +67 -0
  49. gr2/python_cli/channel_bridge.py +131 -0
  50. gr2/python_cli/clone_exec.py +1019 -0
  51. gr2/python_cli/commit.py +199 -0
  52. gr2/python_cli/config.py +291 -0
  53. gr2/python_cli/env_exec.py +419 -0
  54. gr2/python_cli/events.py +529 -0
  55. gr2/python_cli/execops.py +372 -0
  56. gr2/python_cli/failures.py +98 -0
  57. gr2/python_cli/file_exec.py +256 -0
  58. gr2/python_cli/gitops.py +226 -0
  59. gr2/python_cli/grip.py +1337 -0
  60. gr2/python_cli/grip_cli.py +493 -0
  61. gr2/python_cli/hooks.py +450 -0
  62. gr2/python_cli/launch_exec.py +786 -0
  63. gr2/python_cli/merge_verification.py +274 -0
  64. gr2/python_cli/migration.py +985 -0
  65. gr2/python_cli/open_gr_review.py +699 -0
  66. gr2/python_cli/platform.py +441 -0
  67. gr2/python_cli/pr.py +487 -0
  68. gr2/python_cli/project_review.py +314 -0
  69. gr2/python_cli/prune.py +365 -0
  70. gr2/python_cli/push.py +172 -0
  71. gr2/python_cli/review.py +462 -0
  72. gr2/python_cli/review_ephemeral.py +143 -0
  73. gr2/python_cli/review_run.py +621 -0
  74. gr2/python_cli/spec_apply.py +1285 -0
  75. gr2/python_cli/staging_cleanup.py +205 -0
  76. gr2/python_cli/syncops.py +920 -0
  77. gr2/python_cli/target.py +100 -0
  78. gr2/python_cli/workspace_snapshot.py +105 -0
  79. gr2/schemas/gr2-materialization-plan-v1.schema.json +191 -0
  80. gr2_overlay/__init__.py +37 -0
@@ -0,0 +1,100 @@
1
+ """gr2 `target`: the stored PR target (`[settings].target` in the workspace spec).
2
+
3
+ This is the WRITER for the value gr2 `prune` already reads. gr1's `gr target set`
4
+ writes the gr1 manifest (`.gitgrip/...`), a different file; this writes
5
+ `.grip/workspace_spec.toml`, which is what gr2 reads.
6
+
7
+ The write is a faithful round-trip -- `tomllib.load` the whole spec, set (or
8
+ remove) `settings.target`, `tomli_w.dumps` the whole thing back, atomically --
9
+ so every other field (repos, units, workspace_name, an existing merge_method)
10
+ survives untouched. It deliberately does NOT go through `_write_workspace_spec`,
11
+ which regenerates the spec from repos/units alone and emits no `[settings]` table
12
+ at all (it would silently drop merge_method). The spec is machine-written, so the
13
+ round-trip dropping TOML comments is a non-issue -- there are none to keep.
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import os
19
+ import tempfile
20
+ import tomllib
21
+ from pathlib import Path
22
+
23
+ import tomli_w
24
+
25
+
26
+ class TargetError(Exception):
27
+ pass
28
+
29
+
30
+ _SPEC_REL = (".grip", "workspace_spec.toml")
31
+
32
+
33
+ def spec_path(workspace_root: Path) -> Path:
34
+ return workspace_root.joinpath(*_SPEC_REL)
35
+
36
+
37
+ def _load(workspace_root: Path) -> dict:
38
+ path = spec_path(workspace_root)
39
+ if not path.is_file():
40
+ raise TargetError(
41
+ f"no workspace spec at {path}: a stored target needs a workspace "
42
+ f"(run inside a gripspace, or set --target on prune directly)"
43
+ )
44
+ with path.open("rb") as fh:
45
+ return tomllib.load(fh)
46
+
47
+
48
+ def _settings(spec: dict) -> dict:
49
+ settings = spec.get("settings", {})
50
+ if not isinstance(settings, dict):
51
+ raise TargetError("workspace spec [settings] must be a table")
52
+ return settings
53
+
54
+
55
+ def _atomic_write(workspace_root: Path, spec: dict) -> None:
56
+ path = spec_path(workspace_root)
57
+ data = tomli_w.dumps(spec)
58
+ # Atomic: write a sibling temp file then os.replace, so a crash mid-write
59
+ # can never leave a truncated spec.
60
+ fd, tmp = tempfile.mkstemp(dir=str(path.parent), prefix=".workspace_spec.", suffix=".toml")
61
+ try:
62
+ with os.fdopen(fd, "w") as fh:
63
+ fh.write(data)
64
+ os.replace(tmp, path)
65
+ finally:
66
+ if os.path.exists(tmp):
67
+ os.unlink(tmp)
68
+
69
+
70
+ def show_target(workspace_root: Path) -> str | None:
71
+ """The stored `[settings].target`, or None if unset."""
72
+ value = _settings(_load(workspace_root)).get("target")
73
+ if value is None:
74
+ return None
75
+ if not isinstance(value, str):
76
+ raise TargetError("workspace setting target must be a string")
77
+ return value
78
+
79
+
80
+ def set_target(workspace_root: Path, branch: str) -> None:
81
+ """Write `[settings].target = <branch>`, preserving every other field."""
82
+ if not branch:
83
+ raise TargetError("target branch must be a non-empty name")
84
+ spec = _load(workspace_root)
85
+ spec.setdefault("settings", {})
86
+ if not isinstance(spec["settings"], dict):
87
+ raise TargetError("workspace spec [settings] must be a table")
88
+ spec["settings"]["target"] = branch
89
+ _atomic_write(workspace_root, spec)
90
+
91
+
92
+ def unset_target(workspace_root: Path) -> bool:
93
+ """Remove `[settings].target` if present. Returns True if something was removed."""
94
+ spec = _load(workspace_root)
95
+ settings = spec.get("settings")
96
+ if isinstance(settings, dict) and "target" in settings:
97
+ del settings["target"]
98
+ _atomic_write(workspace_root, spec)
99
+ return True
100
+ return False
@@ -0,0 +1,105 @@
1
+ """Capture a materialized lane's resolved repository state as one kind=workspace
2
+ gr commit (R2 Milestone 1, the section-5 semantic tree for the workspace kind).
3
+
4
+ The workspace gr commit is a reproduction coordinate: it records, per repo, the
5
+ exact head a checkout must materialize plus the base it builds on. A repo with
6
+ uncommitted changes is REFUSED, not recorded — snapshotting a dirty tree would
7
+ record a coordinate that does not reproduce the author's actual state. Commit
8
+ the lane first (``gr2 commit`` lane-aware), then snapshot.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from pathlib import Path
14
+
15
+ from gr2.prototypes import lane_workspace_prototype as lane_proto
16
+
17
+ from . import grip
18
+ from .gitops import git, repo_dirty
19
+
20
+
21
+ class WorkspaceSnapshotError(Exception):
22
+ pass
23
+
24
+
25
+ def _resolve_head(repo_root: Path, key: str, fork_base_sha: str | None) -> tuple[str, str]:
26
+ """(commit, base) for one lane repo.
27
+
28
+ `commit` is HEAD. `base` is the RECORDED fork base, never derived
29
+ from HEAD^: a lane with N commits must record where its work began, and HEAD^
30
+ is only the (N-1)th commit. A lane with no recorded fork base reads base as
31
+ unknown and is REFUSED here — the reproduction path never substitutes HEAD^ or
32
+ a live merge-base for a coordinate it does not actually know.
33
+ """
34
+ head = git(repo_root, "rev-parse", "--verify", "HEAD")
35
+ if head.returncode != 0 or not head.stdout.strip():
36
+ raise WorkspaceSnapshotError(f"repo {key} has no HEAD to snapshot")
37
+ commit = head.stdout.strip()
38
+ if not fork_base_sha:
39
+ raise WorkspaceSnapshotError(
40
+ f"repo {key} has no recorded fork base; base is unknown. Recreate the lane "
41
+ "with a recorded fork base; the snapshot will not substitute HEAD^."
42
+ )
43
+ # The recorded fork base must be a real commit in this repo and an ancestor of
44
+ # HEAD — otherwise it is not a coordinate HEAD actually builds on.
45
+ if git(repo_root, "rev-parse", "--verify", "--quiet", f"{fork_base_sha}^{{commit}}").returncode != 0:
46
+ raise WorkspaceSnapshotError(
47
+ f"repo {key} recorded fork base {fork_base_sha} is not a commit in this repo"
48
+ )
49
+ if git(repo_root, "merge-base", "--is-ancestor", fork_base_sha, commit).returncode != 0:
50
+ raise WorkspaceSnapshotError(
51
+ f"repo {key} recorded fork base {fork_base_sha} is not an ancestor of HEAD {commit}"
52
+ )
53
+ return commit, fork_base_sha
54
+
55
+
56
+ def resolve_lane_repos(workspace_root: Path, owner_unit: str, lane_name: str) -> list[dict[str, str]]:
57
+ """Resolve every repo of a materialized lane into the section-5 fields.
58
+
59
+ Refuses a repo with uncommitted changes (a snapshot must reproduce).
60
+ """
61
+ workspace_root = Path(workspace_root).resolve()
62
+ doc = lane_proto.load_lane_doc(workspace_root, owner_unit, lane_name)
63
+ fork_base = doc.get("fork_base", {})
64
+ spec = lane_proto.load_workspace_spec(workspace_root)
65
+ spec_by_name = {r.get("name"): r for r in spec.get("repos", [])}
66
+ lane_root = lane_proto.lane_dir(workspace_root, owner_unit, lane_name)
67
+
68
+ resolved: list[dict[str, str]] = []
69
+ for key in doc.get("repos", []):
70
+ repo_root = lane_root / "repos" / key
71
+ if not (repo_root / ".git").exists():
72
+ raise WorkspaceSnapshotError(f"repo {key} is not materialized at {repo_root}")
73
+ if repo_dirty(repo_root):
74
+ raise WorkspaceSnapshotError(
75
+ f"repo {key} has uncommitted changes; commit the lane before snapshotting"
76
+ )
77
+ repo_spec = spec_by_name.get(key)
78
+ if not repo_spec:
79
+ raise WorkspaceSnapshotError(f"repo {key} is not in the workspace spec")
80
+ commit, base = _resolve_head(repo_root, key, fork_base.get(key, {}).get("sha"))
81
+ resolved.append({
82
+ "key": key,
83
+ "remote": str(repo_spec.get("url", "")),
84
+ "path": str(repo_spec.get("path", "")),
85
+ "commit": commit,
86
+ "base": base,
87
+ })
88
+ return resolved
89
+
90
+
91
+ def snapshot_lane(workspace_root: Path, owner_unit: str, lane_name: str) -> str:
92
+ """Resolve a materialized lane and write one kind=workspace gr commit.
93
+
94
+ Returns the gr commit id (the reproduction coordinate).
95
+ """
96
+ workspace_root = Path(workspace_root).resolve()
97
+ repos = resolve_lane_repos(workspace_root, owner_unit, lane_name)
98
+ if not repos:
99
+ raise WorkspaceSnapshotError(f"lane {owner_unit}/{lane_name} has no repos to snapshot")
100
+ return grip.create_workspace_commit(workspace_root, repos)
101
+
102
+
103
+ def read_snapshot(workspace_root: Path, commit: str) -> list[dict[str, str]]:
104
+ """Decode a kind=workspace gr commit's resolved repository fields."""
105
+ return grip.read_workspace_commit(Path(workspace_root).resolve(), commit)
@@ -0,0 +1,191 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "urn:synapt:gr2:materialization-plan:v1",
4
+ "title": "gr2 MaterializationPlan v1",
5
+ "description": "Identity-free, one-opaque-unit materialization plan consumed by the neutral gr2 executor.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": [
9
+ "schema_version",
10
+ "plan_id",
11
+ "unit_key",
12
+ "workspace_spec_sha256",
13
+ "operations"
14
+ ],
15
+ "properties": {
16
+ "schema_version": {
17
+ "const": 1
18
+ },
19
+ "plan_id": {
20
+ "$ref": "#/$defs/opaqueToken"
21
+ },
22
+ "unit_key": {
23
+ "$ref": "#/$defs/opaqueToken"
24
+ },
25
+ "workspace_spec_sha256": {
26
+ "$ref": "#/$defs/sha256"
27
+ },
28
+ "operations": {
29
+ "type": "array",
30
+ "minItems": 1,
31
+ "items": {
32
+ "oneOf": [
33
+ {
34
+ "$ref": "#/$defs/cloneOperation"
35
+ },
36
+ {
37
+ "$ref": "#/$defs/venvOperation"
38
+ },
39
+ {
40
+ "$ref": "#/$defs/editableInstallOperation"
41
+ },
42
+ {
43
+ "$ref": "#/$defs/projectFileOperation"
44
+ }
45
+ ]
46
+ }
47
+ }
48
+ },
49
+ "$defs": {
50
+ "opaqueToken": {
51
+ "type": "string",
52
+ "minLength": 1,
53
+ "maxLength": 128,
54
+ "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$"
55
+ },
56
+ "sha256": {
57
+ "type": "string",
58
+ "pattern": "^[0-9a-f]{64}$"
59
+ },
60
+ "nonEmptyString": {
61
+ "type": "string",
62
+ "minLength": 1,
63
+ "maxLength": 2048,
64
+ "pattern": "^[^\\u0000]+$"
65
+ },
66
+ "workspaceRelativePath": {
67
+ "type": "string",
68
+ "minLength": 1,
69
+ "maxLength": 1024,
70
+ "pattern": "^(?![~/])(?!.*\\\\)(?!.*//)(?!.*(?:^|/)\\.{1,2}(?:/|$))[^\\u0000]+$"
71
+ },
72
+ "cacheReferencePath": {
73
+ "type": "string",
74
+ "pattern": "^\\.grip/cache/repos/[A-Za-z0-9][A-Za-z0-9._-]{0,127}\\.git$"
75
+ },
76
+ "stagedInputPath": {
77
+ "type": "string",
78
+ "pattern": "^\\.grip/staging/inputs/[A-Za-z0-9][A-Za-z0-9._-]{0,127}$"
79
+ },
80
+ "cloneOperation": {
81
+ "type": "object",
82
+ "additionalProperties": false,
83
+ "required": [
84
+ "kind",
85
+ "repo_url",
86
+ "dest_path",
87
+ "branch"
88
+ ],
89
+ "properties": {
90
+ "kind": {
91
+ "const": "clone"
92
+ },
93
+ "repo_url": {
94
+ "$ref": "#/$defs/nonEmptyString"
95
+ },
96
+ "dest_path": {
97
+ "$ref": "#/$defs/workspaceRelativePath"
98
+ },
99
+ "branch": {
100
+ "$ref": "#/$defs/nonEmptyString"
101
+ },
102
+ "reference_base": {
103
+ "$ref": "#/$defs/cacheReferencePath"
104
+ }
105
+ }
106
+ },
107
+ "venvOperation": {
108
+ "type": "object",
109
+ "additionalProperties": false,
110
+ "required": [
111
+ "kind",
112
+ "dest_path",
113
+ "engine",
114
+ "python"
115
+ ],
116
+ "properties": {
117
+ "kind": {
118
+ "const": "venv"
119
+ },
120
+ "dest_path": {
121
+ "$ref": "#/$defs/workspaceRelativePath"
122
+ },
123
+ "engine": {
124
+ "const": "uv"
125
+ },
126
+ "python": {
127
+ "$ref": "#/$defs/nonEmptyString"
128
+ }
129
+ }
130
+ },
131
+ "editableInstallOperation": {
132
+ "type": "object",
133
+ "additionalProperties": false,
134
+ "required": [
135
+ "kind",
136
+ "venv_path",
137
+ "source_path",
138
+ "extras"
139
+ ],
140
+ "properties": {
141
+ "kind": {
142
+ "const": "editable_install"
143
+ },
144
+ "venv_path": {
145
+ "$ref": "#/$defs/workspaceRelativePath"
146
+ },
147
+ "source_path": {
148
+ "$ref": "#/$defs/workspaceRelativePath"
149
+ },
150
+ "extras": {
151
+ "type": "array",
152
+ "uniqueItems": true,
153
+ "items": {
154
+ "type": "string",
155
+ "minLength": 1,
156
+ "maxLength": 128,
157
+ "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$"
158
+ }
159
+ }
160
+ }
161
+ },
162
+ "projectFileOperation": {
163
+ "type": "object",
164
+ "additionalProperties": false,
165
+ "required": [
166
+ "kind",
167
+ "source_path",
168
+ "source_sha256",
169
+ "dest_path",
170
+ "mode"
171
+ ],
172
+ "properties": {
173
+ "kind": {
174
+ "const": "project_file"
175
+ },
176
+ "source_path": {
177
+ "$ref": "#/$defs/stagedInputPath"
178
+ },
179
+ "source_sha256": {
180
+ "$ref": "#/$defs/sha256"
181
+ },
182
+ "dest_path": {
183
+ "$ref": "#/$defs/workspaceRelativePath"
184
+ },
185
+ "mode": {
186
+ "const": "copy"
187
+ }
188
+ }
189
+ }
190
+ }
191
+ }
@@ -0,0 +1,37 @@
1
+ """Deprecated compatibility shim: ``gr2_overlay`` was renamed to ``gr2.overlay``.
2
+
3
+ Import ``gr2.overlay`` instead. Every submodule of the real package is aliased
4
+ through ``sys.modules`` here, so ``gr2_overlay.units`` IS ``gr2.overlay.units``
5
+ (the same module object, not a parallel copy) — a consumer that has not yet
6
+ migrated keeps working with no behavioural drift. Removed in the release after
7
+ 1.5.0; downstream consumers migrate to ``gr2.overlay`` before then.
8
+ """
9
+ from __future__ import annotations
10
+
11
+ import importlib as _importlib
12
+ import pkgutil as _pkgutil
13
+ import sys as _sys
14
+ import warnings as _warnings
15
+
16
+ _warnings.warn(
17
+ "gr2_overlay is deprecated and will be removed in the release after 1.5.0; "
18
+ "import gr2.overlay instead.",
19
+ DeprecationWarning,
20
+ stacklevel=2,
21
+ )
22
+
23
+ _real = _importlib.import_module("gr2.overlay")
24
+
25
+ # Alias every submodule so `import gr2_overlay.X` and `from gr2_overlay.X import Y`
26
+ # resolve to the SAME module object as gr2.overlay.X. Discovered dynamically from
27
+ # the real package so a new submodule is covered without editing this shim.
28
+ for _info in _pkgutil.iter_modules(_real.__path__):
29
+ _mod = _importlib.import_module(f"gr2.overlay.{_info.name}")
30
+ _sys.modules[f"{__name__}.{_info.name}"] = _mod
31
+ globals()[_info.name] = _mod
32
+
33
+ # Re-export the real package's declared public names (`from gr2_overlay import X`).
34
+ for _attr in getattr(_real, "__all__", ()):
35
+ globals()[_attr] = getattr(_real, _attr)
36
+
37
+ del _importlib, _pkgutil, _sys, _warnings, _info, _mod, _real