Merge pull request #359 from AIOSAI/citizen/drone

feat(drone): switch merge_plugin from --squash to --merge (straight merge commits only)
This commit is contained in:
AIPass
2026-04-20 12:12:58 -07:00
committed by GitHub
5 changed files with 33 additions and 17 deletions
+15
View File
@@ -127,6 +127,21 @@
"file": "apps/plugins/hook_sounds/hook_sounds_plugin.py",
"standard": "trigger",
"reason": "Simple toggle plugin — creates/removes a flag file. No state changes that warrant trigger events."
},
{
"file": "tests/test_devpulse_plugins.py",
"standard": "architecture",
"reason": "Test file — intentionally outside 3-layer structure. tests/ is a peer of apps/, not part of it."
},
{
"file": "tests/test_devpulse_plugins.py",
"standard": "deep_nesting",
"reason": "Test mock side_effect closures are necessarily nested inside test methods. Refactoring to top-level breaks test locality."
},
{
"file": "tests/test_devpulse_plugins.py",
"standard": "documentation",
"reason": "Test methods and mock helpers in this file are pre-existing without docstrings. Class docstrings describe intent; per-method docs would be noise."
}
],
"notes": {
+4 -4
View File
@@ -417,8 +417,8 @@ def get_help(command: str | None = None) -> str:
)
if command == "merge":
return (
"git merge <PR#> — Squash-merge a PR and sync local main (devpulse only)\n"
" Runs gh pr merge --squash --delete-branch, then git pull --rebase.\n"
"git merge <PR#> — Merge a PR and sync local main (devpulse only)\n"
" Runs gh pr merge --merge --delete-branch, then git pull --rebase.\n"
)
if command == "smart-sync":
return (
@@ -437,7 +437,7 @@ def get_help(command: str | None = None) -> str:
"Commands:\n"
" pr <description> Create a PR with scoped changes\n"
" system-pr <desc> Create a system-wide PR (devpulse only)\n"
" merge <PR#> Squash-merge a PR (devpulse only)\n"
" merge <PR#> Merge a PR (devpulse only)\n"
" smart-sync Fetch + rebase if behind (devpulse only)\n"
" fix Fix broken git states (devpulse only)\n"
" status Show git status for your branch\n"
@@ -462,7 +462,7 @@ def get_introspective() -> str:
" plugins/devpulse_ops/\n"
" - auth.py (verify_caller — passport-based authorization)\n"
" - pr_plugin.py (create_system_pr — system-wide PR workflow)\n"
" - merge_plugin.py (merge_pr — squash-merge PR + sync)\n"
" - merge_plugin.py (merge_pr — merge PR + sync)\n"
" - sync_plugin.py (smart_sync — fetch + rebase if behind)\n"
" - fix_plugin.py (fix_git_state — detect/fix broken states)\n"
"\n"
@@ -1,14 +1,14 @@
# =================== AIPass ====================
# Name: merge_plugin.py
# Description: Squash-merge a PR and sync local main
# Description: Merge a PR and sync local main
# Version: 1.0.0
# Created: 2026-03-30
# Modified: 2026-03-30
# =============================================
"""Squash-merge a PR and sync local main.
"""Merge a PR and sync local main.
Squash-merges the given PR number via ``gh``, deletes the remote branch,
Merges the given PR number via ``gh``, deletes the remote branch,
pulls to sync local main, and returns the merge commit hash and PR title.
Only authorized callers (verified via :mod:`auth`) may invoke this.
"""
@@ -23,7 +23,7 @@ from aipass.drone.apps.handlers.git.lock_handler import find_repo_root
def merge_pr(pr_number: str, caller: str) -> dict:
"""Squash-merge a PR and sync local main.
"""Merge a PR and sync local main.
Args:
pr_number: The PR number to merge (e.g. ``"42"``).
@@ -43,9 +43,9 @@ def merge_pr(pr_number: str, caller: str) -> dict:
}
try:
# Step 1: Squash-merge the PR
# Step 1: Merge the PR
merge = subprocess.run(
["gh", "pr", "merge", pr_number, "--squash", "--delete-branch"],
["gh", "pr", "merge", pr_number, "--merge", "--delete-branch"],
capture_output=True,
text=True,
cwd=str(repo_root),
@@ -11,9 +11,9 @@
Creates PRs from local main commits that are ahead of origin/main.
If there are uncommitted tracked changes, commits them first (like a
normal commit), then creates a feature branch at main's current tip,
pushes it, and opens a PR. Local main is never moved — after squash-
merge and pull, git detects the commits are already applied and skips
them cleanly.
pushes it, and opens a PR. Local main is never moved — after the
straight merge and pull, local commits are already in origin/main's
ancestry via the merge commit, so git pull fast-forwards cleanly.
Only authorized callers (verified via :mod:`auth`) may invoke this.
"""
@@ -59,8 +59,9 @@ def create_system_pr(description: str, caller: str) -> dict:
(just a normal commit). Then creates a feature branch at main's tip,
pushes it, and opens a PR. Local main is never artificially moved.
After GitHub squash-merges the PR, ``git pull --rebase`` detects that
the local commits are already applied and skips them cleanly.
After GitHub merges the PR, local commits are already in origin/main's
ancestry via the merge commit — ``git pull --rebase`` fast-forwards
cleanly without replaying any commits.
Args:
description: Short description for the PR title/commit.
@@ -119,7 +119,7 @@ class TestAuthDenialFix:
class TestMergePrHappyPath:
"""merge_pr should squash-merge, pull, and return commit + title."""
"""merge_pr should merge, pull, and return commit + title."""
@patch("aipass.drone.apps.plugins.devpulse_ops.merge_plugin.find_repo_root")
@patch("aipass.drone.apps.plugins.devpulse_ops.merge_plugin.subprocess.run")
@@ -496,7 +496,7 @@ class TestGitModuleRouting:
from aipass.drone.apps.modules.git_module import get_help
help_text = get_help("merge")
assert "squash" in help_text.lower() or "Squash" in help_text
assert "merge" in help_text.lower()
def test_get_help_smart_sync_specific(self) -> None:
from aipass.drone.apps.modules.git_module import get_help