Skip to content

Commit 9e93762

Browse files
committed
Maintainership converter command line tool
1 parent c08a656 commit 9e93762

2 files changed

Lines changed: 135 additions & 0 deletions

File tree

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import osc.commandline_git
2+
3+
4+
class MaintainershipCommand(osc.commandline_git.GitObsCommand):
5+
"""
6+
Read _maintainership.json and convert it from legacy format to the current format
7+
"""
8+
9+
name = "maintainership-converter"
10+
11+
def init_arguments(self):
12+
self.add_argument(
13+
"path",
14+
nargs="?",
15+
default="_maintainership.json",
16+
help="Path to the _maintainership.json file (default: %(default)s)",
17+
)
18+
19+
def run(self, args):
20+
from osc.gitea_api import maintainership
21+
22+
with open(args.path, "r", encoding="utf-8") as f:
23+
data = f.read()
24+
25+
obj = maintainership.Maintainership.from_string(data)
26+
print(obj.to_string())
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import contextlib
2+
import io
3+
import json
4+
import os
5+
import shutil
6+
import tempfile
7+
import unittest
8+
9+
10+
class TestMaintainershipConverter(unittest.TestCase):
11+
def setUp(self):
12+
self.tmpdir = tempfile.mkdtemp(prefix="osc_test_maintainership_converter_")
13+
14+
def tearDown(self):
15+
shutil.rmtree(self.tmpdir, ignore_errors=True)
16+
17+
def _write_file(self, text):
18+
path = os.path.join(self.tmpdir, "_maintainership.json")
19+
with open(path, "w", encoding="utf-8") as f:
20+
f.write(text)
21+
return path
22+
23+
def _run_converter(self, path):
24+
from osc.commands_git.maintainership_converter import MaintainershipCommand
25+
26+
cmd = MaintainershipCommand.__new__(MaintainershipCommand)
27+
28+
args = type("Args", (), {"path": path})()
29+
stdout = io.StringIO()
30+
with contextlib.redirect_stdout(stdout):
31+
cmd.run(args)
32+
return stdout.getvalue()
33+
34+
def test_legacy_format_converted(self):
35+
"""Legacy format is converted to v1.0 format."""
36+
path = self._write_file(
37+
'{"":["project-maintainer","@project-group"], "package1":["alice","@pkg1-group"], "package2":["bob"]}'
38+
)
39+
output = self._run_converter(path)
40+
result = json.loads(output)
41+
42+
self.assertEqual(result["header"]["document"], "obs-maintainers")
43+
self.assertEqual(result["header"]["version"], "1.0")
44+
45+
self.assertEqual(result["project"]["users"], ["project-maintainer"])
46+
self.assertEqual(result["project"]["groups"], ["project-group"])
47+
48+
self.assertEqual(result["packages"]["package1"]["users"], ["alice"])
49+
self.assertEqual(result["packages"]["package1"]["groups"], ["pkg1-group"])
50+
51+
self.assertEqual(result["packages"]["package2"]["users"], ["bob"])
52+
self.assertIsNone(result["packages"]["package2"]["groups"])
53+
54+
def test_v1_format_passthrough(self):
55+
"""v1.0 format is printed unchanged."""
56+
path = self._write_file(
57+
'{"header":{"document":"obs-maintainers","version":"1.0"},'
58+
'"project":{"users":["alice"],"groups":null},'
59+
'"packages":{"pkg1":{"users":["bob"],"groups":["team"]},'
60+
'"pkg2":{"users":null,"groups":["team"]}}}'
61+
)
62+
output = self._run_converter(path)
63+
result = json.loads(output)
64+
65+
self.assertEqual(result["header"]["document"], "obs-maintainers")
66+
self.assertEqual(result["header"]["version"], "1.0")
67+
self.assertEqual(result["project"]["users"], ["alice"])
68+
self.assertEqual(result["packages"]["pkg1"]["users"], ["bob"])
69+
self.assertEqual(result["packages"]["pkg1"]["groups"], ["team"])
70+
71+
def test_legacy_empty_project_maintainers(self):
72+
"""Legacy format with no project-level maintainers."""
73+
path = self._write_file('{"package1":["alice"]}')
74+
output = self._run_converter(path)
75+
result = json.loads(output)
76+
77+
self.assertEqual(result["header"]["document"], "obs-maintainers")
78+
self.assertIsNone(result["project"]["users"])
79+
self.assertIsNone(result["project"]["groups"])
80+
self.assertEqual(result["packages"]["package1"]["users"], ["alice"])
81+
82+
def test_legacy_groups_only(self):
83+
"""Legacy format with only groups, no users."""
84+
path = self._write_file('{"":["@admins","@maintainers"],"pkg":["@team"]}')
85+
output = self._run_converter(path)
86+
result = json.loads(output)
87+
88+
self.assertIsNone(result["project"]["users"])
89+
self.assertEqual(result["project"]["groups"], ["admins", "maintainers"])
90+
self.assertIsNone(result["packages"]["pkg"]["users"])
91+
self.assertEqual(result["packages"]["pkg"]["groups"], ["team"])
92+
93+
def test_file_not_found(self):
94+
"""Non-existent file raises an error."""
95+
path = os.path.join(self.tmpdir, "nonexistent.json")
96+
with self.assertRaises(FileNotFoundError):
97+
self._run_converter(path)
98+
99+
def test_invalid_json(self):
100+
"""Invalid JSON raises an error."""
101+
path = os.path.join(self.tmpdir, "_maintainership.json")
102+
with open(path, "w") as f:
103+
f.write("not valid json")
104+
with self.assertRaises(json.JSONDecodeError):
105+
self._run_converter(path)
106+
107+
108+
if __name__ == "__main__":
109+
unittest.main()

0 commit comments

Comments
 (0)