@opengsd/gsd-path 1.6.2 → 1.7.1
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/CHANGELOG.md +30 -0
- package/DOCS.md +27 -1
- package/FULL.md +4 -3
- package/HOOKS.md +38 -4
- package/QUICK.md +1 -1
- package/README.md +19 -11
- package/RUNTIME.md +5 -3
- package/WORKFLOW.md +11 -4
- package/package.json +1 -1
- package/scripts/_common.py +76 -2
- package/scripts/archive_milestone.py +129 -7
- package/scripts/build_recovery.py +25 -2
- package/scripts/build_state.py +333 -8
- package/scripts/check_handoffs.py +437 -30
- package/scripts/check_task_briefs.py +135 -11
- package/scripts/discussion_validate.py +46 -10
- package/scripts/dispatch_driver.py +33 -3
- package/scripts/git_guard.py +44 -4
- package/scripts/guard_hook.py +456 -9
- package/scripts/install.py +34 -4
- package/scripts/integration.py +196 -39
- package/scripts/isolation.py +1000 -38
- package/scripts/lean_verification.py +28 -13
- package/scripts/loop_run.py +9 -4
- package/scripts/members.py +17 -3
- package/scripts/pipeline_diagnose.py +104 -1
- package/scripts/pipeline_state.py +10 -2
- package/scripts/runtime_store.py +88 -3
- package/scripts/state_checkpoint.py +15 -0
- package/scripts/status_runtime.py +73 -2
- package/scripts/workflow_run.py +10 -3
- package/scripts/worktree_paths.py +45 -0
- package/skills/gsd-path/BUILD.md +4 -1
- package/skills/gsd-path/DEFINE.md +2 -1
- package/skills/gsd-path/PLAN.md +13 -3
- package/skills/gsd-path/SHIP.md +41 -8
- package/skills/gsd-path/SKILL.md +16 -1
- package/skills/gsd-path/references/build-native.md +28 -7
- package/skills/gsd-path/references/coder.md +6 -3
- package/skills/gsd-path/references/planner.md +4 -3
- package/skills/gsd-path/references/reviewer-final.md +10 -0
- package/skills/gsd-path/references/reviewer-wave.md +71 -27
- package/skills/gsd-path/references/reviewer.md +21 -8
- package/skills/gsd-path/references/runtime-selection.md +36 -0
- package/skills/gsd-path/references/spec-probes.md +19 -1
- package/skills/gsd-path/scripts/_common.py +76 -2
- package/skills/gsd-path/scripts/archive_milestone.py +129 -7
- package/skills/gsd-path/scripts/build_recovery.py +25 -2
- package/skills/gsd-path/scripts/build_state.py +333 -8
- package/skills/gsd-path/scripts/check_handoffs.py +437 -30
- package/skills/gsd-path/scripts/check_task_briefs.py +135 -11
- package/skills/gsd-path/scripts/discussion_validate.py +46 -10
- package/skills/gsd-path/scripts/dispatch_driver.py +33 -3
- package/skills/gsd-path/scripts/git_guard.py +44 -4
- package/skills/gsd-path/scripts/integration.py +196 -39
- package/skills/gsd-path/scripts/isolation.py +1000 -38
- package/skills/gsd-path/scripts/lean_verification.py +28 -13
- package/skills/gsd-path/scripts/loop_run.py +9 -4
- package/skills/gsd-path/scripts/members.py +17 -3
- package/skills/gsd-path/scripts/pipeline_state.py +10 -2
- package/skills/gsd-path/scripts/state_checkpoint.py +15 -0
- package/skills/gsd-path/scripts/status_runtime.py +73 -2
- package/skills/gsd-path/scripts/workflow_run.py +10 -3
- package/skills/gsd-path/scripts/worktree_paths.py +45 -0
- package/skills/gsd-path/templates/intent.md +5 -3
- package/skills/gsd-path/templates/plan.md +3 -2
- package/skills/gsd-path/templates/task.md +8 -1
- package/skills/gsd-path/templates/wave-review.md +24 -8
- package/skills/gsd-path-build/SKILL.md +4 -1
- package/skills/gsd-path-build/references/build-native.md +28 -7
- package/skills/gsd-path-build/references/coder.md +6 -3
- package/skills/gsd-path-build/references/reviewer-final.md +10 -0
- package/skills/gsd-path-build/references/reviewer-wave.md +71 -27
- package/skills/gsd-path-build/references/reviewer.md +21 -8
- package/skills/gsd-path-build/references/runtime-selection.md +36 -0
- package/skills/gsd-path-build/references/spec-probes.md +19 -1
- package/skills/gsd-path-build/scripts/_common.py +76 -2
- package/skills/gsd-path-build/scripts/archive_milestone.py +129 -7
- package/skills/gsd-path-build/scripts/build_recovery.py +25 -2
- package/skills/gsd-path-build/scripts/build_state.py +333 -8
- package/skills/gsd-path-build/scripts/check_handoffs.py +437 -30
- package/skills/gsd-path-build/scripts/check_task_briefs.py +135 -11
- package/skills/gsd-path-build/scripts/discussion_validate.py +46 -10
- package/skills/gsd-path-build/scripts/dispatch_driver.py +33 -3
- package/skills/gsd-path-build/scripts/integration.py +196 -39
- package/skills/gsd-path-build/scripts/isolation.py +1000 -38
- package/skills/gsd-path-build/scripts/loop_run.py +9 -4
- package/skills/gsd-path-build/scripts/members.py +17 -3
- package/skills/gsd-path-build/scripts/pipeline_state.py +10 -2
- package/skills/gsd-path-build/scripts/state_checkpoint.py +15 -0
- package/skills/gsd-path-build/scripts/workflow_run.py +10 -3
- package/skills/gsd-path-build/scripts/worktree_paths.py +45 -0
- package/skills/gsd-path-build/templates/task.md +8 -1
- package/skills/gsd-path-build/templates/wave-review.md +24 -8
- package/skills/gsd-path-decide/references/runtime-selection.md +36 -0
- package/skills/gsd-path-decide/scripts/_common.py +76 -2
- package/skills/gsd-path-decide/scripts/archive_milestone.py +129 -7
- package/skills/gsd-path-decide/scripts/build_recovery.py +25 -2
- package/skills/gsd-path-decide/scripts/check_handoffs.py +437 -30
- package/skills/gsd-path-decide/scripts/check_task_briefs.py +135 -11
- package/skills/gsd-path-decide/scripts/discussion_validate.py +46 -10
- package/skills/gsd-path-decide/scripts/integration.py +196 -39
- package/skills/gsd-path-decide/scripts/isolation.py +1000 -38
- package/skills/gsd-path-decide/scripts/loop_run.py +9 -4
- package/skills/gsd-path-decide/scripts/members.py +17 -3
- package/skills/gsd-path-decide/scripts/pipeline_state.py +10 -2
- package/skills/gsd-path-decide/scripts/state_checkpoint.py +15 -0
- package/skills/gsd-path-decide/scripts/worktree_paths.py +45 -0
- package/skills/gsd-path-define/SKILL.md +2 -1
- package/skills/gsd-path-define/references/runtime-selection.md +36 -0
- package/skills/gsd-path-define/references/spec-probes.md +19 -1
- package/skills/gsd-path-define/scripts/_common.py +76 -2
- package/skills/gsd-path-define/scripts/archive_milestone.py +129 -7
- package/skills/gsd-path-define/scripts/build_recovery.py +25 -2
- package/skills/gsd-path-define/scripts/check_handoffs.py +437 -30
- package/skills/gsd-path-define/scripts/check_task_briefs.py +135 -11
- package/skills/gsd-path-define/scripts/discussion_validate.py +46 -10
- package/skills/gsd-path-define/scripts/integration.py +196 -39
- package/skills/gsd-path-define/scripts/isolation.py +1000 -38
- package/skills/gsd-path-define/scripts/loop_run.py +9 -4
- package/skills/gsd-path-define/scripts/members.py +17 -3
- package/skills/gsd-path-define/scripts/pipeline_state.py +10 -2
- package/skills/gsd-path-define/scripts/state_checkpoint.py +15 -0
- package/skills/gsd-path-define/scripts/worktree_paths.py +45 -0
- package/skills/gsd-path-define/templates/intent.md +5 -3
- package/skills/gsd-path-discuss/references/runtime-selection.md +36 -0
- package/skills/gsd-path-discuss/scripts/_common.py +76 -2
- package/skills/gsd-path-discuss/scripts/loop_run.py +9 -4
- package/skills/gsd-path-discuss/scripts/worktree_paths.py +45 -0
- package/skills/gsd-path-docs-audit/references/runtime-selection.md +36 -0
- package/skills/gsd-path-docs-audit/scripts/_common.py +76 -2
- package/skills/gsd-path-docs-audit/scripts/archive_milestone.py +129 -7
- package/skills/gsd-path-docs-audit/scripts/build_recovery.py +25 -2
- package/skills/gsd-path-docs-audit/scripts/check_handoffs.py +437 -30
- package/skills/gsd-path-docs-audit/scripts/check_task_briefs.py +135 -11
- package/skills/gsd-path-docs-audit/scripts/discussion_validate.py +46 -10
- package/skills/gsd-path-docs-audit/scripts/integration.py +196 -39
- package/skills/gsd-path-docs-audit/scripts/isolation.py +1000 -38
- package/skills/gsd-path-docs-audit/scripts/loop_run.py +9 -4
- package/skills/gsd-path-docs-audit/scripts/members.py +17 -3
- package/skills/gsd-path-docs-audit/scripts/pipeline_state.py +10 -2
- package/skills/gsd-path-docs-audit/scripts/state_checkpoint.py +15 -0
- package/skills/gsd-path-docs-audit/scripts/worktree_paths.py +45 -0
- package/skills/gsd-path-forensics/references/runtime-selection.md +36 -0
- package/skills/gsd-path-forensics/scripts/_common.py +76 -2
- package/skills/gsd-path-forensics/scripts/loop_run.py +9 -4
- package/skills/gsd-path-forensics/scripts/pipeline_diagnose.py +104 -1
- package/skills/gsd-path-forensics/scripts/worktree_paths.py +45 -0
- package/skills/gsd-path-inspect/references/runtime-selection.md +36 -0
- package/skills/gsd-path-inspect/scripts/_common.py +76 -2
- package/skills/gsd-path-inspect/scripts/archive_milestone.py +129 -7
- package/skills/gsd-path-inspect/scripts/build_recovery.py +25 -2
- package/skills/gsd-path-inspect/scripts/check_handoffs.py +437 -30
- package/skills/gsd-path-inspect/scripts/check_task_briefs.py +135 -11
- package/skills/gsd-path-inspect/scripts/discussion_validate.py +46 -10
- package/skills/gsd-path-inspect/scripts/integration.py +196 -39
- package/skills/gsd-path-inspect/scripts/isolation.py +1000 -38
- package/skills/gsd-path-inspect/scripts/loop_run.py +9 -4
- package/skills/gsd-path-inspect/scripts/members.py +17 -3
- package/skills/gsd-path-inspect/scripts/pipeline_state.py +10 -2
- package/skills/gsd-path-inspect/scripts/state_checkpoint.py +15 -0
- package/skills/gsd-path-inspect/scripts/workflow_run.py +10 -3
- package/skills/gsd-path-inspect/scripts/worktree_paths.py +45 -0
- package/skills/gsd-path-loop/references/runtime-selection.md +36 -0
- package/skills/gsd-path-loop/scripts/_common.py +76 -2
- package/skills/gsd-path-loop/scripts/loop_run.py +9 -4
- package/skills/gsd-path-loop/scripts/worktree_paths.py +45 -0
- package/skills/gsd-path-migrate/references/runtime-selection.md +36 -0
- package/skills/gsd-path-migrate/scripts/_common.py +76 -2
- package/skills/gsd-path-migrate/scripts/loop_run.py +9 -4
- package/skills/gsd-path-plan/SKILL.md +13 -3
- package/skills/gsd-path-plan/references/planner.md +4 -3
- package/skills/gsd-path-plan/references/runtime-selection.md +36 -0
- package/skills/gsd-path-plan/references/spec-probes.md +19 -1
- package/skills/gsd-path-plan/scripts/_common.py +76 -2
- package/skills/gsd-path-plan/scripts/archive_milestone.py +129 -7
- package/skills/gsd-path-plan/scripts/build_recovery.py +25 -2
- package/skills/gsd-path-plan/scripts/check_handoffs.py +437 -30
- package/skills/gsd-path-plan/scripts/check_task_briefs.py +135 -11
- package/skills/gsd-path-plan/scripts/discussion_validate.py +46 -10
- package/skills/gsd-path-plan/scripts/integration.py +196 -39
- package/skills/gsd-path-plan/scripts/isolation.py +1000 -38
- package/skills/gsd-path-plan/scripts/loop_run.py +9 -4
- package/skills/gsd-path-plan/scripts/members.py +17 -3
- package/skills/gsd-path-plan/scripts/pipeline_state.py +10 -2
- package/skills/gsd-path-plan/scripts/state_checkpoint.py +15 -0
- package/skills/gsd-path-plan/scripts/workflow_run.py +10 -3
- package/skills/gsd-path-plan/scripts/worktree_paths.py +45 -0
- package/skills/gsd-path-plan/templates/plan.md +3 -2
- package/skills/gsd-path-plan/templates/task.md +8 -1
- package/skills/gsd-path-research/references/runtime-selection.md +36 -0
- package/skills/gsd-path-research/scripts/_common.py +76 -2
- package/skills/gsd-path-research/scripts/archive_milestone.py +129 -7
- package/skills/gsd-path-research/scripts/build_recovery.py +25 -2
- package/skills/gsd-path-research/scripts/check_handoffs.py +437 -30
- package/skills/gsd-path-research/scripts/check_task_briefs.py +135 -11
- package/skills/gsd-path-research/scripts/discussion_validate.py +46 -10
- package/skills/gsd-path-research/scripts/integration.py +196 -39
- package/skills/gsd-path-research/scripts/isolation.py +1000 -38
- package/skills/gsd-path-research/scripts/loop_run.py +9 -4
- package/skills/gsd-path-research/scripts/members.py +17 -3
- package/skills/gsd-path-research/scripts/pipeline_state.py +10 -2
- package/skills/gsd-path-research/scripts/state_checkpoint.py +15 -0
- package/skills/gsd-path-research/scripts/worktree_paths.py +45 -0
- package/skills/gsd-path-roadmap/references/runtime-selection.md +36 -0
- package/skills/gsd-path-roadmap/scripts/_common.py +76 -2
- package/skills/gsd-path-roadmap/scripts/archive_milestone.py +129 -7
- package/skills/gsd-path-roadmap/scripts/build_recovery.py +25 -2
- package/skills/gsd-path-roadmap/scripts/check_handoffs.py +437 -30
- package/skills/gsd-path-roadmap/scripts/check_task_briefs.py +135 -11
- package/skills/gsd-path-roadmap/scripts/discussion_validate.py +46 -10
- package/skills/gsd-path-roadmap/scripts/integration.py +196 -39
- package/skills/gsd-path-roadmap/scripts/isolation.py +1000 -38
- package/skills/gsd-path-roadmap/scripts/loop_run.py +9 -4
- package/skills/gsd-path-roadmap/scripts/members.py +17 -3
- package/skills/gsd-path-roadmap/scripts/pipeline_state.py +10 -2
- package/skills/gsd-path-roadmap/scripts/state_checkpoint.py +15 -0
- package/skills/gsd-path-roadmap/scripts/worktree_paths.py +45 -0
- package/skills/gsd-path-ship/SKILL.md +41 -8
- package/skills/gsd-path-ship/references/reviewer-final.md +10 -0
- package/skills/gsd-path-ship/references/reviewer-wave.md +71 -27
- package/skills/gsd-path-ship/references/reviewer.md +21 -8
- package/skills/gsd-path-ship/references/runtime-selection.md +36 -0
- package/skills/gsd-path-ship/references/spec-probes.md +19 -1
- package/skills/gsd-path-ship/scripts/_common.py +76 -2
- package/skills/gsd-path-ship/scripts/archive_milestone.py +129 -7
- package/skills/gsd-path-ship/scripts/build_recovery.py +25 -2
- package/skills/gsd-path-ship/scripts/build_state.py +333 -8
- package/skills/gsd-path-ship/scripts/check_handoffs.py +437 -30
- package/skills/gsd-path-ship/scripts/check_task_briefs.py +135 -11
- package/skills/gsd-path-ship/scripts/discussion_validate.py +46 -10
- package/skills/gsd-path-ship/scripts/integration.py +196 -39
- package/skills/gsd-path-ship/scripts/isolation.py +1000 -38
- package/skills/gsd-path-ship/scripts/lean_verification.py +28 -13
- package/skills/gsd-path-ship/scripts/loop_run.py +9 -4
- package/skills/gsd-path-ship/scripts/members.py +17 -3
- package/skills/gsd-path-ship/scripts/pipeline_state.py +10 -2
- package/skills/gsd-path-ship/scripts/state_checkpoint.py +15 -0
- package/skills/gsd-path-ship/scripts/workflow_run.py +10 -3
- package/skills/gsd-path-ship/scripts/worktree_paths.py +45 -0
- package/skills/gsd-path-ship/templates/wave-review.md +24 -8
- package/skills/gsd-path-undo/references/runtime-selection.md +36 -0
- package/skills/gsd-path-undo/scripts/_common.py +76 -2
- package/skills/gsd-path-undo/scripts/loop_run.py +9 -4
- package/skills/gsd-path-undo/scripts/worktree_paths.py +45 -0
- package/skills/path/BUILD.md +4 -1
- package/skills/path/DEFINE.md +2 -1
- package/skills/path/PLAN.md +13 -3
- package/skills/path/SHIP.md +41 -8
- package/skills/path/SKILL.md +16 -1
- package/skills/path/references/build-native.md +28 -7
- package/skills/path/references/coder.md +6 -3
- package/skills/path/references/planner.md +4 -3
- package/skills/path/references/reviewer-final.md +10 -0
- package/skills/path/references/reviewer-wave.md +71 -27
- package/skills/path/references/reviewer.md +21 -8
- package/skills/path/references/runtime-selection.md +36 -0
- package/skills/path/references/spec-probes.md +19 -1
- package/skills/path/scripts/_common.py +76 -2
- package/skills/path/scripts/archive_milestone.py +129 -7
- package/skills/path/scripts/build_recovery.py +25 -2
- package/skills/path/scripts/build_state.py +333 -8
- package/skills/path/scripts/check_handoffs.py +437 -30
- package/skills/path/scripts/check_task_briefs.py +135 -11
- package/skills/path/scripts/discussion_validate.py +46 -10
- package/skills/path/scripts/dispatch_driver.py +33 -3
- package/skills/path/scripts/git_guard.py +44 -4
- package/skills/path/scripts/integration.py +196 -39
- package/skills/path/scripts/isolation.py +1000 -38
- package/skills/path/scripts/lean_verification.py +28 -13
- package/skills/path/scripts/loop_run.py +9 -4
- package/skills/path/scripts/members.py +17 -3
- package/skills/path/scripts/pipeline_state.py +10 -2
- package/skills/path/scripts/state_checkpoint.py +15 -0
- package/skills/path/scripts/status_runtime.py +73 -2
- package/skills/path/scripts/workflow_run.py +10 -3
- package/skills/path/scripts/worktree_paths.py +45 -0
- package/skills/path/templates/intent.md +5 -3
- package/skills/path/templates/plan.md +3 -2
- package/skills/path/templates/task.md +8 -1
- package/skills/path/templates/wave-review.md +24 -8
package/scripts/integration.py
CHANGED
|
@@ -11,7 +11,7 @@ import json
|
|
|
11
11
|
import re
|
|
12
12
|
import contextlib
|
|
13
13
|
from pathlib import Path, PurePosixPath
|
|
14
|
-
from typing import Optional
|
|
14
|
+
from typing import Mapping, Optional
|
|
15
15
|
|
|
16
16
|
if __package__:
|
|
17
17
|
from .pipeline_git import (
|
|
@@ -25,7 +25,7 @@ if __package__:
|
|
|
25
25
|
ship_subject,
|
|
26
26
|
)
|
|
27
27
|
from .pipeline_state import PipelineState
|
|
28
|
-
from .isolation import publication_base
|
|
28
|
+
from .isolation import publication_base, _unlink_link_like, IsolationError
|
|
29
29
|
from . import _common
|
|
30
30
|
from . import worktree_paths
|
|
31
31
|
else:
|
|
@@ -41,7 +41,7 @@ else:
|
|
|
41
41
|
ship_subject,
|
|
42
42
|
)
|
|
43
43
|
from pipeline_state import PipelineState
|
|
44
|
-
from isolation import publication_base
|
|
44
|
+
from isolation import publication_base, _unlink_link_like, IsolationError
|
|
45
45
|
import _common
|
|
46
46
|
import worktree_paths
|
|
47
47
|
except ImportError: # pragma: no cover - package import used by tests
|
|
@@ -56,7 +56,7 @@ else:
|
|
|
56
56
|
ship_subject,
|
|
57
57
|
)
|
|
58
58
|
from scripts.pipeline_state import PipelineState
|
|
59
|
-
from scripts.isolation import publication_base
|
|
59
|
+
from scripts.isolation import publication_base, _unlink_link_like, IsolationError
|
|
60
60
|
from scripts import _common
|
|
61
61
|
from scripts import worktree_paths
|
|
62
62
|
|
|
@@ -79,6 +79,13 @@ run_git = _common.run_git
|
|
|
79
79
|
run_command = _common.run_command
|
|
80
80
|
|
|
81
81
|
|
|
82
|
+
def _defuse_links(tree: Path) -> None:
|
|
83
|
+
try:
|
|
84
|
+
_unlink_link_like(tree) # never traverse links (#462)
|
|
85
|
+
except IsolationError as error:
|
|
86
|
+
raise ArchiveError(str(error)) from error
|
|
87
|
+
|
|
88
|
+
|
|
82
89
|
def resolve_remote_default(project: Path) -> str:
|
|
83
90
|
result = run_git(project, "symbolic-ref", "--quiet", "refs/remotes/origin/HEAD")
|
|
84
91
|
remote_ref = result.stdout.strip()
|
|
@@ -107,12 +114,25 @@ def live_remote_ref(project: Path, ref: str) -> Optional[str]:
|
|
|
107
114
|
return rows[0][0]
|
|
108
115
|
|
|
109
116
|
|
|
110
|
-
def publishable_bound_branch(
|
|
117
|
+
def publishable_bound_branch(
|
|
118
|
+
project: Path, branch: str, ship_commit: str, adopt_published: Optional[str] = None
|
|
119
|
+
) -> Optional[str]:
|
|
111
120
|
"""The live origin SHA the bound ref may move from: absent, the ship
|
|
112
|
-
commit,
|
|
121
|
+
commit, an owner-adopted rebased head proven by its archived receipt, or a
|
|
122
|
+
user-attested strict ancestor adopted through --adopt-published-bound
|
|
123
|
+
(#464: a build task legitimately pushed the bound branch as a deploy pin)."""
|
|
113
124
|
remote_sha = live_remote_ref(project, f"refs/heads/{branch}")
|
|
114
125
|
if remote_sha is None or remote_sha == ship_commit:
|
|
115
126
|
return remote_sha
|
|
127
|
+
if adopt_published is not None:
|
|
128
|
+
if remote_sha != adopt_published:
|
|
129
|
+
raise ArchiveError(
|
|
130
|
+
f"origin/{branch} moved or collides: {remote_sha} != {adopt_published}")
|
|
131
|
+
if run_git(project, "merge-base", "--is-ancestor", remote_sha, ship_commit).returncode != 0:
|
|
132
|
+
raise ArchiveError(
|
|
133
|
+
f"origin/{branch} {remote_sha} diverges from the ship commit; "
|
|
134
|
+
"an adoption cannot rewind")
|
|
135
|
+
return remote_sha
|
|
116
136
|
try:
|
|
117
137
|
approved_base = publication_base(project, branch, ship_commit)
|
|
118
138
|
except (RuntimeError, OSError) as error:
|
|
@@ -122,9 +142,11 @@ def publishable_bound_branch(project: Path, branch: str, ship_commit: str) -> Op
|
|
|
122
142
|
return remote_sha
|
|
123
143
|
|
|
124
144
|
|
|
125
|
-
def publish_bound_branch(
|
|
145
|
+
def publish_bound_branch(
|
|
146
|
+
project: Path, branch: str, ship_commit: str, adopt_published: Optional[str] = None
|
|
147
|
+
) -> None:
|
|
126
148
|
remote_ref = f"refs/heads/{branch}"
|
|
127
|
-
remote_sha = publishable_bound_branch(project, branch, ship_commit)
|
|
149
|
+
remote_sha = publishable_bound_branch(project, branch, ship_commit, adopt_published)
|
|
128
150
|
if remote_sha != ship_commit:
|
|
129
151
|
# An absent ref carries an empty lease; an adopted head leases its exact SHA.
|
|
130
152
|
archive_milestone.require_git_success(
|
|
@@ -232,6 +254,8 @@ def remove_registered_worktree(project: Path, branch: str, expected_path: Path)
|
|
|
232
254
|
)
|
|
233
255
|
if status:
|
|
234
256
|
raise ArchiveError("integration worktree is dirty; refusing cleanup")
|
|
257
|
+
if registered.is_dir():
|
|
258
|
+
_defuse_links(registered)
|
|
235
259
|
archive_milestone.require_git_success(
|
|
236
260
|
run_git(project, "worktree", "remove", str(registered)),
|
|
237
261
|
"remove integration worktree",
|
|
@@ -792,17 +816,7 @@ def pull_request_tag_message(
|
|
|
792
816
|
|
|
793
817
|
|
|
794
818
|
def pull_request_tag_metadata(project: Path, tag_ref: str) -> dict[str, str]:
|
|
795
|
-
|
|
796
|
-
run_git(project, "for-each-ref", "--format=%(contents)", tag_ref),
|
|
797
|
-
"inspect pull-request milestone tag",
|
|
798
|
-
)
|
|
799
|
-
fields: dict[str, str] = {}
|
|
800
|
-
for line in message.splitlines():
|
|
801
|
-
match = re.fullmatch(r"(Mode|Pull-Request|Ship|Landing): (.+)", line)
|
|
802
|
-
if match:
|
|
803
|
-
if match.group(1) in fields:
|
|
804
|
-
raise ArchiveError("pull-request milestone tag repeats metadata")
|
|
805
|
-
fields[match.group(1)] = match.group(2)
|
|
819
|
+
fields = archive_milestone.tag_metadata_fields(project, tag_ref)
|
|
806
820
|
if set(fields) != {"Mode", "Pull-Request", "Ship", "Landing"}:
|
|
807
821
|
raise ArchiveError("pull-request milestone tag is missing metadata")
|
|
808
822
|
if fields["Mode"] != "pull-request":
|
|
@@ -1158,7 +1172,7 @@ def integrate_external_landing(
|
|
|
1158
1172
|
return validate_integrated(project, archive_milestone.milestone_slug(state))
|
|
1159
1173
|
|
|
1160
1174
|
|
|
1161
|
-
def integrate(repo: Path, slug: str) -> dict:
|
|
1175
|
+
def integrate(repo: Path, slug: str, adopt_published: Optional[str] = None) -> dict:
|
|
1162
1176
|
project = repo.resolve()
|
|
1163
1177
|
active_root = archive_milestone.require_project_layout(project)
|
|
1164
1178
|
state, _, loaded_state_path = archive_milestone.strict_state(project)
|
|
@@ -1201,6 +1215,10 @@ def integrate(repo: Path, slug: str) -> dict:
|
|
|
1201
1215
|
remote_default_sha = archive_milestone.require_git_success(
|
|
1202
1216
|
run_git(project, "rev-parse", remote_default), "resolve remote default"
|
|
1203
1217
|
)
|
|
1218
|
+
if adopt_published is not None and state.integration != "direct":
|
|
1219
|
+
raise ArchiveError(
|
|
1220
|
+
"--adopt-published-bound applies only to direct integration, "
|
|
1221
|
+
f"not {state.integration}")
|
|
1204
1222
|
if state.integration == "pull-request":
|
|
1205
1223
|
return integrate_pull_request(
|
|
1206
1224
|
project,
|
|
@@ -1220,7 +1238,7 @@ def integrate(repo: Path, slug: str) -> dict:
|
|
|
1220
1238
|
default_name,
|
|
1221
1239
|
bound_branch,
|
|
1222
1240
|
)
|
|
1223
|
-
publishable_bound_branch(project, bound_branch, ship_commit)
|
|
1241
|
+
publishable_bound_branch(project, bound_branch, ship_commit, adopt_published)
|
|
1224
1242
|
integration_branch, worktree = integration_names(project, archive_name, pin=True)
|
|
1225
1243
|
interrupted_worktree = registered_worktree(project, integration_branch)
|
|
1226
1244
|
if (
|
|
@@ -1311,7 +1329,7 @@ def integrate(repo: Path, slug: str) -> dict:
|
|
|
1311
1329
|
elif published_merge.returncode != 0:
|
|
1312
1330
|
archive_milestone.require_git_success(published_merge, "inspect published integration merge")
|
|
1313
1331
|
|
|
1314
|
-
publish_bound_branch(project, bound_branch, ship_commit)
|
|
1332
|
+
publish_bound_branch(project, bound_branch, ship_commit, adopt_published)
|
|
1315
1333
|
if not tag_published:
|
|
1316
1334
|
archive_milestone.require_git_success(
|
|
1317
1335
|
run_git(
|
|
@@ -1462,13 +1480,21 @@ def validate_integrated(
|
|
|
1462
1480
|
if run_git(project, "rev-parse", "--verify", "--quiet", tag_ref).returncode != 0:
|
|
1463
1481
|
raise ArchiveError(f"missing milestone tag: {tag_name}")
|
|
1464
1482
|
metadata = pull_request_tag_metadata(project, tag_ref)
|
|
1465
|
-
|
|
1483
|
+
# A rebase merge rewrites the ship commits, so historical validation
|
|
1484
|
+
# over landed history finds the tag's Landing where the bound branch
|
|
1485
|
+
# found its Ship (#450); archive validate proved the mapping.
|
|
1486
|
+
if metadata["Ship"] != ship_commit and not (
|
|
1487
|
+
shipped.get("rebased_ship") == metadata["Ship"]
|
|
1488
|
+
and metadata["Landing"] == ship_commit
|
|
1489
|
+
):
|
|
1466
1490
|
raise ArchiveError("pull-request milestone tag names the wrong ship commit")
|
|
1467
1491
|
merge_commit = metadata["Landing"]
|
|
1468
1492
|
pull_request = metadata["Pull-Request"]
|
|
1469
1493
|
require_github_authentication()
|
|
1470
1494
|
repository = github_repository(project)
|
|
1471
|
-
|
|
1495
|
+
# The PR head is the original ship commit, which the tag names; a
|
|
1496
|
+
# rebase-merged landing rewrites it on the default branch (#450).
|
|
1497
|
+
pull = find_pull_request(repository, bound_branch, metadata["Ship"], default_name)
|
|
1472
1498
|
if pull is None:
|
|
1473
1499
|
raise ArchiveError("GitHub pull request for the ship commit is missing")
|
|
1474
1500
|
if pull["html_url"] != pull_request:
|
|
@@ -1484,8 +1510,14 @@ def validate_integrated(
|
|
|
1484
1510
|
pull["number"],
|
|
1485
1511
|
merge_commit,
|
|
1486
1512
|
)
|
|
1513
|
+
# Prove the pair the tag names: the original ship commit, whose objects
|
|
1514
|
+
# archive validation already required to resolve (#450).
|
|
1487
1515
|
require_pull_request_merge(
|
|
1488
|
-
project,
|
|
1516
|
+
project,
|
|
1517
|
+
merge_commit,
|
|
1518
|
+
metadata["Ship"],
|
|
1519
|
+
remote_default,
|
|
1520
|
+
allow_rebase=True,
|
|
1489
1521
|
)
|
|
1490
1522
|
require_annotated_tag(
|
|
1491
1523
|
project,
|
|
@@ -1495,7 +1527,7 @@ def validate_integrated(
|
|
|
1495
1527
|
pull_request_tag_message(
|
|
1496
1528
|
archive_name,
|
|
1497
1529
|
pull_request,
|
|
1498
|
-
|
|
1530
|
+
metadata["Ship"],
|
|
1499
1531
|
merge_commit,
|
|
1500
1532
|
),
|
|
1501
1533
|
)
|
|
@@ -1561,6 +1593,7 @@ def validate_integrated(
|
|
|
1561
1593
|
ship_commit,
|
|
1562
1594
|
tag_name,
|
|
1563
1595
|
merge_commit,
|
|
1596
|
+
tag_ship=shipped.get("rebased_ship"),
|
|
1564
1597
|
allow_missing_bound=state.integration in ("pull-request", "external-landing")
|
|
1565
1598
|
or (historical and has_origin),
|
|
1566
1599
|
retired=historical,
|
|
@@ -1609,10 +1642,14 @@ def require_published_integration(
|
|
|
1609
1642
|
merge_commit: str,
|
|
1610
1643
|
allow_missing_bound: bool = False,
|
|
1611
1644
|
retired: bool = False,
|
|
1645
|
+
tag_ship: Optional[str] = None,
|
|
1612
1646
|
) -> None:
|
|
1613
1647
|
if allow_missing_bound:
|
|
1614
1648
|
published_ship = live_remote_ref(project, f"refs/heads/{bound_branch}")
|
|
1615
|
-
|
|
1649
|
+
# A surviving bound branch stays at the original ship commit, which a
|
|
1650
|
+
# rebase merge rewrote into the landing validated here (#450).
|
|
1651
|
+
expected_ships = {ship_commit} | ({tag_ship} if tag_ship else set())
|
|
1652
|
+
if published_ship is not None and published_ship not in expected_ships:
|
|
1616
1653
|
raise ArchiveError(
|
|
1617
1654
|
f"origin/{bound_branch} is {published_ship}, expected ship commit {ship_commit}"
|
|
1618
1655
|
)
|
|
@@ -1691,6 +1728,28 @@ def member_pull_request_tag_message(tag_name: str, pull_request: str, reviewed_h
|
|
|
1691
1728
|
f"Pull-Request: {pull_request}\nReviewed-HEAD: {reviewed_head}\nLanding: {landing}")
|
|
1692
1729
|
|
|
1693
1730
|
|
|
1731
|
+
def member_adopted_tag_message(tag_name: str, reviewed_head: str, landing: str) -> str:
|
|
1732
|
+
return (f"milestone {tag_name.removeprefix('milestone/')}\n\nMode: adopted-landing\n"
|
|
1733
|
+
f"Reviewed-HEAD: {reviewed_head}\nLanding: {landing}\n\n"
|
|
1734
|
+
"The reviewed HEAD reached the member default branch through earlier user-approved "
|
|
1735
|
+
"publications; Path made no merge.")
|
|
1736
|
+
|
|
1737
|
+
|
|
1738
|
+
def member_adopted_tag_metadata(contents: str) -> dict[str, str]:
|
|
1739
|
+
fields: dict[str, str] = {}
|
|
1740
|
+
for line in contents.splitlines():
|
|
1741
|
+
match = re.fullmatch(r"(Mode|Reviewed-HEAD|Landing): (.+)", line)
|
|
1742
|
+
if match:
|
|
1743
|
+
if match.group(1) in fields:
|
|
1744
|
+
raise ArchiveError("adopted-landing member tag repeats metadata")
|
|
1745
|
+
fields[match.group(1)] = match.group(2)
|
|
1746
|
+
if set(fields) != {"Mode", "Reviewed-HEAD", "Landing"}:
|
|
1747
|
+
raise ArchiveError("adopted-landing member tag is missing metadata")
|
|
1748
|
+
if fields["Mode"] != "adopted-landing":
|
|
1749
|
+
raise ArchiveError("adopted-landing member tag has the wrong mode")
|
|
1750
|
+
return fields
|
|
1751
|
+
|
|
1752
|
+
|
|
1694
1753
|
def member_pull_request_tag_metadata(contents: str) -> dict[str, str]:
|
|
1695
1754
|
fields: dict[str, str] = {}
|
|
1696
1755
|
for line in contents.splitlines():
|
|
@@ -1805,7 +1864,8 @@ def _require_member_close(checkout: Path, member: str, bound: str, reviewed_head
|
|
|
1805
1864
|
raise ArchiveError(f"member {member} remote default must be {default}, got {live!r}")
|
|
1806
1865
|
|
|
1807
1866
|
|
|
1808
|
-
def integrate_member(coordinator: Path, member: str, archive_path: str, reviewed_head: str
|
|
1867
|
+
def integrate_member(coordinator: Path, member: str, archive_path: str, reviewed_head: str,
|
|
1868
|
+
adopt: Optional[str] = None) -> dict:
|
|
1809
1869
|
"""Direct-mode member close: publish the bound branch, merge it into the member
|
|
1810
1870
|
default branch, then tag."""
|
|
1811
1871
|
checkout, project, bound, subject, tag_name, default = _member_names(coordinator, member, archive_path)
|
|
@@ -1813,16 +1873,32 @@ def integrate_member(coordinator: Path, member: str, archive_path: str, reviewed
|
|
|
1813
1873
|
body = _member_merge_body(archive_path, member, reviewed_head)
|
|
1814
1874
|
_require_member_close(checkout, member, bound, reviewed_head, default)
|
|
1815
1875
|
merge = _find_member_merge(checkout, subject, body, reviewed_head, default)
|
|
1876
|
+
adopted = False
|
|
1816
1877
|
if merge is None:
|
|
1817
1878
|
origin_main = archive_milestone.require_git_success(
|
|
1818
1879
|
run_git(checkout, "rev-parse", origin_default), f"resolve member {origin_default}")
|
|
1819
1880
|
ancestry = run_git(checkout, "merge-base", "--is-ancestor", reviewed_head, origin_main)
|
|
1820
1881
|
if ancestry.returncode == 0:
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1882
|
+
if live_remote_ref(checkout, f"refs/tags/{tag_name}") is not None:
|
|
1883
|
+
return validate_member_integrated(coordinator, member, archive_path, reviewed_head)
|
|
1884
|
+
if adopt is None:
|
|
1885
|
+
raise ArchiveError(
|
|
1886
|
+
f"member reviewed HEAD reached {origin_default} outside Path; user-approved recovery "
|
|
1887
|
+
f"required: rerun close-members with --adopt-landed-member {member}={origin_main}")
|
|
1888
|
+
if adopt != origin_main:
|
|
1889
|
+
raise ArchiveError(
|
|
1890
|
+
f"member {member} --adopt-landed-member names {adopt}, "
|
|
1891
|
+
f"but {origin_default} is {origin_main}")
|
|
1892
|
+
merge = origin_main
|
|
1893
|
+
adopted = True
|
|
1894
|
+
elif ancestry.returncode != 1:
|
|
1824
1895
|
archive_milestone.require_git_success(ancestry, "inspect member reviewed HEAD ancestry")
|
|
1825
|
-
|
|
1896
|
+
elif adopt is not None:
|
|
1897
|
+
raise ArchiveError(
|
|
1898
|
+
f"member {member} --adopt-landed-member was given, but its reviewed HEAD "
|
|
1899
|
+
f"has not reached {origin_default}")
|
|
1900
|
+
if merge is None:
|
|
1901
|
+
merge = _create_member_merge(checkout, project, archive_path, subject, body, reviewed_head, origin_main)
|
|
1826
1902
|
# Publish in order: bound branch, default branch, tag; a rerun publishes only what is missing.
|
|
1827
1903
|
_publish_member_bound(checkout, project, member, bound, reviewed_head)
|
|
1828
1904
|
if run_git(checkout, "merge-base", "--is-ancestor", merge, origin_default).returncode != 0:
|
|
@@ -1830,7 +1906,10 @@ def integrate_member(coordinator: Path, member: str, archive_path: str, reviewed
|
|
|
1830
1906
|
archive_milestone.require_git_success(
|
|
1831
1907
|
run_git(checkout, "update-ref", f"refs/remotes/{origin_default}", merge),
|
|
1832
1908
|
f"refresh member {origin_default}")
|
|
1833
|
-
|
|
1909
|
+
tag_message = (
|
|
1910
|
+
member_adopted_tag_message(tag_name, reviewed_head, merge) if adopted else None
|
|
1911
|
+
)
|
|
1912
|
+
tag_object, tag_published = ensure_integration_tag(checkout, tag_name, merge, tag_message)
|
|
1834
1913
|
if not tag_published:
|
|
1835
1914
|
_push_member_ref(checkout, project, f"refs/tags/{tag_name}", f"refs/tags/{tag_name}", tag_object, None)
|
|
1836
1915
|
archive_milestone.require_git_success(
|
|
@@ -1903,6 +1982,54 @@ def validate_member_integrated(
|
|
|
1903
1982
|
run_git(checkout, "update-ref", tracking, published), "refresh member milestone-tag ref")
|
|
1904
1983
|
if published is not None:
|
|
1905
1984
|
contents = run_git(checkout, "for-each-ref", "--format=%(contents)", tracking).stdout
|
|
1985
|
+
if "Mode: adopted-landing" in contents.splitlines():
|
|
1986
|
+
# A user-approved adoption: the reviewed HEAD reached the member default
|
|
1987
|
+
# branch through earlier publications, so the tag proves the landing in
|
|
1988
|
+
# place of the canonical merge Path never made (#458). Only a
|
|
1989
|
+
# direct-mode member may carry one; a pull-request member validates
|
|
1990
|
+
# through its merged PR instead.
|
|
1991
|
+
try:
|
|
1992
|
+
import members as members_module
|
|
1993
|
+
except ImportError: # pragma: no cover - package import used by tests
|
|
1994
|
+
from scripts import members as members_module
|
|
1995
|
+
common = Path(archive_milestone.require_git_success(
|
|
1996
|
+
run_git(coordinator, "rev-parse", "--path-format=absolute", "--git-common-dir"),
|
|
1997
|
+
"resolve Git common dir"))
|
|
1998
|
+
try:
|
|
1999
|
+
closed_modes = {
|
|
2000
|
+
row["name"]: row["mode"]
|
|
2001
|
+
for row in members_module.read_member_close(common, PurePosixPath(archive_path).name)
|
|
2002
|
+
}
|
|
2003
|
+
effective_mode = closed_modes.get(member) or {
|
|
2004
|
+
item["name"]: item["integration"]
|
|
2005
|
+
for item in members_module.read_members(coordinator)
|
|
2006
|
+
}.get(member, "default")
|
|
2007
|
+
except members_module.MembersError as error:
|
|
2008
|
+
raise ArchiveError(str(error)) from error
|
|
2009
|
+
if effective_mode == "default":
|
|
2010
|
+
effective_mode = archive_milestone.strict_state(coordinator)[0].integration
|
|
2011
|
+
if effective_mode != "direct":
|
|
2012
|
+
raise ArchiveError(
|
|
2013
|
+
f"member {member} is not direct-mode; an adopted-landing tag cannot close it")
|
|
2014
|
+
metadata = member_adopted_tag_metadata(contents)
|
|
2015
|
+
if metadata["Reviewed-HEAD"] != reviewed_head:
|
|
2016
|
+
raise ArchiveError(f"member {member} tag {tag_name} names a different reviewed HEAD")
|
|
2017
|
+
merge = archive_milestone.require_git_success(
|
|
2018
|
+
run_git(checkout, "rev-parse", f"{tracking}^{{commit}}"), "resolve member tag target")
|
|
2019
|
+
if metadata["Landing"] != merge:
|
|
2020
|
+
raise ArchiveError(f"member {member} tag {tag_name} names a different landing")
|
|
2021
|
+
require_annotated_tag(
|
|
2022
|
+
checkout, tracking, merge, f"member tag {tag_name}",
|
|
2023
|
+
member_adopted_tag_message(tag_name, reviewed_head, merge),
|
|
2024
|
+
)
|
|
2025
|
+
origin_default_ref = f"origin/{default}"
|
|
2026
|
+
for earlier, later in ((reviewed_head, merge), (merge, origin_default_ref)):
|
|
2027
|
+
if run_git(checkout, "merge-base", "--is-ancestor", earlier, later).returncode != 0:
|
|
2028
|
+
raise ArchiveError(
|
|
2029
|
+
f"member {member} adopted landing {merge} does not follow the reviewed HEAD "
|
|
2030
|
+
f"on {origin_default_ref}")
|
|
2031
|
+
return {"member": member, "merge": merge, "tag": tag_name, "tag_object": published,
|
|
2032
|
+
"adopted": True}
|
|
1906
2033
|
if "Mode: pull-request" in contents.splitlines():
|
|
1907
2034
|
metadata = member_pull_request_tag_metadata(contents)
|
|
1908
2035
|
if metadata["Reviewed-HEAD"] != reviewed_head:
|
|
@@ -1945,6 +2072,10 @@ def retire_member(coordinator: Path, member: str, archive_path: str, reviewed_he
|
|
|
1945
2072
|
import members
|
|
1946
2073
|
except ImportError: # pragma: no cover - package import used by tests
|
|
1947
2074
|
from scripts import members
|
|
2075
|
+
try:
|
|
2076
|
+
import isolation
|
|
2077
|
+
except ImportError: # pragma: no cover - package import used by tests
|
|
2078
|
+
from scripts import isolation
|
|
1948
2079
|
checkout, project, bound, _, tag_name, default = _member_names(coordinator, member, archive_path)
|
|
1949
2080
|
proven = validate_member_integrated(coordinator, member, archive_path, reviewed_head)
|
|
1950
2081
|
if (proven["merge"], proven["tag"]) != (merge, tag):
|
|
@@ -1978,8 +2109,14 @@ def retire_member(coordinator: Path, member: str, archive_path: str, reviewed_he
|
|
|
1978
2109
|
run_git(checkout, "push", f"--force-with-lease={bound_ref}:{reviewed_head}", "origin", "--delete", bound),
|
|
1979
2110
|
f"delete member origin/{bound}")
|
|
1980
2111
|
if holder is not None:
|
|
1981
|
-
|
|
1982
|
-
|
|
2112
|
+
# The bound checkout may contain submodules: git refuses a plain worktree
|
|
2113
|
+
# removal for them regardless of cleanliness. isolation's remover defuses
|
|
2114
|
+
# links, deinits submodules, and forces the removal after the clean check
|
|
2115
|
+
# above has already guarded the dirty case.
|
|
2116
|
+
try:
|
|
2117
|
+
isolation._remove_worktree(checkout, holder, force=False)
|
|
2118
|
+
except isolation.IsolationError as error:
|
|
2119
|
+
raise ArchiveError(f"remove member bound checkout failed: {error}") from error
|
|
1983
2120
|
if local is not None:
|
|
1984
2121
|
archive_milestone.require_git_success(run_git(checkout, "update-ref", "-d", bound_ref, reviewed_head),
|
|
1985
2122
|
"delete member bound branch")
|
|
@@ -1994,8 +2131,13 @@ def retire_member(coordinator: Path, member: str, archive_path: str, reviewed_he
|
|
|
1994
2131
|
return {"member": member, "retired": bound}
|
|
1995
2132
|
|
|
1996
2133
|
|
|
1997
|
-
def close_members(repo: Path) -> dict:
|
|
1998
|
-
"""Close each locked member in lock order before the coordinator ship commit; resumable.
|
|
2134
|
+
def close_members(repo: Path, adopt: Optional[Mapping[str, str]] = None) -> dict:
|
|
2135
|
+
"""Close each locked member in lock order before the coordinator ship commit; resumable.
|
|
2136
|
+
|
|
2137
|
+
``adopt`` carries user-approved ``--adopt-landed-member`` recoveries: the
|
|
2138
|
+
member name maps to the exact live default-branch tip whose history
|
|
2139
|
+
already contains the reviewed HEAD (#458).
|
|
2140
|
+
"""
|
|
1999
2141
|
try:
|
|
2000
2142
|
import check_handoffs
|
|
2001
2143
|
import members
|
|
@@ -2026,6 +2168,19 @@ def close_members(repo: Path) -> dict:
|
|
|
2026
2168
|
rows = {row["name"]: row for row in members.read_member_close(common, archive_name)}
|
|
2027
2169
|
except members.MembersError as error:
|
|
2028
2170
|
raise ArchiveError(str(error)) from error
|
|
2171
|
+
if adopt:
|
|
2172
|
+
locked_names = {entry["name"] for entry in locked}
|
|
2173
|
+
unknown = sorted(set(adopt) - locked_names)
|
|
2174
|
+
if unknown:
|
|
2175
|
+
raise ArchiveError(
|
|
2176
|
+
f"--adopt-landed-member names member(s) not locked in this milestone: {', '.join(unknown)}")
|
|
2177
|
+
for adopted_name in sorted(adopt):
|
|
2178
|
+
mode = modes.get(adopted_name, "default")
|
|
2179
|
+
if mode == "default":
|
|
2180
|
+
mode = state.integration
|
|
2181
|
+
if mode != "direct":
|
|
2182
|
+
raise ArchiveError(
|
|
2183
|
+
f"--adopt-landed-member names {adopted_name}, which is not a direct-mode member")
|
|
2029
2184
|
def save() -> None:
|
|
2030
2185
|
members.write_member_close(common, archive_name, [rows[item["name"]] for item in locked if item["name"] in rows])
|
|
2031
2186
|
|
|
@@ -2048,14 +2203,16 @@ def close_members(repo: Path) -> dict:
|
|
|
2048
2203
|
rows[name] = row
|
|
2049
2204
|
save()
|
|
2050
2205
|
if mode == "direct":
|
|
2051
|
-
result = integrate_member(project, name, state.archive, heads[name]
|
|
2206
|
+
result = integrate_member(project, name, state.archive, heads[name],
|
|
2207
|
+
adopt=(adopt or {}).get(name))
|
|
2052
2208
|
else:
|
|
2053
2209
|
result = integrate_member_pull_request(project, name, state.archive, heads[name])
|
|
2054
2210
|
if result.get("status") == "awaiting-merge":
|
|
2055
2211
|
row["pull_request"] = result["pull_request"]
|
|
2056
2212
|
save()
|
|
2057
2213
|
return {"status": "awaiting-merge", "member": name, "pull_request": result["pull_request"]}
|
|
2058
|
-
row.update(status="integrated", merge=result["merge"], tag=result["tag"]
|
|
2214
|
+
row.update(status="integrated", merge=result["merge"], tag=result["tag"],
|
|
2215
|
+
**({"adopted": True} if result.get("adopted") else {}))
|
|
2059
2216
|
save()
|
|
2060
2217
|
closed.append(row)
|
|
2061
2218
|
return {"status": "integrated", "members": closed, "body": ship_commit_body(state.archive, reviewed, closed)}
|