Skip to content

Commit 06c5816

Browse files
committed
Maintainership converter command line tool
1 parent c08a656 commit 06c5816

2 files changed

Lines changed: 142 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: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import json
2+
import os
3+
import sys
4+
import io
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+
self.orig_stdout = sys.stdout
14+
sys.stdout = io.StringIO()
15+
16+
def tearDown(self):
17+
sys.stdout = self.orig_stdout
18+
shutil.rmtree(self.tmpdir, ignore_errors=True)
19+
20+
def _write_file(self, text):
21+
path = os.path.join(self.tmpdir, "_maintainership.json")
22+
with open(path, "w", encoding="utf-8") as f:
23+
f.write(text)
24+
return path
25+
26+
def _run_converter(self, path):
27+
from osc.commands_git.maintainership_converter import MaintainershipCommand
28+
29+
cmd = MaintainershipCommand.__new__(MaintainershipCommand)
30+
31+
args = type("Args", (), {"path": path})()
32+
cmd.run(args)
33+
return sys.stdout.getvalue()
34+
35+
def test_legacy_format_converted(self):
36+
"""Legacy format is converted to v1.0 format."""
37+
path = self._write_file(
38+
'{"":["project-maintainer","@project-group"],'
39+
'"package1":["alice","@pkg1-group"],'
40+
'"package2":["bob"]}'
41+
)
42+
output = self._run_converter(path)
43+
result = json.loads(output)
44+
45+
self.assertEqual(result["header"]["document"], "obs-maintainers")
46+
self.assertEqual(result["header"]["version"], "1.0")
47+
48+
self.assertEqual(result["project"]["users"], ["project-maintainer"])
49+
self.assertEqual(result["project"]["groups"], ["project-group"])
50+
51+
self.assertEqual(result["packages"]["package1"]["users"], ["alice"])
52+
self.assertEqual(result["packages"]["package1"]["groups"], ["pkg1-group"])
53+
54+
self.assertEqual(result["packages"]["package2"]["users"], ["bob"])
55+
self.assertIsNone(result["packages"]["package2"]["groups"])
56+
57+
def test_v1_format_passthrough(self):
58+
"""v1.0 format is printed unchanged."""
59+
path = self._write_file(
60+
'{"header":{"document":"obs-maintainers","version":"1.0"},'
61+
'"project":{"users":["alice"],"groups":null},'
62+
'"packages":{"pkg1":{"users":["bob"],"groups":["team"]},'
63+
'"pkg2":{"users":null,"groups":["team"]}}}'
64+
)
65+
output = self._run_converter(path)
66+
result = json.loads(output)
67+
68+
self.assertEqual(result["header"]["document"], "obs-maintainers")
69+
self.assertEqual(result["header"]["version"], "1.0")
70+
self.assertEqual(result["project"]["users"], ["alice"])
71+
self.assertEqual(result["packages"]["pkg1"]["users"], ["bob"])
72+
self.assertEqual(result["packages"]["pkg1"]["groups"], ["team"])
73+
74+
def test_legacy_empty_project_maintainers(self):
75+
"""Legacy format with no project-level maintainers."""
76+
path = self._write_file(
77+
'{"package1":["alice"]}'
78+
)
79+
output = self._run_converter(path)
80+
result = json.loads(output)
81+
82+
self.assertEqual(result["header"]["document"], "obs-maintainers")
83+
self.assertIsNone(result["project"]["users"])
84+
self.assertIsNone(result["project"]["groups"])
85+
self.assertEqual(result["packages"]["package1"]["users"], ["alice"])
86+
87+
def test_legacy_groups_only(self):
88+
"""Legacy format with only groups, no users."""
89+
path = self._write_file(
90+
'{"":["@admins","@maintainers"],"pkg":["@team"]}'
91+
)
92+
output = self._run_converter(path)
93+
result = json.loads(output)
94+
95+
self.assertIsNone(result["project"]["users"])
96+
self.assertEqual(result["project"]["groups"], ["admins", "maintainers"])
97+
self.assertIsNone(result["packages"]["pkg"]["users"])
98+
self.assertEqual(result["packages"]["pkg"]["groups"], ["team"])
99+
100+
def test_file_not_found(self):
101+
"""Non-existent file raises an error."""
102+
path = os.path.join(self.tmpdir, "nonexistent.json")
103+
with self.assertRaises(FileNotFoundError):
104+
self._run_converter(path)
105+
106+
def test_invalid_json(self):
107+
"""Invalid JSON raises an error."""
108+
path = os.path.join(self.tmpdir, "_maintainership.json")
109+
with open(path, "w") as f:
110+
f.write("not valid json")
111+
with self.assertRaises(json.JSONDecodeError):
112+
self._run_converter(path)
113+
114+
115+
if __name__ == "__main__":
116+
unittest.main()

0 commit comments

Comments
 (0)