1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
"""Tests for project actions API."""
from __future__ import annotations
import stat
import sys
from pathlib import Path
import pytest
from pygittools.actions import (
Action,
ActionSpec,
ProjectActions,
find_project_root,
load_action_specs,
project_actions,
project_venv_python,
run_action_script,
)
def _write_pyproject(root: Path, actions_toml: str) -> None:
(root / "pyproject.toml").write_text(
f'[project]\nname = "demo"\nversion = "0"\n\n[tool.pgt]\nactions = {actions_toml}\n',
encoding="utf-8",
)
def _make_venv(root: Path) -> Path:
venv = root / ".venv"
bin_dir = venv / "bin"
bin_dir.mkdir(parents=True)
python = bin_dir / "python"
python.symlink_to(sys.executable)
return python
def test_find_project_root(tmp_path: Path) -> None:
nested = tmp_path / "a" / "b"
nested.mkdir(parents=True)
(tmp_path / "pyproject.toml").write_text("[project]\nname='x'\n", encoding="utf-8")
assert find_project_root(nested) == tmp_path.resolve()
assert find_project_root(tmp_path) == tmp_path.resolve()
def test_load_action_specs_inline_tables(tmp_path: Path) -> None:
_write_pyproject(
tmp_path,
"""[
{ name = "lint", script = "scripts/lint.py" },
{ name = "test", script = "scripts/test.py" },
]""",
)
specs = load_action_specs(tmp_path)
assert specs == [
ActionSpec(name="lint", script="scripts/lint.py"),
ActionSpec(name="test", script="scripts/test.py"),
]
def test_load_action_specs_pair_arrays(tmp_path: Path) -> None:
_write_pyproject(tmp_path, '[["lint", "scripts/lint.py"], ["test", "scripts/test.sh"]]')
specs = load_action_specs(tmp_path)
assert [s.name for s in specs] == ["lint", "test"]
def test_load_action_specs_invalid(tmp_path: Path) -> None:
_write_pyproject(tmp_path, '{ name = "lint" }')
with pytest.raises(ValueError, match="must be an array"):
load_action_specs(tmp_path)
def test_project_actions_is_action(tmp_path: Path) -> None:
_write_pyproject(tmp_path, '[{ name = "ok", script = "ok.py" }]')
runner = project_actions(tmp_path)
assert isinstance(runner, Action)
assert issubclass(ProjectActions, Action)
@pytest.mark.skipif(sys.platform == "win32", reason="symlink venv setup requires SeCreateSymbolicLinkPrivilege")
def test_run_action_script_python_success(tmp_path: Path) -> None:
_make_venv(tmp_path)
script = tmp_path / "ok.py"
script.write_text("print('hello-ok')\n", encoding="utf-8")
ok, message = run_action_script(tmp_path, "ok.py")
assert ok is True
assert message == "hello-ok"
@pytest.mark.skipif(sys.platform == "win32", reason="symlink venv setup requires SeCreateSymbolicLinkPrivilege")
def test_run_action_script_python_failure(tmp_path: Path) -> None:
_make_venv(tmp_path)
script = tmp_path / "fail.py"
script.write_text("import sys\nprint('boom', file=sys.stderr)\nsys.exit(2)\n", encoding="utf-8")
ok, message = run_action_script(tmp_path, "fail.py")
assert ok is False
assert "boom" in message
def test_run_action_script_missing_venv(tmp_path: Path) -> None:
script = tmp_path / "ok.py"
script.write_text("print('x')\n", encoding="utf-8")
ok, message = run_action_script(tmp_path, "ok.py")
assert ok is False
assert "venv not found" in message
@pytest.mark.skipif(sys.platform == "win32", reason="symlink venv setup requires SeCreateSymbolicLinkPrivilege")
def test_run_action_script_shell(tmp_path: Path) -> None:
_make_venv(tmp_path)
script = tmp_path / "hi.sh"
script.write_text("#!/bin/bash\necho shell-ok\n", encoding="utf-8")
script.chmod(script.stat().st_mode | stat.S_IXUSR)
ok, message = run_action_script(tmp_path, "hi.sh")
assert ok is True
assert message == "shell-ok"
assert project_venv_python(tmp_path) is not None
def test_project_actions_unknown(tmp_path: Path) -> None:
_write_pyproject(tmp_path, "[]")
ok, message = ProjectActions(tmp_path).action("missing")
assert ok is False
assert "Unknown action" in message
@pytest.mark.skipif(sys.platform == "win32", reason="symlink venv setup requires SeCreateSymbolicLinkPrivilege")
def test_project_actions_runs_named(tmp_path: Path) -> None:
_make_venv(tmp_path)
_write_pyproject(tmp_path, '[{ name = "greet", script = "greet.py" }]')
(tmp_path / "greet.py").write_text("print('hi')\n", encoding="utf-8")
ok, message = ProjectActions(tmp_path).action("greet")
assert ok is True
assert message == "hi"