Merge pull request #567 from AIOSAI/dev
fix(ci): auto-format on commit, decouple lint from tests, codecov threshold
This commit is contained in:
@@ -6,6 +6,9 @@ on:
|
||||
pull_request:
|
||||
branches: [main, dev]
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -19,7 +22,6 @@ jobs:
|
||||
- run: ruff format --check src/ tests/
|
||||
|
||||
test:
|
||||
needs: lint
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
|
||||
@@ -8,6 +8,9 @@ on:
|
||||
schedule:
|
||||
- cron: "0 6 * * 1"
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
||||
|
||||
jobs:
|
||||
dependency-scan:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
coverage:
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
target: 75%
|
||||
threshold: 2%
|
||||
patch:
|
||||
default:
|
||||
target: 70%
|
||||
|
||||
comment:
|
||||
layout: "reach,diff,flags,files"
|
||||
behavior: default
|
||||
require_changes: false
|
||||
+1
-1
@@ -55,7 +55,7 @@ dev = [
|
||||
"pytest>=9.0.3",
|
||||
"pytest-cov",
|
||||
"pytest-timeout",
|
||||
"ruff",
|
||||
"ruff>=0.11",
|
||||
"coverage",
|
||||
"pyright",
|
||||
"Pygments>=2.20.0",
|
||||
|
||||
@@ -64,6 +64,7 @@ from aipass.aipass.apps.handlers.ui.progress import (
|
||||
|
||||
_BRANCH_ROOT = Path(__file__).resolve().parents[2]
|
||||
|
||||
|
||||
class CheckResult(NamedTuple):
|
||||
"""Single doctor check result."""
|
||||
|
||||
@@ -426,8 +427,10 @@ def _print_manual_wire_warning(
|
||||
console.print(f" [dim]•[/dim] {var} — {desc}")
|
||||
console.print()
|
||||
if missing_deny or missing_ask:
|
||||
console.print(f"{len(missing_deny)} deny rules + {len(missing_ask)} ask rules"
|
||||
" (protect ~/.secrets/, block destructive git)")
|
||||
console.print(
|
||||
f"{len(missing_deny)} deny rules + {len(missing_ask)} ask rules"
|
||||
" (protect ~/.secrets/, block destructive git)"
|
||||
)
|
||||
console.print()
|
||||
console.print("[dim]Wire manually when ready — see .claude/hooks/README.md[/dim]")
|
||||
|
||||
@@ -555,8 +558,7 @@ def run_doctor(verbose: bool = False, interactive: bool = False, fix: bool = Fal
|
||||
manifest_results = _check_provider_manifest(interactive=interactive, fix=fix)
|
||||
if manifest_results:
|
||||
groups["Services"] = [
|
||||
r for r in groups.get("Services", [])
|
||||
if r.label not in ("hooks", "env vars", "permissions")
|
||||
r for r in groups.get("Services", []) if r.label not in ("hooks", "env vars", "permissions")
|
||||
] + manifest_results
|
||||
|
||||
pass_count = 0
|
||||
|
||||
@@ -157,10 +157,7 @@ def _auto_wire_provider(manifest_path: Path, interactive: bool = True) -> List[s
|
||||
event_hooks = [event_hooks]
|
||||
settings["hooks"][event] = event_hooks
|
||||
|
||||
already_wired = any(
|
||||
isinstance(h, dict) and script in json.dumps(h)
|
||||
for h in event_hooks
|
||||
)
|
||||
already_wired = any(isinstance(h, dict) and script in json.dumps(h) for h in event_hooks)
|
||||
if not already_wired:
|
||||
if source_type == "user":
|
||||
hook_path = f"~/.claude/hooks/{script}"
|
||||
|
||||
@@ -615,7 +615,9 @@ def _write_init_report(agent_path: str, accumulated: Dict[str, Any], dry_run: bo
|
||||
provider_gaps = accumulated.get("provider_gaps", {})
|
||||
if provider_gaps:
|
||||
report["provider_gaps"] = provider_gaps
|
||||
report["provider_action"] = "Provider settings need configuring. Tell the user what is missing and point them to provider_manifest.json for details."
|
||||
report["provider_action"] = (
|
||||
"Provider settings need configuring. Tell the user what is missing and point them to provider_manifest.json for details."
|
||||
)
|
||||
report_path = dropbox / "init_report.json"
|
||||
report_path.write_text(json.dumps(report, indent=2) + "\n", encoding="utf-8")
|
||||
logger.info("[init_flow] init report written to %s", report_path)
|
||||
|
||||
@@ -67,6 +67,20 @@ def commit_changes(
|
||||
repo_root = find_repo_root()
|
||||
|
||||
if all_files:
|
||||
import shutil
|
||||
|
||||
ruff_bin = shutil.which("ruff")
|
||||
if not ruff_bin:
|
||||
venv_ruff = repo_root / ".venv" / "bin" / "ruff"
|
||||
if venv_ruff.exists():
|
||||
ruff_bin = str(venv_ruff)
|
||||
if ruff_bin:
|
||||
subprocess.run(
|
||||
[ruff_bin, "format", "src/", "tests/"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
cwd=str(repo_root),
|
||||
)
|
||||
add_result = subprocess.run(
|
||||
["git", "add", "-A"],
|
||||
capture_output=True,
|
||||
|
||||
@@ -82,7 +82,9 @@ def create_dev_pr(description: str) -> dict:
|
||||
if "github.com" in line:
|
||||
existing_url = line.strip()
|
||||
break
|
||||
msg = f"Pushed to dev. PR already open: {existing_url}" if existing_url else "Pushed to dev. PR already open."
|
||||
msg = (
|
||||
f"Pushed to dev. PR already open: {existing_url}" if existing_url else "Pushed to dev. PR already open."
|
||||
)
|
||||
json_handler.log_operation("dev_pr_push_existing", {"pr_url": existing_url, "description": description})
|
||||
return {"success": True, "message": msg, "pr_url": existing_url}
|
||||
return {"success": False, "message": f"PR creation failed: {stderr}", "pr_url": ""}
|
||||
|
||||
@@ -570,8 +570,7 @@ def get_help(command: str | None = None) -> str:
|
||||
)
|
||||
if command == "delete-branch":
|
||||
return (
|
||||
"git delete-branch <name> — Delete a remote branch [owner]\n"
|
||||
" Protected: main and dev cannot be deleted.\n"
|
||||
"git delete-branch <name> — Delete a remote branch [owner]\n Protected: main and dev cannot be deleted.\n"
|
||||
)
|
||||
if command == "commit":
|
||||
return (
|
||||
|
||||
@@ -343,15 +343,19 @@ class TestCommitChanges:
|
||||
assert "nothing to commit" in result["stderr"].lower()
|
||||
|
||||
def test_commit_all_stages_first(self, repo_dir: Path) -> None:
|
||||
mock_ruff = MagicMock(returncode=0, stdout="", stderr="")
|
||||
mock_add = MagicMock(returncode=0, stderr="")
|
||||
mock_diff = MagicMock(returncode=1, stdout="", stderr="")
|
||||
mock_commit = MagicMock(returncode=0, stdout="[main def456] all commit", stderr="")
|
||||
|
||||
branch_dir = repo_dir / "src" / "aipass" / "api"
|
||||
|
||||
with patch(
|
||||
"aipass.drone.apps.handlers.git.commit_handler.subprocess.run",
|
||||
side_effect=[mock_add, mock_diff, mock_commit],
|
||||
with (
|
||||
patch("shutil.which", return_value="/usr/bin/ruff"),
|
||||
patch(
|
||||
"aipass.drone.apps.handlers.git.commit_handler.subprocess.run",
|
||||
side_effect=[mock_ruff, mock_add, mock_diff, mock_commit],
|
||||
),
|
||||
):
|
||||
result = commit_changes("all commit", branch_dir=branch_dir, all_files=True)
|
||||
|
||||
@@ -427,15 +431,16 @@ class TestCheckoutHandler:
|
||||
def test_checkout_git_failure(self, repo_dir: Path) -> None:
|
||||
mock_status = MagicMock(returncode=0, stdout="", stderr="")
|
||||
mock_checkout = MagicMock(returncode=1, stdout="", stderr="error: pathspec 'main' did not match")
|
||||
mock_create = MagicMock(returncode=0, stdout="Switched to a new branch 'main'", stderr="")
|
||||
|
||||
with patch(
|
||||
"aipass.drone.apps.handlers.git.checkout_handler.subprocess.run",
|
||||
side_effect=[mock_status, mock_checkout],
|
||||
side_effect=[mock_status, mock_checkout, mock_create],
|
||||
):
|
||||
result = checkout_branch("main")
|
||||
|
||||
assert result["exit_code"] == 1
|
||||
assert result["current_branch"] == ""
|
||||
assert result["exit_code"] == 0
|
||||
assert result["current_branch"] == "main"
|
||||
|
||||
|
||||
# ===========================================================================
|
||||
@@ -554,11 +559,11 @@ class TestUpdatedHelp:
|
||||
assert "global" in text.lower()
|
||||
assert "owner" in text.lower()
|
||||
|
||||
def test_help_marks_pr_deprecated(self) -> None:
|
||||
def test_help_marks_pr_legacy(self) -> None:
|
||||
from aipass.drone.apps.modules.git_module import get_help
|
||||
|
||||
text = get_help()
|
||||
assert "deprecated" in text.lower()
|
||||
assert "legacy" in text.lower()
|
||||
|
||||
def test_introspection_includes_new_handlers(self) -> None:
|
||||
from aipass.drone.apps.modules.git_module import get_introspective
|
||||
|
||||
@@ -295,14 +295,14 @@ class TestSyncHandler:
|
||||
registry.write_text("{}", encoding="utf-8")
|
||||
monkeypatch.chdir(tmp_path)
|
||||
|
||||
mock_checkout = MagicMock(returncode=0, stdout="Switched to branch 'main'", stderr="")
|
||||
mock_head = MagicMock(returncode=0, stdout="main", stderr="")
|
||||
mock_fetch = MagicMock(returncode=0, stdout="", stderr="")
|
||||
mock_rev_list = MagicMock(returncode=0, stdout="0\t0\n", stderr="")
|
||||
mock_pull = MagicMock(returncode=0, stdout="Already up to date.", stderr="")
|
||||
|
||||
with patch(
|
||||
"aipass.drone.apps.handlers.git.sync_handler.subprocess.run",
|
||||
side_effect=[mock_checkout, mock_fetch, mock_rev_list, mock_pull],
|
||||
side_effect=[mock_head, mock_fetch, mock_rev_list, mock_pull],
|
||||
):
|
||||
result = sync_main()
|
||||
|
||||
@@ -335,14 +335,14 @@ class TestSyncHandler:
|
||||
registry.write_text("{}", encoding="utf-8")
|
||||
monkeypatch.chdir(tmp_path)
|
||||
|
||||
mock_checkout = MagicMock(returncode=0, stdout="", stderr="")
|
||||
mock_head = MagicMock(returncode=0, stdout="main", stderr="")
|
||||
mock_fetch = MagicMock(returncode=0, stdout="", stderr="")
|
||||
mock_rev_list = MagicMock(returncode=0, stdout="0\t1\n", stderr="")
|
||||
mock_pull = MagicMock(returncode=1, stdout="", stderr="fatal: unable to access remote")
|
||||
|
||||
with patch(
|
||||
"aipass.drone.apps.handlers.git.sync_handler.subprocess.run",
|
||||
side_effect=[mock_checkout, mock_fetch, mock_rev_list, mock_pull],
|
||||
side_effect=[mock_head, mock_fetch, mock_rev_list, mock_pull],
|
||||
):
|
||||
result = sync_main()
|
||||
|
||||
@@ -658,14 +658,14 @@ class TestGitModuleRouting:
|
||||
registry.write_text("{}", encoding="utf-8")
|
||||
monkeypatch.chdir(tmp_path)
|
||||
|
||||
mock_checkout = MagicMock(returncode=0, stdout="", stderr="")
|
||||
mock_head = MagicMock(returncode=0, stdout="main", stderr="")
|
||||
mock_fetch = MagicMock(returncode=0, stdout="", stderr="")
|
||||
mock_rev_list = MagicMock(returncode=0, stdout="0\t0\n", stderr="")
|
||||
mock_pull = MagicMock(returncode=0, stdout="Already up to date.", stderr="")
|
||||
|
||||
with patch(
|
||||
"aipass.drone.apps.handlers.git.sync_handler.subprocess.run",
|
||||
side_effect=[mock_checkout, mock_fetch, mock_rev_list, mock_pull],
|
||||
side_effect=[mock_head, mock_fetch, mock_rev_list, mock_pull],
|
||||
):
|
||||
result = handle_command("sync")
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"metadata": {
|
||||
"version": "1.0.0",
|
||||
"last_updated": "2026-05-10",
|
||||
"last_updated": "2026-05-12",
|
||||
"description": "Template file tracking registry for ID-based updates"
|
||||
},
|
||||
"files": {
|
||||
@@ -155,7 +155,7 @@
|
||||
"content_hash": "a4cf0a8e3b4f",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f026": {
|
||||
"f015": {
|
||||
"path": "apps/modules/__init__.py",
|
||||
"name": "__init__.py",
|
||||
"content_hash": "e3b0c44298fc",
|
||||
@@ -263,7 +263,7 @@
|
||||
"content_hash": "28e9ae373563",
|
||||
"has_branch_placeholder": false
|
||||
},
|
||||
"f015": {
|
||||
"f026": {
|
||||
"path": "apps/plugins/__init__.py",
|
||||
"name": "__init__.py",
|
||||
"content_hash": "e3b0c44298fc",
|
||||
|
||||
Reference in New Issue
Block a user