sase-github 0.2.8__tar.gz → 0.2.10__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.
- sase_github-0.2.10/.release-please-manifest.json +3 -0
- {sase_github-0.2.8 → sase_github-0.2.10}/CHANGELOG.md +14 -0
- {sase_github-0.2.8 → sase_github-0.2.10}/PKG-INFO +2 -2
- {sase_github-0.2.8 → sase_github-0.2.10}/pyproject.toml +2 -2
- sase_github-0.2.10/src/sase_github/scripts/gh_release.py +27 -0
- {sase_github-0.2.8 → sase_github-0.2.10}/src/sase_github/scripts/gh_setup.py +58 -0
- {sase_github-0.2.8 → sase_github-0.2.10}/src/sase_github/scripts/new_pr_desc_get_context.py +1 -4
- {sase_github-0.2.8 → sase_github-0.2.10}/src/sase_github/workspace_plugin.py +2 -13
- {sase_github-0.2.8 → sase_github-0.2.10}/src/sase_github/xprompts/gh.yml +7 -11
- {sase_github-0.2.8 → sase_github-0.2.10}/tests/test_gh_workspace_claims.py +97 -7
- sase_github-0.2.10/tests/test_new_pr_desc_get_context.py +68 -0
- {sase_github-0.2.8 → sase_github-0.2.10}/tests/test_workspace_plugin.py +55 -0
- sase_github-0.2.8/.release-please-manifest.json +0 -3
- {sase_github-0.2.8 → sase_github-0.2.10}/.github/workflows/ci.yml +0 -0
- {sase_github-0.2.8 → sase_github-0.2.10}/.github/workflows/pr-title.yml +0 -0
- {sase_github-0.2.8 → sase_github-0.2.10}/.github/workflows/publish.yml +0 -0
- {sase_github-0.2.8 → sase_github-0.2.10}/.sase_beads/beads.db +0 -0
- {sase_github-0.2.8 → sase_github-0.2.10}/.sase_beads/config.json +0 -0
- {sase_github-0.2.8 → sase_github-0.2.10}/.sase_beads/issues.jsonl +0 -0
- {sase_github-0.2.8 → sase_github-0.2.10}/CLAUDE.md +0 -0
- {sase_github-0.2.8 → sase_github-0.2.10}/Justfile +0 -0
- {sase_github-0.2.8 → sase_github-0.2.10}/LICENSE +0 -0
- {sase_github-0.2.8 → sase_github-0.2.10}/README.md +0 -0
- {sase_github-0.2.8 → sase_github-0.2.10}/docs/architecture.md +0 -0
- {sase_github-0.2.8 → sase_github-0.2.10}/docs/configuration.md +0 -0
- {sase_github-0.2.8 → sase_github-0.2.10}/docs/xprompts.md +0 -0
- {sase_github-0.2.8 → sase_github-0.2.10}/release-please-config.json +0 -0
- {sase_github-0.2.8 → sase_github-0.2.10}/src/sase_github/__init__.py +0 -0
- {sase_github-0.2.8 → sase_github-0.2.10}/src/sase_github/config.py +0 -0
- {sase_github-0.2.8 → sase_github-0.2.10}/src/sase_github/default_config.yml +0 -0
- {sase_github-0.2.8 → sase_github-0.2.10}/src/sase_github/errors.py +0 -0
- {sase_github-0.2.8 → sase_github-0.2.10}/src/sase_github/plugin.py +0 -0
- {sase_github-0.2.8 → sase_github-0.2.10}/src/sase_github/scripts/__init__.py +0 -0
- {sase_github-0.2.8 → sase_github-0.2.10}/src/sase_github/task_types.py +0 -0
- {sase_github-0.2.8 → sase_github-0.2.10}/src/sase_github/xprompts/new_pr_desc.yml +0 -0
- {sase_github-0.2.8 → sase_github-0.2.10}/src/sase_github/xprompts/pr_diff.yml +0 -0
- {sase_github-0.2.8 → sase_github-0.2.10}/src/sase_github/xprompts/prdd.yml +0 -0
- {sase_github-0.2.8 → sase_github-0.2.10}/tests/__init__.py +0 -0
- {sase_github-0.2.8 → sase_github-0.2.10}/tests/test_ci_install_contract.py +0 -0
- {sase_github-0.2.8 → sase_github-0.2.10}/tests/test_config.py +0 -0
- {sase_github-0.2.8 → sase_github-0.2.10}/tests/test_github_issues.py +0 -0
- {sase_github-0.2.8 → sase_github-0.2.10}/tests/test_github_plugin.py +0 -0
- {sase_github-0.2.8 → sase_github-0.2.10}/tests/test_github_pull_requests.py +0 -0
- {sase_github-0.2.8 → sase_github-0.2.10}/tests/test_submit_with_recorded_pr.py +0 -0
- {sase_github-0.2.8 → sase_github-0.2.10}/tests/test_task_types.py +0 -0
- {sase_github-0.2.8 → sase_github-0.2.10}/uv.lock +0 -0
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.2.10](https://github.com/sase-org/sase-github/compare/v0.2.9...v0.2.10) (2026-09-08)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **gh:** reject non-github setup workspaces ([f032e91](https://github.com/sase-org/sase-github/commit/f032e91b0b10a5a71af913b7d2ad424e1700b93c))
|
|
9
|
+
|
|
10
|
+
## [0.2.9](https://github.com/sase-org/sase-github/compare/v0.2.8...v0.2.9) (2026-08-29)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
* **workspace:** identity-check #gh release and skip on handoff ([2571c9d](https://github.com/sase-org/sase-github/commit/2571c9d7466d4c6020a87b6ee86068c12170cdab))
|
|
16
|
+
|
|
3
17
|
## [0.2.8](https://github.com/sase-org/sase-github/compare/v0.2.7...v0.2.8) (2026-08-29)
|
|
4
18
|
|
|
5
19
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: sase-github
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.10
|
|
4
4
|
Summary: GitHub VCS plugin for sase
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
License-File: LICENSE
|
|
7
7
|
Requires-Python: >=3.12
|
|
8
|
-
Requires-Dist: sase>=0.
|
|
8
|
+
Requires-Dist: sase>=0.17.0
|
|
9
9
|
Provides-Extra: dev
|
|
10
10
|
Requires-Dist: mypy; extra == 'dev'
|
|
11
11
|
Requires-Dist: pytest; extra == 'dev'
|
|
@@ -4,12 +4,12 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "sase-github"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.10"
|
|
8
8
|
description = "GitHub VCS plugin for sase"
|
|
9
9
|
requires-python = ">=3.12"
|
|
10
10
|
license = "MIT"
|
|
11
11
|
license-files = ["LICENSE"]
|
|
12
|
-
dependencies = ["sase>=0.
|
|
12
|
+
dependencies = ["sase>=0.17.0"]
|
|
13
13
|
|
|
14
14
|
[project.optional-dependencies]
|
|
15
15
|
dev = [
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Release step for the #gh xprompt workflow."""
|
|
2
|
+
|
|
3
|
+
from sase.workspace_provider.vcs_release import VcsReleaseResult, release_vcs_workspace
|
|
4
|
+
|
|
5
|
+
_CALLER_TAG = "gh-release"
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def main(
|
|
9
|
+
*,
|
|
10
|
+
project_file: str,
|
|
11
|
+
workspace_num: int,
|
|
12
|
+
workspace_dir: str,
|
|
13
|
+
workflow_name: str,
|
|
14
|
+
cl_name: str | None,
|
|
15
|
+
) -> None:
|
|
16
|
+
"""Identity-checked, handoff-aware release of the #gh workspace claim."""
|
|
17
|
+
result: VcsReleaseResult = release_vcs_workspace(
|
|
18
|
+
project_file=project_file,
|
|
19
|
+
workspace_num=workspace_num,
|
|
20
|
+
workspace_dir=workspace_dir,
|
|
21
|
+
workflow_name=workflow_name,
|
|
22
|
+
cl_name=cl_name,
|
|
23
|
+
caller_tag=_CALLER_TAG,
|
|
24
|
+
)
|
|
25
|
+
print(f"released={'true' if result.released else 'false'}")
|
|
26
|
+
if result.skip_reason:
|
|
27
|
+
print(f"skip_reason={result.skip_reason}")
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Setup step for the #gh xprompt workflow."""
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
|
+
import subprocess
|
|
4
5
|
import sys
|
|
5
6
|
|
|
6
7
|
from sase.core.occupancy_guard import OccupancyCaller, ensure_workspace_not_occupied
|
|
@@ -84,6 +85,22 @@ def main(
|
|
|
84
85
|
pinned=not release,
|
|
85
86
|
)
|
|
86
87
|
|
|
88
|
+
try:
|
|
89
|
+
_assert_github_vcs_provider(
|
|
90
|
+
workspace_dir=workspace_dir,
|
|
91
|
+
primary_workspace_dir=resolved.primary_workspace_dir,
|
|
92
|
+
)
|
|
93
|
+
except Exception:
|
|
94
|
+
if not pre_allocated:
|
|
95
|
+
release_workspace(
|
|
96
|
+
project_file,
|
|
97
|
+
workspace_num,
|
|
98
|
+
workflow_name,
|
|
99
|
+
cl_name,
|
|
100
|
+
caller_tag=_CALLER_TAG,
|
|
101
|
+
)
|
|
102
|
+
raise
|
|
103
|
+
|
|
87
104
|
# Refuse to hand this checkout to `prepare`/`checkout` if another live
|
|
88
105
|
# agent still occupies it. Runs on the pre_allocated branch too: the
|
|
89
106
|
# launcher already wrote its own occupant record, and the conflict
|
|
@@ -254,6 +271,47 @@ def _materialize_or_release(
|
|
|
254
271
|
return workspace_dir
|
|
255
272
|
|
|
256
273
|
|
|
274
|
+
def _assert_github_vcs_provider(
|
|
275
|
+
*,
|
|
276
|
+
workspace_dir: str,
|
|
277
|
+
primary_workspace_dir: str,
|
|
278
|
+
) -> None:
|
|
279
|
+
provider_name = _detect_vcs_provider_name(workspace_dir)
|
|
280
|
+
if provider_name == "github":
|
|
281
|
+
return
|
|
282
|
+
actual_origin = _origin_url(workspace_dir)
|
|
283
|
+
expected_origin = _origin_url(primary_workspace_dir)
|
|
284
|
+
raise RuntimeError(
|
|
285
|
+
"GitHub workflow refused workspace because its VCS provider resolved "
|
|
286
|
+
f"to {provider_name or 'unknown'!r}, not 'github'. "
|
|
287
|
+
f"workspace origin: {actual_origin or '<unreadable>'}; "
|
|
288
|
+
f"expected GitHub origin: {expected_origin or '<unreadable>'}"
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def _detect_vcs_provider_name(workspace_dir: str) -> str | None:
|
|
293
|
+
from sase.vcs_provider import detect_vcs
|
|
294
|
+
|
|
295
|
+
return detect_vcs(workspace_dir)
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
def _origin_url(workspace_dir: str) -> str | None:
|
|
299
|
+
try:
|
|
300
|
+
result = subprocess.run(
|
|
301
|
+
["git", "config", "--get", "remote.origin.url"],
|
|
302
|
+
cwd=workspace_dir,
|
|
303
|
+
capture_output=True,
|
|
304
|
+
text=True,
|
|
305
|
+
check=False,
|
|
306
|
+
timeout=10,
|
|
307
|
+
)
|
|
308
|
+
except (subprocess.TimeoutExpired, FileNotFoundError, OSError):
|
|
309
|
+
return None
|
|
310
|
+
if result.returncode != 0:
|
|
311
|
+
return None
|
|
312
|
+
return result.stdout.strip() or None
|
|
313
|
+
|
|
314
|
+
|
|
257
315
|
def _describe_workspace_occupant(project_file: str, workspace_num: int) -> str | None:
|
|
258
316
|
occupants = [
|
|
259
317
|
claim
|
|
@@ -7,10 +7,7 @@ from pathlib import Path
|
|
|
7
7
|
|
|
8
8
|
from sase.workspace_provider.utils import get_default_branch, parse_workspace_dir
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
from sase.ace.patch import find_all_patches
|
|
12
|
-
except ImportError: # Older supported SASE releases expose only ChangeSpec names.
|
|
13
|
-
from sase.ace.changespec import find_all_changespecs as find_all_patches
|
|
10
|
+
from sase.ace.patch import find_all_patches
|
|
14
11
|
|
|
15
12
|
try:
|
|
16
13
|
from sase.core.paths import ( # type: ignore[attr-defined]
|
|
@@ -17,19 +17,8 @@ from collections.abc import Mapping, Sequence
|
|
|
17
17
|
from pathlib import Path
|
|
18
18
|
from typing import TYPE_CHECKING, Any, Literal
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
from sase.ace.patch.project_spec_path import preferred_project_spec_path
|
|
23
|
-
except ImportError: # Older supported SASE releases expose only ChangeSpec names.
|
|
24
|
-
from sase.ace.changespec import ( # type: ignore[no-redef]
|
|
25
|
-
# Legacy import names are adapted to canonical Patch locals.
|
|
26
|
-
ChangeSpec as Patch,
|
|
27
|
-
# Legacy import names are adapted to canonical Patch locals.
|
|
28
|
-
find_all_changespecs as find_all_patches,
|
|
29
|
-
)
|
|
30
|
-
from sase.ace.changespec.project_spec_path import ( # type: ignore[no-redef]
|
|
31
|
-
preferred_project_spec_path,
|
|
32
|
-
)
|
|
20
|
+
from sase.ace.patch import Patch, find_all_patches
|
|
21
|
+
from sase.ace.patch.project_spec_path import preferred_project_spec_path
|
|
33
22
|
from sase.workspace_provider import (
|
|
34
23
|
ExternalRepoCloneResult,
|
|
35
24
|
ResolvedRef,
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
description: Check out a GitHub ref in an isolated workspace and show resulting changes.
|
|
2
|
-
wraps_all: true
|
|
3
2
|
tags: vcs, rollover
|
|
4
3
|
|
|
5
4
|
input:
|
|
@@ -120,18 +119,15 @@ steps:
|
|
|
120
119
|
finally: true
|
|
121
120
|
if: "{{ setup.should_release }}"
|
|
122
121
|
python: |
|
|
123
|
-
from
|
|
124
|
-
from sase.workspace_provider.occupant import clear_occupant_record
|
|
122
|
+
from sase_github.scripts.gh_release import main
|
|
125
123
|
|
|
126
|
-
|
|
127
|
-
{{ setup.project_file | tojson }},
|
|
128
|
-
{{ setup.workspace_num }},
|
|
129
|
-
{{ setup.
|
|
130
|
-
{{
|
|
131
|
-
|
|
124
|
+
main(
|
|
125
|
+
project_file={{ setup.project_file | tojson }},
|
|
126
|
+
workspace_num={{ setup.workspace_num }},
|
|
127
|
+
workspace_dir={{ setup.workspace_dir | tojson }},
|
|
128
|
+
workflow_name={{ setup.workflow_name | tojson }},
|
|
129
|
+
cl_name={{ gh_ref | tojson }} if "/" not in {{ gh_ref | tojson }} else None,
|
|
132
130
|
)
|
|
133
|
-
clear_occupant_record({{ setup.workspace_dir | tojson }})
|
|
134
|
-
print("released=true")
|
|
135
131
|
output: { released: bool }
|
|
136
132
|
|
|
137
133
|
- name: diff
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import os
|
|
6
|
+
from collections.abc import Iterator
|
|
6
7
|
from pathlib import Path
|
|
7
8
|
from types import SimpleNamespace
|
|
8
9
|
from unittest.mock import MagicMock, patch
|
|
@@ -23,12 +24,18 @@ from sase.workspace_provider.occupant import (
|
|
|
23
24
|
write_occupant_record,
|
|
24
25
|
)
|
|
25
26
|
|
|
26
|
-
from sase_github.scripts import gh_setup
|
|
27
|
+
from sase_github.scripts import gh_release, gh_setup
|
|
27
28
|
from sase_github.workspace_plugin import GitHubWorkspacePlugin
|
|
28
29
|
|
|
29
30
|
ROOT = Path(__file__).resolve().parents[1]
|
|
30
31
|
|
|
31
32
|
|
|
33
|
+
@pytest.fixture(autouse=True)
|
|
34
|
+
def _github_provider_guard_passes() -> Iterator[None]:
|
|
35
|
+
with patch("sase_github.scripts.gh_setup._assert_github_vcs_provider"):
|
|
36
|
+
yield
|
|
37
|
+
|
|
38
|
+
|
|
32
39
|
def _write_project_file(
|
|
33
40
|
tmp_path: Path, running_claims: list[WorkspaceClaim] | None = None
|
|
34
41
|
) -> str:
|
|
@@ -939,16 +946,99 @@ class TestWsSubmitAtomicClaim:
|
|
|
939
946
|
)
|
|
940
947
|
|
|
941
948
|
|
|
942
|
-
def
|
|
949
|
+
def test_gh_release_step_delegates_to_gh_release() -> None:
|
|
943
950
|
text = (ROOT / "src" / "sase_github" / "xprompts" / "gh.yml").read_text(
|
|
944
951
|
encoding="utf-8"
|
|
945
952
|
)
|
|
946
|
-
assert
|
|
953
|
+
assert "from sase_github.scripts.gh_release import main" in text
|
|
954
|
+
assert "workspace_dir={{ setup.workspace_dir | tojson }}" in text
|
|
955
|
+
assert "release_workspace(" not in text
|
|
956
|
+
assert "clear_occupant_record(" not in text
|
|
947
957
|
|
|
948
958
|
|
|
949
|
-
def
|
|
950
|
-
|
|
959
|
+
def test_gh_release_caller_tag() -> None:
|
|
960
|
+
source = (ROOT / "src" / "sase_github" / "scripts" / "gh_release.py").read_text(
|
|
951
961
|
encoding="utf-8"
|
|
952
962
|
)
|
|
953
|
-
assert "
|
|
954
|
-
|
|
963
|
+
assert '_CALLER_TAG = "gh-release"' in source
|
|
964
|
+
|
|
965
|
+
|
|
966
|
+
def test_gh_release_step_releases_owned_claim_and_occupant(
|
|
967
|
+
tmp_path: Path, capsys: pytest.CaptureFixture[str]
|
|
968
|
+
) -> None:
|
|
969
|
+
runner_pid = os.getppid()
|
|
970
|
+
project_file = _write_project_file(
|
|
971
|
+
tmp_path,
|
|
972
|
+
running_claims=[WorkspaceClaim(23, "gh-acme/widget", None, pid=runner_pid)],
|
|
973
|
+
)
|
|
974
|
+
workspace_dir = tmp_path / "widget_23"
|
|
975
|
+
workspace_dir.mkdir()
|
|
976
|
+
write_occupant_record(
|
|
977
|
+
str(workspace_dir),
|
|
978
|
+
new_occupant_record(
|
|
979
|
+
pid=runner_pid,
|
|
980
|
+
workflow="gh-acme/widget",
|
|
981
|
+
project="gh_acme__widget",
|
|
982
|
+
workspace_num=23,
|
|
983
|
+
),
|
|
984
|
+
)
|
|
985
|
+
|
|
986
|
+
with _isolate_ledger(tmp_path):
|
|
987
|
+
gh_release.main(
|
|
988
|
+
project_file=project_file,
|
|
989
|
+
workspace_num=23,
|
|
990
|
+
workspace_dir=str(workspace_dir),
|
|
991
|
+
workflow_name="gh-acme/widget",
|
|
992
|
+
cl_name=None,
|
|
993
|
+
)
|
|
994
|
+
|
|
995
|
+
out = capsys.readouterr().out
|
|
996
|
+
assert "released=true" in out
|
|
997
|
+
assert get_claimed_workspaces(project_file) == []
|
|
998
|
+
assert read_occupant_record(str(workspace_dir)) is None
|
|
999
|
+
|
|
1000
|
+
|
|
1001
|
+
def test_gh_release_step_skips_on_handoff(
|
|
1002
|
+
tmp_path: Path, capsys: pytest.CaptureFixture[str]
|
|
1003
|
+
) -> None:
|
|
1004
|
+
runner_pid = os.getppid()
|
|
1005
|
+
project_file = _write_project_file(
|
|
1006
|
+
tmp_path,
|
|
1007
|
+
running_claims=[WorkspaceClaim(23, "gh-acme/widget", None, pid=runner_pid)],
|
|
1008
|
+
)
|
|
1009
|
+
workspace_dir = tmp_path / "widget_23"
|
|
1010
|
+
workspace_dir.mkdir()
|
|
1011
|
+
write_occupant_record(
|
|
1012
|
+
str(workspace_dir),
|
|
1013
|
+
new_occupant_record(
|
|
1014
|
+
pid=runner_pid,
|
|
1015
|
+
workflow="gh-acme/widget",
|
|
1016
|
+
project="gh_acme__widget",
|
|
1017
|
+
workspace_num=23,
|
|
1018
|
+
),
|
|
1019
|
+
)
|
|
1020
|
+
artifacts = tmp_path / "artifacts"
|
|
1021
|
+
artifacts.mkdir()
|
|
1022
|
+
(artifacts / ".sase_monitor_pending").write_text("{}", encoding="utf-8")
|
|
1023
|
+
|
|
1024
|
+
with (
|
|
1025
|
+
_isolate_ledger(tmp_path),
|
|
1026
|
+
patch.dict(os.environ, {"SASE_ARTIFACTS_DIR": str(artifacts)}),
|
|
1027
|
+
):
|
|
1028
|
+
gh_release.main(
|
|
1029
|
+
project_file=project_file,
|
|
1030
|
+
workspace_num=23,
|
|
1031
|
+
workspace_dir=str(workspace_dir),
|
|
1032
|
+
workflow_name="gh-acme/widget",
|
|
1033
|
+
cl_name=None,
|
|
1034
|
+
)
|
|
1035
|
+
|
|
1036
|
+
out = capsys.readouterr().out
|
|
1037
|
+
assert "released=false" in out
|
|
1038
|
+
assert "skip_reason=handoff" in out
|
|
1039
|
+
assert [claim.workspace_num for claim in get_claimed_workspaces(project_file)] == [
|
|
1040
|
+
23
|
|
1041
|
+
]
|
|
1042
|
+
occupant = read_occupant_record(str(workspace_dir))
|
|
1043
|
+
assert occupant is not None
|
|
1044
|
+
assert occupant.pid == runner_pid
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""Tests for the #new_pr_desc xprompt's get-context script."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from types import SimpleNamespace
|
|
5
|
+
from unittest.mock import patch
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
from sase_github.scripts import new_pr_desc_get_context
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_main_reports_context_for_found_patch(
|
|
13
|
+
tmp_path: Path,
|
|
14
|
+
capsys: pytest.CaptureFixture[str],
|
|
15
|
+
) -> None:
|
|
16
|
+
"""The script resolves the Patch through the canonical ``sase.ace.patch`` API."""
|
|
17
|
+
workspace_dir = tmp_path / "workspace"
|
|
18
|
+
workspace_dir.mkdir()
|
|
19
|
+
diff_dir = tmp_path / "gh-diffs"
|
|
20
|
+
diff_dir.mkdir()
|
|
21
|
+
entry = SimpleNamespace(
|
|
22
|
+
name="feature",
|
|
23
|
+
file_path=str(workspace_dir / "feature.sase"),
|
|
24
|
+
description="Add feature",
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
with (
|
|
28
|
+
patch(
|
|
29
|
+
"sase_github.scripts.new_pr_desc_get_context.find_all_patches",
|
|
30
|
+
return_value=[entry],
|
|
31
|
+
),
|
|
32
|
+
patch(
|
|
33
|
+
"sase_github.scripts.new_pr_desc_get_context.parse_workspace_dir",
|
|
34
|
+
return_value=str(workspace_dir),
|
|
35
|
+
),
|
|
36
|
+
patch(
|
|
37
|
+
"sase_github.scripts.new_pr_desc_get_context.get_default_branch",
|
|
38
|
+
return_value="origin/main",
|
|
39
|
+
),
|
|
40
|
+
patch(
|
|
41
|
+
"sase_github.scripts.new_pr_desc_get_context.subprocess.run",
|
|
42
|
+
return_value=SimpleNamespace(returncode=0, stdout=""),
|
|
43
|
+
),
|
|
44
|
+
patch(
|
|
45
|
+
"sase_github.scripts.new_pr_desc_get_context.get_sase_managed_tmpdir",
|
|
46
|
+
return_value=str(diff_dir),
|
|
47
|
+
),
|
|
48
|
+
):
|
|
49
|
+
new_pr_desc_get_context.main(name="feature")
|
|
50
|
+
|
|
51
|
+
out = capsys.readouterr().out
|
|
52
|
+
assert "error=\n" in out
|
|
53
|
+
assert "description=Add feature\n" in out
|
|
54
|
+
assert "default_branch=main\n" in out
|
|
55
|
+
assert "branch_name=feature\n" in out
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def test_main_reports_error_for_missing_patch(
|
|
59
|
+
capsys: pytest.CaptureFixture[str],
|
|
60
|
+
) -> None:
|
|
61
|
+
with patch(
|
|
62
|
+
"sase_github.scripts.new_pr_desc_get_context.find_all_patches",
|
|
63
|
+
return_value=[],
|
|
64
|
+
):
|
|
65
|
+
new_pr_desc_get_context.main(name="missing")
|
|
66
|
+
|
|
67
|
+
out = capsys.readouterr().out
|
|
68
|
+
assert "error=Patch 'missing' not found\n" in out
|
|
@@ -2096,6 +2096,7 @@ class TestGhSetup:
|
|
|
2096
2096
|
"sase_github.scripts.gh_setup.materialize_sdd_store",
|
|
2097
2097
|
side_effect=materialize,
|
|
2098
2098
|
) as materialize_mock,
|
|
2099
|
+
patch("sase_github.scripts.gh_setup._assert_github_vcs_provider"),
|
|
2099
2100
|
):
|
|
2100
2101
|
gh_setup.main(gh_ref="acme/widget", n=None, release=False)
|
|
2101
2102
|
|
|
@@ -2142,6 +2143,60 @@ class TestGhSetup:
|
|
|
2142
2143
|
caller_tag="gh-setup",
|
|
2143
2144
|
)
|
|
2144
2145
|
|
|
2146
|
+
def test_non_github_workspace_provider_releases_claimed_slot(self) -> None:
|
|
2147
|
+
from sase_github.scripts import gh_setup
|
|
2148
|
+
|
|
2149
|
+
resolved = ResolvedRef(
|
|
2150
|
+
project_file="/tmp/gh_acme__widget.sase",
|
|
2151
|
+
project_name="gh_acme__widget",
|
|
2152
|
+
primary_workspace_dir="/work/widget/",
|
|
2153
|
+
checkout_target="origin/main",
|
|
2154
|
+
canonical_ref="gh_acme__widget",
|
|
2155
|
+
)
|
|
2156
|
+
|
|
2157
|
+
with (
|
|
2158
|
+
patch("sase_github.scripts.gh_setup.resolve_ref", return_value=resolved),
|
|
2159
|
+
patch.dict(os.environ, {"SASE_GH_PRE_ALLOCATED": "0"}),
|
|
2160
|
+
patch(
|
|
2161
|
+
"sase_github.scripts.gh_setup.ensure_workspace_checkout",
|
|
2162
|
+
return_value="/work/widget_7/",
|
|
2163
|
+
),
|
|
2164
|
+
patch(
|
|
2165
|
+
"sase_github.scripts.gh_setup.claim_next_axe_workspace",
|
|
2166
|
+
return_value=7,
|
|
2167
|
+
),
|
|
2168
|
+
patch(
|
|
2169
|
+
"sase_github.scripts.gh_setup.materialize_sdd_store",
|
|
2170
|
+
return_value=None,
|
|
2171
|
+
),
|
|
2172
|
+
patch(
|
|
2173
|
+
"sase_github.scripts.gh_setup._detect_vcs_provider_name",
|
|
2174
|
+
return_value="bare_git",
|
|
2175
|
+
),
|
|
2176
|
+
patch(
|
|
2177
|
+
"sase_github.scripts.gh_setup._origin_url",
|
|
2178
|
+
side_effect=lambda path: {
|
|
2179
|
+
"/work/widget_7/": "/work/widget/",
|
|
2180
|
+
"/work/widget/": "git@github.com:acme/widget.git",
|
|
2181
|
+
}[path],
|
|
2182
|
+
),
|
|
2183
|
+
patch("sase_github.scripts.gh_setup.release_workspace") as release,
|
|
2184
|
+
pytest.raises(RuntimeError) as exc_info,
|
|
2185
|
+
):
|
|
2186
|
+
gh_setup.main(gh_ref="acme/widget", n=None, release=False)
|
|
2187
|
+
|
|
2188
|
+
message = str(exc_info.value)
|
|
2189
|
+
assert "VCS provider resolved to 'bare_git'" in message
|
|
2190
|
+
assert "workspace origin: /work/widget/" in message
|
|
2191
|
+
assert "expected GitHub origin: git@github.com:acme/widget.git" in message
|
|
2192
|
+
release.assert_called_once_with(
|
|
2193
|
+
"/tmp/gh_acme__widget.sase",
|
|
2194
|
+
7,
|
|
2195
|
+
"gh-acme/widget",
|
|
2196
|
+
None,
|
|
2197
|
+
caller_tag="gh-setup",
|
|
2198
|
+
)
|
|
2199
|
+
|
|
2145
2200
|
|
|
2146
2201
|
# ── detect_workflow_type (via plugin) ────────────────────────────────
|
|
2147
2202
|
|
|
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
|