feat(git): git operations with allowlist gates and confirm on destructive ops
This commit is contained in:
16
src/rook/git.py
Normal file
16
src/rook/git.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import subprocess
|
||||||
|
from rook.policy import check_allowlist, confirm_spawn
|
||||||
|
|
||||||
|
|
||||||
|
class PolicyError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def run_git(args: list[str], cwd: str = '.') -> str:
|
||||||
|
if check_allowlist('git', args[0]) is False:
|
||||||
|
if not confirm_spawn('git ' + args[0], ' '.join(args)):
|
||||||
|
raise PolicyError(f"git {args[0]} denied by policy")
|
||||||
|
result = subprocess.run(['git'] + args, cwd=cwd, capture_output=True, text=True)
|
||||||
|
if result.returncode != 0:
|
||||||
|
raise RuntimeError(result.stderr)
|
||||||
|
return result.stdout
|
||||||
40
tests/test_git.py
Normal file
40
tests/test_git.py
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import pytest
|
||||||
|
from unittest.mock import patch, MagicMock
|
||||||
|
from rook.git import run_git, PolicyError
|
||||||
|
|
||||||
|
|
||||||
|
def test_git_status_runs():
|
||||||
|
with patch('rook.git.subprocess.run') as mock_run:
|
||||||
|
mock_run.return_value = MagicMock(stdout='On branch master', returncode=0)
|
||||||
|
result = run_git(['status'])
|
||||||
|
assert 'branch' in result
|
||||||
|
|
||||||
|
|
||||||
|
def test_git_commit_allowed():
|
||||||
|
with patch('rook.git.subprocess.run') as mock_run:
|
||||||
|
mock_run.return_value = MagicMock(stdout='', returncode=0)
|
||||||
|
run_git(['commit', '-m', 'msg'])
|
||||||
|
called_args = mock_run.call_args[0][0]
|
||||||
|
assert called_args[:2] == ['git', 'commit']
|
||||||
|
|
||||||
|
|
||||||
|
def test_git_push_requires_confirm_yes():
|
||||||
|
with patch('rook.git.confirm_spawn', return_value=True) as mock_confirm, \
|
||||||
|
patch('rook.git.subprocess.run') as mock_run:
|
||||||
|
mock_run.return_value = MagicMock(stdout='', returncode=0)
|
||||||
|
run_git(['push'])
|
||||||
|
mock_confirm.assert_called()
|
||||||
|
mock_run.assert_called()
|
||||||
|
|
||||||
|
|
||||||
|
def test_git_push_denied_raises_policy_error():
|
||||||
|
with patch('rook.git.confirm_spawn', return_value=False):
|
||||||
|
with pytest.raises(PolicyError):
|
||||||
|
run_git(['push'])
|
||||||
|
|
||||||
|
|
||||||
|
def test_git_nonzero_raises_runtime_error():
|
||||||
|
with patch('rook.git.subprocess.run') as mock_run:
|
||||||
|
mock_run.return_value = MagicMock(stdout='', stderr='fatal: error', returncode=128)
|
||||||
|
with pytest.raises(RuntimeError):
|
||||||
|
run_git(['status'])
|
||||||
Reference in New Issue
Block a user