sase-github 0.2.0__tar.gz → 0.2.2__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.2/.release-please-manifest.json +3 -0
- {sase_github-0.2.0 → sase_github-0.2.2}/CHANGELOG.md +14 -0
- {sase_github-0.2.0 → sase_github-0.2.2}/PKG-INFO +1 -1
- {sase_github-0.2.0 → sase_github-0.2.2}/README.md +1 -1
- {sase_github-0.2.0 → sase_github-0.2.2}/docs/architecture.md +11 -11
- {sase_github-0.2.0 → sase_github-0.2.2}/docs/xprompts.md +4 -4
- {sase_github-0.2.0 → sase_github-0.2.2}/pyproject.toml +1 -1
- {sase_github-0.2.0 → sase_github-0.2.2}/src/sase_github/scripts/new_pr_desc_get_context.py +44 -13
- {sase_github-0.2.0 → sase_github-0.2.2}/src/sase_github/workspace_plugin.py +52 -45
- {sase_github-0.2.0 → sase_github-0.2.2}/src/sase_github/xprompts/gh.yml +9 -7
- {sase_github-0.2.0 → sase_github-0.2.2}/src/sase_github/xprompts/new_pr_desc.yml +3 -3
- {sase_github-0.2.0 → sase_github-0.2.2}/tests/test_submit_with_recorded_pr.py +1 -1
- {sase_github-0.2.0 → sase_github-0.2.2}/tests/test_workspace_plugin.py +5 -5
- sase_github-0.2.0/.release-please-manifest.json +0 -3
- {sase_github-0.2.0 → sase_github-0.2.2}/.github/workflows/ci.yml +0 -0
- {sase_github-0.2.0 → sase_github-0.2.2}/.github/workflows/pr-title.yml +0 -0
- {sase_github-0.2.0 → sase_github-0.2.2}/.github/workflows/publish.yml +0 -0
- {sase_github-0.2.0 → sase_github-0.2.2}/.sase_beads/beads.db +0 -0
- {sase_github-0.2.0 → sase_github-0.2.2}/.sase_beads/config.json +0 -0
- {sase_github-0.2.0 → sase_github-0.2.2}/.sase_beads/issues.jsonl +0 -0
- {sase_github-0.2.0 → sase_github-0.2.2}/CLAUDE.md +0 -0
- {sase_github-0.2.0 → sase_github-0.2.2}/Justfile +0 -0
- {sase_github-0.2.0 → sase_github-0.2.2}/LICENSE +0 -0
- {sase_github-0.2.0 → sase_github-0.2.2}/docs/configuration.md +0 -0
- {sase_github-0.2.0 → sase_github-0.2.2}/release-please-config.json +0 -0
- {sase_github-0.2.0 → sase_github-0.2.2}/src/sase_github/__init__.py +0 -0
- {sase_github-0.2.0 → sase_github-0.2.2}/src/sase_github/config.py +0 -0
- {sase_github-0.2.0 → sase_github-0.2.2}/src/sase_github/default_config.yml +0 -0
- {sase_github-0.2.0 → sase_github-0.2.2}/src/sase_github/errors.py +0 -0
- {sase_github-0.2.0 → sase_github-0.2.2}/src/sase_github/plugin.py +0 -0
- {sase_github-0.2.0 → sase_github-0.2.2}/src/sase_github/scripts/__init__.py +0 -0
- {sase_github-0.2.0 → sase_github-0.2.2}/src/sase_github/scripts/gh_setup.py +0 -0
- {sase_github-0.2.0 → sase_github-0.2.2}/src/sase_github/xprompts/pr_diff.yml +0 -0
- {sase_github-0.2.0 → sase_github-0.2.2}/src/sase_github/xprompts/prdd.yml +0 -0
- {sase_github-0.2.0 → sase_github-0.2.2}/tests/__init__.py +0 -0
- {sase_github-0.2.0 → sase_github-0.2.2}/tests/test_ci_install_contract.py +0 -0
- {sase_github-0.2.0 → sase_github-0.2.2}/tests/test_config.py +0 -0
- {sase_github-0.2.0 → sase_github-0.2.2}/tests/test_github_issues.py +0 -0
- {sase_github-0.2.0 → sase_github-0.2.2}/tests/test_github_plugin.py +0 -0
- {sase_github-0.2.0 → sase_github-0.2.2}/uv.lock +0 -0
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.2.2](https://github.com/sase-org/sase-github/compare/v0.2.1...v0.2.2) (2026-08-08)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* adopt Patch terminology in GitHub integration ([bc4fec5](https://github.com/sase-org/sase-github/commit/bc4fec5d436853eb9b839339d855093d13ce83aa))
|
|
9
|
+
|
|
10
|
+
## [0.2.1](https://github.com/sase-org/sase-github/compare/v0.2.0...v0.2.1) (2026-07-25)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* write handoff diff files under the managed temp root (sase-96.8.5) ([dd5deed](https://github.com/sase-org/sase-github/commit/dd5deedd25aeaccbb984b647c3abf60ea1d5afdc))
|
|
16
|
+
|
|
3
17
|
## [0.2.0](https://github.com/sase-org/sase-github/compare/v0.1.7...v0.2.0) (2026-07-16)
|
|
4
18
|
|
|
5
19
|
|
|
@@ -59,7 +59,7 @@ Requires `sase>=0.11.0` as a dependency. For GitHub Enterprise Server or self-ho
|
|
|
59
59
|
### Workspace Provider
|
|
60
60
|
|
|
61
61
|
- **GitHubWorkspacePlugin** — Workspace provider that handles GitHub-specific workflow orchestration: reference
|
|
62
|
-
resolution (repo paths, project names,
|
|
62
|
+
resolution (repo paths, project names, Patch names), PR submission via `gh pr merge`, branch management, and
|
|
63
63
|
commit description formatting. It also owns GitHub SDD policy: every GitHub project requires a labeled sidecar
|
|
64
64
|
repository, materialized before `#gh` work starts.
|
|
65
65
|
- **Repo completion for `#gh:<owner>/` refs** — Supplies repository candidates to SASE prompt completion by calling
|
|
@@ -51,7 +51,7 @@ Handles higher-level workflow orchestration. Implements workspace hooks for GitH
|
|
|
51
51
|
| `ws_create_sdd_remote()` | Verifies or creates `--sdd`, `--plans`, or `--research` and returns its metadata |
|
|
52
52
|
| `ws_prepare_mail()` | Displays branch/description and prompts user before pushing |
|
|
53
53
|
| `ws_format_commit_description()` | Prepends `[project]` prefix to commit messages |
|
|
54
|
-
| `ws_submit()` | Submits a
|
|
54
|
+
| `ws_submit()` | Submits a Patch by merging its PR via `gh pr merge --merge --delete-branch` |
|
|
55
55
|
|
|
56
56
|
The provider declares `separate_repo` SDD policy. Core supplies a unique staging path; the plugin finds or creates the
|
|
57
57
|
selected sidecar, ensures its `sase--sdd` label, and clones with the normal SSH/HTTPS fallback. It returns only a
|
|
@@ -98,13 +98,13 @@ When the ref matches an existing project directory, `PROJECT_NAME`, or project a
|
|
|
98
98
|
- Reads `WORKSPACE_DIR` from the project file
|
|
99
99
|
- Checks out the default branch
|
|
100
100
|
|
|
101
|
-
### Mode 3:
|
|
101
|
+
### Mode 3: Patch Name
|
|
102
102
|
|
|
103
|
-
When the ref matches an existing
|
|
103
|
+
When the ref matches an existing Patch:
|
|
104
104
|
|
|
105
|
-
- Searches all
|
|
106
|
-
- Reads `WORKSPACE_DIR` from the
|
|
107
|
-
- Checks out `origin/<name>` (the
|
|
105
|
+
- Searches all Patches for a matching name
|
|
106
|
+
- Reads `WORKSPACE_DIR` from the Patch's project file
|
|
107
|
+
- Checks out `origin/<name>` (the Patch's branch)
|
|
108
108
|
|
|
109
109
|
## Ref Namespace Completion
|
|
110
110
|
|
|
@@ -119,14 +119,14 @@ deduplicated case-insensitively, with record-derived spellings preferred when bo
|
|
|
119
119
|
|
|
120
120
|
## Submission Flow
|
|
121
121
|
|
|
122
|
-
When submitting a GitHub
|
|
122
|
+
When submitting a GitHub Patch (`ws_submit`):
|
|
123
123
|
|
|
124
|
-
1. Kill and persist all running processes on the
|
|
125
|
-
2. Verify no active child
|
|
126
|
-
3. Claim a workspace and checkout the
|
|
124
|
+
1. Kill and persist all running processes on the Patch
|
|
125
|
+
2. Verify no active child Patches exist
|
|
126
|
+
3. Claim a workspace and checkout the Patch branch
|
|
127
127
|
4. Check for an existing PR on the branch
|
|
128
128
|
5. Merge via `gh pr merge --merge --delete-branch`
|
|
129
|
-
6. Finalize submission (update
|
|
129
|
+
6. Finalize submission (update Patch status)
|
|
130
130
|
7. Release the workspace
|
|
131
131
|
|
|
132
132
|
## Config Helper
|
|
@@ -14,7 +14,7 @@ the target branch, and captures diffs.
|
|
|
14
14
|
|
|
15
15
|
| Parameter | Type | Default | Description |
|
|
16
16
|
|---|---|---|---|
|
|
17
|
-
| `gh_ref` | word | (required) | Repo path (`user/project`), project name, or
|
|
17
|
+
| `gh_ref` | word | (required) | Repo path (`user/project`), project name, or Patch name |
|
|
18
18
|
| `n` | int | `null` | Workspace number override (auto-assigned if null) |
|
|
19
19
|
| `release` | bool | `true` | Whether to release the workspace when done |
|
|
20
20
|
| `workflow_label` | word | `null` | Optional label for the workflow |
|
|
@@ -41,7 +41,7 @@ the target branch, and captures diffs.
|
|
|
41
41
|
# Work on a project by shorthand name
|
|
42
42
|
#gh(sase) Add a new CLI command for status checking
|
|
43
43
|
|
|
44
|
-
# Resume work on an existing
|
|
44
|
+
# Resume work on an existing Patch
|
|
45
45
|
#gh(fix-auth-bug) Continue implementing the OAuth flow
|
|
46
46
|
|
|
47
47
|
# Work without releasing the workspace
|
|
@@ -58,12 +58,12 @@ Generates an AI-written PR title and body from the diff and commits on a branch,
|
|
|
58
58
|
|
|
59
59
|
| Parameter | Type | Description |
|
|
60
60
|
|---|---|---|
|
|
61
|
-
| `name` | word |
|
|
61
|
+
| `name` | word | Patch name to generate the description for |
|
|
62
62
|
|
|
63
63
|
### Steps
|
|
64
64
|
|
|
65
65
|
1. **get_context** — Runs a Python script that retrieves the diff, commit history, workspace directory, default branch,
|
|
66
|
-
and current branch name for the
|
|
66
|
+
and current branch name for the Patch.
|
|
67
67
|
2. **generate** — Launches a sub-agent with the diff and commits, instructing it to produce a title (conventional
|
|
68
68
|
commits format, under 72 chars) and a body (markdown with `## Summary` and bullet points).
|
|
69
69
|
3. **apply** — Looks up the PR for the branch via `gh pr view` and updates it with `gh pr edit` using the generated
|
|
@@ -1,26 +1,51 @@
|
|
|
1
1
|
"""Get context step for the #new_pr_desc xprompt workflow."""
|
|
2
2
|
|
|
3
|
+
import os
|
|
3
4
|
import subprocess
|
|
4
5
|
import tempfile
|
|
6
|
+
from pathlib import Path
|
|
5
7
|
|
|
6
|
-
from sase.ace.changespec import find_all_changespecs
|
|
7
8
|
from sase.workspace_provider.utils import get_default_branch, parse_workspace_dir
|
|
8
9
|
|
|
10
|
+
try:
|
|
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
|
|
14
|
+
|
|
15
|
+
try:
|
|
16
|
+
from sase.core.paths import ( # type: ignore[attr-defined]
|
|
17
|
+
get_sase_managed_tmpdir,
|
|
18
|
+
)
|
|
19
|
+
except ImportError: # sase < 0.11.2 does not ship the helper yet
|
|
20
|
+
|
|
21
|
+
def get_sase_managed_tmpdir(*parts: str) -> str:
|
|
22
|
+
"""Fall back to the same managed root the sase helper resolves."""
|
|
23
|
+
sase_tmpdir = os.environ.get("SASE_TMPDIR")
|
|
24
|
+
if sase_tmpdir:
|
|
25
|
+
root = Path(sase_tmpdir).expanduser()
|
|
26
|
+
else:
|
|
27
|
+
sase_home = os.environ.get("SASE_HOME")
|
|
28
|
+
home = Path(sase_home).expanduser() if sase_home else Path.home() / ".sase"
|
|
29
|
+
root = home / "tmp"
|
|
30
|
+
for part in parts:
|
|
31
|
+
root /= part
|
|
32
|
+
root.mkdir(parents=True, exist_ok=True)
|
|
33
|
+
return str(root)
|
|
34
|
+
|
|
9
35
|
|
|
10
36
|
def main(*, name: str) -> None:
|
|
11
|
-
"""Find
|
|
37
|
+
"""Find a Patch, then get diff and commit subjects for PR description generation.
|
|
12
38
|
|
|
13
39
|
Prints key=value output for the workflow executor.
|
|
14
40
|
"""
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
changespec = cs
|
|
41
|
+
patch = None
|
|
42
|
+
for candidate in find_all_patches():
|
|
43
|
+
if candidate.name == name:
|
|
44
|
+
patch = candidate
|
|
20
45
|
break
|
|
21
46
|
|
|
22
|
-
if
|
|
23
|
-
print(f"error=
|
|
47
|
+
if patch is None:
|
|
48
|
+
print(f"error=Patch '{name}' not found")
|
|
24
49
|
print("description=")
|
|
25
50
|
print("diff=")
|
|
26
51
|
print("workspace_dir=")
|
|
@@ -28,7 +53,7 @@ def main(*, name: str) -> None:
|
|
|
28
53
|
print("branch_name=")
|
|
29
54
|
return
|
|
30
55
|
|
|
31
|
-
project_file =
|
|
56
|
+
project_file = patch.file_path
|
|
32
57
|
workspace_dir = parse_workspace_dir(project_file)
|
|
33
58
|
if not workspace_dir:
|
|
34
59
|
print("error=WORKSPACE_DIR is not set for this project")
|
|
@@ -43,7 +68,7 @@ def main(*, name: str) -> None:
|
|
|
43
68
|
default_branch = default_branch_ref.rsplit("/", 1)[-1]
|
|
44
69
|
|
|
45
70
|
# Get description
|
|
46
|
-
desc =
|
|
71
|
+
desc = patch.description or "No description"
|
|
47
72
|
|
|
48
73
|
# Get diff against default branch
|
|
49
74
|
try:
|
|
@@ -71,9 +96,15 @@ def main(*, name: str) -> None:
|
|
|
71
96
|
except Exception:
|
|
72
97
|
commits = ""
|
|
73
98
|
|
|
74
|
-
# Save diff to temp file for the prompt
|
|
99
|
+
# Save diff to a managed temp file for the prompt. The downstream workflow
|
|
100
|
+
# interpolates this path, so it outlives this process and is reclaimed by
|
|
101
|
+
# the managed temp-root reaper rather than deleted here.
|
|
75
102
|
diff_file = tempfile.NamedTemporaryFile(
|
|
76
|
-
mode="w",
|
|
103
|
+
mode="w",
|
|
104
|
+
suffix=".diff",
|
|
105
|
+
prefix="pr_desc_",
|
|
106
|
+
dir=get_sase_managed_tmpdir("gh-diffs"),
|
|
107
|
+
delete=False,
|
|
77
108
|
)
|
|
78
109
|
diff_file.write(diff)
|
|
79
110
|
diff_file.close()
|
|
@@ -17,7 +17,17 @@ from collections.abc import Mapping, Sequence
|
|
|
17
17
|
from pathlib import Path
|
|
18
18
|
from typing import TYPE_CHECKING, Any, Literal
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
try:
|
|
21
|
+
from sase.ace.patch import Patch, find_all_patches
|
|
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
|
+
ChangeSpec as Patch,
|
|
26
|
+
find_all_changespecs as find_all_patches,
|
|
27
|
+
)
|
|
28
|
+
from sase.ace.changespec.project_spec_path import ( # type: ignore[no-redef]
|
|
29
|
+
preferred_project_spec_path,
|
|
30
|
+
)
|
|
21
31
|
from sase.workspace_provider import (
|
|
22
32
|
ExternalRepoCloneResult,
|
|
23
33
|
ResolvedRef,
|
|
@@ -456,17 +466,18 @@ class GitHubWorkspacePlugin:
|
|
|
456
466
|
project_basename: str,
|
|
457
467
|
console: object | None = None,
|
|
458
468
|
) -> tuple[bool, str | None] | None:
|
|
459
|
-
"""Submit a GitHub
|
|
469
|
+
"""Submit a GitHub Patch by merging its PR."""
|
|
460
470
|
from sase.workspace_provider import detect_workflow_type
|
|
461
471
|
|
|
462
|
-
|
|
472
|
+
patch_file = changespec_file
|
|
473
|
+
patch_name = changespec_name
|
|
474
|
+
vcs_type = detect_workflow_type(patch_file)
|
|
463
475
|
if vcs_type != "gh":
|
|
464
476
|
return None
|
|
465
477
|
|
|
466
478
|
from rich.console import Console as RichConsole
|
|
467
479
|
from rich.markup import escape as escape_markup
|
|
468
480
|
|
|
469
|
-
from sase.ace.changespec import ChangeSpec, find_all_changespecs
|
|
470
481
|
from sase.ace.hooks.processes import (
|
|
471
482
|
kill_and_persist_all_running_processes,
|
|
472
483
|
)
|
|
@@ -483,14 +494,13 @@ class GitHubWorkspacePlugin:
|
|
|
483
494
|
console if isinstance(console, RichConsole) else None
|
|
484
495
|
)
|
|
485
496
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
changespec = cs
|
|
497
|
+
patch: Patch | None = None
|
|
498
|
+
for candidate in find_all_patches():
|
|
499
|
+
if candidate.name == patch_name:
|
|
500
|
+
patch = candidate
|
|
491
501
|
break
|
|
492
|
-
if
|
|
493
|
-
return (False, f"
|
|
502
|
+
if patch is None:
|
|
503
|
+
return (False, f"Patch '{patch_name}' not found")
|
|
494
504
|
|
|
495
505
|
log_fn = (
|
|
496
506
|
(lambda msg: rich_console.print(f"[cyan]{escape_markup(msg)}[/cyan]"))
|
|
@@ -498,31 +508,31 @@ class GitHubWorkspacePlugin:
|
|
|
498
508
|
else None
|
|
499
509
|
)
|
|
500
510
|
kill_and_persist_all_running_processes(
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
511
|
+
patch,
|
|
512
|
+
patch_file,
|
|
513
|
+
patch_name,
|
|
504
514
|
"Killed hook running on submitted PR.",
|
|
505
515
|
log_fn=log_fn,
|
|
506
516
|
)
|
|
507
517
|
|
|
508
|
-
|
|
518
|
+
all_patches = find_all_patches()
|
|
509
519
|
if has_active_children(
|
|
510
|
-
|
|
511
|
-
|
|
520
|
+
patch,
|
|
521
|
+
all_patches,
|
|
512
522
|
terminal_statuses=("Submitted", "Reverted", "Archived"),
|
|
513
523
|
):
|
|
514
524
|
return (
|
|
515
525
|
False,
|
|
516
|
-
"Cannot submit: other
|
|
526
|
+
"Cannot submit: other Patches have this one as their "
|
|
517
527
|
"parent and are not Submitted, Reverted, or Archived",
|
|
518
528
|
)
|
|
519
529
|
|
|
520
|
-
workspace_dir = parse_workspace_dir(
|
|
530
|
+
workspace_dir = parse_workspace_dir(patch_file)
|
|
521
531
|
if not workspace_dir:
|
|
522
532
|
return (False, "WORKSPACE_DIR is not set for this project")
|
|
523
533
|
|
|
524
|
-
workspace_num = get_first_available_axe_workspace(
|
|
525
|
-
workflow_name = f"submit-{
|
|
534
|
+
workspace_num = get_first_available_axe_workspace(patch_file)
|
|
535
|
+
workflow_name = f"submit-{patch_name}"
|
|
526
536
|
pid = os.getpid()
|
|
527
537
|
|
|
528
538
|
try:
|
|
@@ -534,11 +544,11 @@ class GitHubWorkspacePlugin:
|
|
|
534
544
|
rich_console.print(f"[cyan]Claiming workspace #{workspace_num}[/cyan]")
|
|
535
545
|
|
|
536
546
|
if not claim_workspace(
|
|
537
|
-
|
|
547
|
+
patch_file,
|
|
538
548
|
workspace_num,
|
|
539
549
|
workflow_name,
|
|
540
550
|
pid,
|
|
541
|
-
|
|
551
|
+
patch_name,
|
|
542
552
|
):
|
|
543
553
|
return (
|
|
544
554
|
False,
|
|
@@ -548,12 +558,12 @@ class GitHubWorkspacePlugin:
|
|
|
548
558
|
try:
|
|
549
559
|
if rich_console:
|
|
550
560
|
rich_console.print(
|
|
551
|
-
f"[cyan]Checking out {escape_markup(
|
|
561
|
+
f"[cyan]Checking out {escape_markup(patch_name)}...[/cyan]"
|
|
552
562
|
)
|
|
553
563
|
|
|
554
564
|
provider = get_vcs_provider(ws_dir)
|
|
555
565
|
branch_name = provider.resolve_revision(
|
|
556
|
-
|
|
566
|
+
patch_name, project_basename, ws_dir
|
|
557
567
|
)
|
|
558
568
|
success, error = provider.checkout(branch_name, ws_dir)
|
|
559
569
|
if not success:
|
|
@@ -564,47 +574,46 @@ class GitHubWorkspacePlugin:
|
|
|
564
574
|
|
|
565
575
|
if rich_console:
|
|
566
576
|
rich_console.print(
|
|
567
|
-
f"[cyan]Merging {escape_markup(
|
|
577
|
+
f"[cyan]Merging {escape_markup(patch_name)} into "
|
|
568
578
|
f"{escape_markup(default_branch)}...[/cyan]"
|
|
569
579
|
)
|
|
570
580
|
|
|
571
581
|
# Prefer the recorded PR URL/number when available — this is
|
|
572
582
|
# resilient to branch renames (e.g. suffix strip/append).
|
|
573
|
-
pr_number = _extract_pr_number(
|
|
583
|
+
pr_number = _extract_pr_number(patch.pr_url)
|
|
574
584
|
if pr_number:
|
|
575
585
|
pr_state = _check_pr_state(pr_number, ws_dir)
|
|
576
586
|
if pr_state == "OPEN":
|
|
577
587
|
return _submit_via_pr_merge(
|
|
578
|
-
|
|
588
|
+
patch, ws_dir, rich_console, pr_number=pr_number
|
|
579
589
|
)
|
|
580
590
|
elif pr_state == "CLOSED":
|
|
581
591
|
return (
|
|
582
592
|
False,
|
|
583
|
-
f"PR #{pr_number} (from
|
|
593
|
+
f"PR #{pr_number} (from Patch PR field) is closed "
|
|
584
594
|
"and unmerged. Reopen it or create a new PR with #pr.",
|
|
585
595
|
)
|
|
586
596
|
elif pr_state == "MERGED":
|
|
587
597
|
return (
|
|
588
598
|
False,
|
|
589
|
-
f"PR #{pr_number} (from
|
|
590
|
-
"merged.",
|
|
599
|
+
f"PR #{pr_number} (from Patch PR field) is already merged.",
|
|
591
600
|
)
|
|
592
601
|
# pr_state is None — fall through to branch-based check
|
|
593
602
|
|
|
594
603
|
# Fallback: check for a PR on the current branch
|
|
595
604
|
has_pr = _check_existing_pr(ws_dir)
|
|
596
605
|
if has_pr:
|
|
597
|
-
return _submit_via_pr_merge(
|
|
606
|
+
return _submit_via_pr_merge(patch, ws_dir, rich_console)
|
|
598
607
|
return (
|
|
599
608
|
False,
|
|
600
609
|
"GitHub project has no PR for this branch. Create a PR first with #pr.",
|
|
601
610
|
)
|
|
602
611
|
finally:
|
|
603
612
|
release_workspace(
|
|
604
|
-
|
|
613
|
+
patch_file,
|
|
605
614
|
workspace_num,
|
|
606
615
|
workflow_name,
|
|
607
|
-
|
|
616
|
+
patch_name,
|
|
608
617
|
)
|
|
609
618
|
if rich_console:
|
|
610
619
|
rich_console.print(f"[cyan]Released workspace #{workspace_num}[/cyan]")
|
|
@@ -1650,8 +1659,6 @@ def _resolve_existing_named_ref(
|
|
|
1650
1659
|
read_only: bool,
|
|
1651
1660
|
strict: bool,
|
|
1652
1661
|
) -> ResolvedRef | None:
|
|
1653
|
-
from sase.ace.changespec import find_all_changespecs
|
|
1654
|
-
|
|
1655
1662
|
projects_base = _projects_base()
|
|
1656
1663
|
alias_record = _find_project_record_for_alias(
|
|
1657
1664
|
_list_project_records(projects_base),
|
|
@@ -1675,20 +1682,20 @@ def _resolve_existing_named_ref(
|
|
|
1675
1682
|
checkout_target=checkout_target,
|
|
1676
1683
|
)
|
|
1677
1684
|
|
|
1678
|
-
for
|
|
1679
|
-
if
|
|
1685
|
+
for patch in find_all_patches():
|
|
1686
|
+
if patch.name != gh_ref:
|
|
1680
1687
|
continue
|
|
1681
|
-
workspace_dir = parse_workspace_dir(
|
|
1688
|
+
workspace_dir = parse_workspace_dir(patch.file_path)
|
|
1682
1689
|
if not workspace_dir:
|
|
1683
1690
|
if strict:
|
|
1684
1691
|
raise ValueError(
|
|
1685
|
-
f"
|
|
1692
|
+
f"Patch '{gh_ref}' found in {patch.file_path} "
|
|
1686
1693
|
"but WORKSPACE_DIR is not set"
|
|
1687
1694
|
)
|
|
1688
1695
|
return None
|
|
1689
1696
|
return ResolvedRef(
|
|
1690
|
-
project_file=
|
|
1691
|
-
project_name=
|
|
1697
|
+
project_file=patch.file_path,
|
|
1698
|
+
project_name=patch.project_basename,
|
|
1692
1699
|
primary_workspace_dir=workspace_dir,
|
|
1693
1700
|
checkout_target=f"origin/{gh_ref}",
|
|
1694
1701
|
)
|
|
@@ -1717,7 +1724,7 @@ def resolve_gh_ref(gh_ref: str) -> ResolvedRef:
|
|
|
1717
1724
|
2. **Project shorthand** (no ``/``, matching project dir): look up
|
|
1718
1725
|
WORKSPACE_DIR from ``~/.sase/projects/<name>/<name>.sase``
|
|
1719
1726
|
(with legacy ``.gp`` fallback).
|
|
1720
|
-
3. **
|
|
1727
|
+
3. **Patch name**: search all Patches for a matching name,
|
|
1721
1728
|
read WORKSPACE_DIR from its project file.
|
|
1722
1729
|
|
|
1723
1730
|
Raises:
|
|
@@ -1782,7 +1789,7 @@ def _check_existing_pr(cwd: str) -> bool:
|
|
|
1782
1789
|
|
|
1783
1790
|
|
|
1784
1791
|
def _submit_via_pr_merge(
|
|
1785
|
-
|
|
1792
|
+
patch: object,
|
|
1786
1793
|
ws_dir: str,
|
|
1787
1794
|
console: object | None,
|
|
1788
1795
|
*,
|
|
@@ -1838,7 +1845,7 @@ def _submit_via_pr_merge(
|
|
|
1838
1845
|
|
|
1839
1846
|
from sase.workspace_provider.submission_utils import finalize_submission
|
|
1840
1847
|
|
|
1841
|
-
return finalize_submission(
|
|
1848
|
+
return finalize_submission(patch.file_path, patch.name, console) # type: ignore[attr-defined, arg-type]
|
|
1842
1849
|
|
|
1843
1850
|
|
|
1844
1851
|
def _non_interactive_gh_env(base: Mapping[str, str] | None = None) -> dict[str, str]:
|
|
@@ -5,7 +5,7 @@ tags: vcs, rollover
|
|
|
5
5
|
input:
|
|
6
6
|
- name: gh_ref
|
|
7
7
|
type: word
|
|
8
|
-
description: GitHub repo, PR, branch, or
|
|
8
|
+
description: GitHub repo, PR, branch, or Patch reference to check out.
|
|
9
9
|
- name: n
|
|
10
10
|
type: int
|
|
11
11
|
default: null
|
|
@@ -68,14 +68,14 @@ steps:
|
|
|
68
68
|
provider = get_vcs_provider(os.getcwd())
|
|
69
69
|
|
|
70
70
|
# Checkout the PR branch so that subsequent diffs are relative to the
|
|
71
|
-
# PR tip rather than the default branch.
|
|
71
|
+
# PR tip rather than the default branch. Try checkout_target first
|
|
72
72
|
# (works for default-branch refs like origin/main), then fall back to
|
|
73
|
-
#
|
|
74
|
-
|
|
73
|
+
# Patch name -> branch name conversion.
|
|
74
|
+
is_patch = checkout_target == f"origin/{gh_ref}"
|
|
75
75
|
ok, err = provider.checkout(checkout_target, os.getcwd())
|
|
76
76
|
if not ok:
|
|
77
|
-
if
|
|
78
|
-
#
|
|
77
|
+
if is_patch:
|
|
78
|
+
# Patch ref — resolve name to branch name
|
|
79
79
|
resolved = provider.resolve_revision(
|
|
80
80
|
gh_ref, project_name, os.getcwd()
|
|
81
81
|
)
|
|
@@ -138,7 +138,9 @@ steps:
|
|
|
138
138
|
exit 0
|
|
139
139
|
fi
|
|
140
140
|
head_now=$(git rev-parse HEAD)
|
|
141
|
-
|
|
141
|
+
diff_dir="${SASE_TMPDIR:-${SASE_HOME:-$HOME/.sase}/tmp}/gh-diffs"
|
|
142
|
+
mkdir -p "$diff_dir"
|
|
143
|
+
diff_file=$(mktemp "$diff_dir/sase-gh-XXXXXX.diff")
|
|
142
144
|
if [ "$head_now" != "$head_before" ]; then
|
|
143
145
|
# Commits were made — show only the last commit's diff
|
|
144
146
|
git diff HEAD~1 HEAD > "$diff_file" 2>/dev/null
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
description: Generate and apply a GitHub pull request title and body from a
|
|
1
|
+
description: Generate and apply a GitHub pull request title and body from a Patch.
|
|
2
2
|
|
|
3
3
|
input:
|
|
4
4
|
- name: name
|
|
5
5
|
type: word
|
|
6
|
-
description:
|
|
6
|
+
description: Patch name whose branch and diff should drive the PR description.
|
|
7
7
|
|
|
8
8
|
steps:
|
|
9
9
|
- name: get_context
|
|
@@ -24,7 +24,7 @@ steps:
|
|
|
24
24
|
agent: |
|
|
25
25
|
Generate a PR title and body for the following changes.
|
|
26
26
|
|
|
27
|
-
##
|
|
27
|
+
## Patch Description
|
|
28
28
|
{{ get_context.description }}
|
|
29
29
|
|
|
30
30
|
## Commits
|
|
@@ -1914,8 +1914,8 @@ class TestResolveGhRef:
|
|
|
1914
1914
|
@patch(
|
|
1915
1915
|
"sase_github.workspace_plugin.get_default_branch", return_value="origin/main"
|
|
1916
1916
|
)
|
|
1917
|
-
@patch("
|
|
1918
|
-
def
|
|
1917
|
+
@patch("sase_github.workspace_plugin.find_all_patches")
|
|
1918
|
+
def test_patch_name(
|
|
1919
1919
|
self,
|
|
1920
1920
|
mock_find: MagicMock,
|
|
1921
1921
|
mock_branch: MagicMock,
|
|
@@ -1941,8 +1941,8 @@ class TestResolveGhRef:
|
|
|
1941
1941
|
assert result.project_name == "proj"
|
|
1942
1942
|
assert result.canonical_ref is None
|
|
1943
1943
|
|
|
1944
|
-
@patch("
|
|
1945
|
-
def
|
|
1944
|
+
@patch("sase_github.workspace_plugin.find_all_patches")
|
|
1945
|
+
def test_patch_no_workspace_dir(self, mock_find: MagicMock) -> None:
|
|
1946
1946
|
with tempfile.NamedTemporaryFile(mode="w", suffix=".sase", delete=False) as f:
|
|
1947
1947
|
f.write("NAME: my-feature\n")
|
|
1948
1948
|
f.flush()
|
|
@@ -1961,7 +1961,7 @@ class TestResolveGhRef:
|
|
|
1961
1961
|
resolve_gh_ref("my-feature")
|
|
1962
1962
|
os.unlink(f.name)
|
|
1963
1963
|
|
|
1964
|
-
@patch("
|
|
1964
|
+
@patch("sase_github.workspace_plugin.find_all_patches", return_value=[])
|
|
1965
1965
|
def test_not_found(self, mock_find: MagicMock) -> None:
|
|
1966
1966
|
with patch(
|
|
1967
1967
|
"sase_github.workspace_plugin.Path.home",
|
|
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
|