Skip to content

Commit b22d9f1

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

2 files changed

Lines changed: 141 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: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
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"],'
38+
'"package1":["alice","@pkg1-group"],'
39+
'"package2":["bob"]}'
40+
)
41+
output = self._run_converter(path)
42+
result = json.loads(output)
43+
44+
self.assertEqual(result["header"]["document"], "obs-maintainers")
45+
self.assertEqual(result["header"]["version"], "1.0")
46+
47+
self.assertEqual(result["project"]["users"], ["project-maintainer"])
48+
self.assertEqual(result["project"]["groups"], ["project-group"])
49+
50+
self.assertEqual(result["packages"]["package1"]["users"], ["alice"])
51+
self.assertEqual(result["packages"]["package1"]["groups"], ["pkg1-group"])
52+
53+
self.assertEqual(result["packages"]["package2"]["users"], ["bob"])
54+
self.assertIsNone(result["packages"]["package2"]["groups"])
55+
56+
def test_v1_format_passthrough(self):
57+
"""v1.0 format is printed unchanged."""
58+
path = self._write_file(
59+
'{"header":{"document":"obs-maintainers","version":"1.0"},'
60+
'"project":{"users":["alice"],"groups":null},'
61+
'"packages":{"pkg1":{"users":["bob"],"groups":["team"]},'
62+
'"pkg2":{"users":null,"groups":["team"]}}}'
63+
)
64+
output = self._run_converter(path)
65+
result = json.loads(output)
66+
67+
self.assertEqual(result["header"]["document"], "obs-maintainers")
68+
self.assertEqual(result["header"]["version"], "1.0")
69+
self.assertEqual(result["project"]["users"], ["alice"])
70+
self.assertEqual(result["packages"]["pkg1"]["users"], ["bob"])
71+
self.assertEqual(result["packages"]["pkg1"]["groups"], ["team"])
72+
73+
def test_legacy_empty_project_maintainers(self):
74+
"""Legacy format with no project-level maintainers."""
75+
path = self._write_file(
76+
'{"package1":["alice"]}'
77+
)
78+
output = self._run_converter(path)
79+
result = json.loads(output)
80+
81+
self.assertEqual(result["header"]["document"], "obs-maintainers")
82+
self.assertIsNone(result["project"]["users"])
83+
self.assertIsNone(result["project"]["groups"])
84+
self.assertEqual(result["packages"]["package1"]["users"], ["alice"])
85+
86+
def test_legacy_groups_only(self):
87+
"""Legacy format with only groups, no users."""
88+
path = self._write_file(
89+
'{"":["@admins","@maintainers"],"pkg":["@team"]}'
90+
)
91+
output = self._run_converter(path)
92+
result = json.loads(output)
93+
94+
self.assertIsNone(result["project"]["users"])
95+
self.assertEqual(result["project"]["groups"], ["admins", "maintainers"])
96+
self.assertIsNone(result["packages"]["pkg"]["users"])
97+
self.assertEqual(result["packages"]["pkg"]["groups"], ["team"])
98+
99+
def test_file_not_found(self):
100+
"""Non-existent file raises an error."""
101+
path = os.path.join(self.tmpdir, "nonexistent.json")
102+
with self.assertRaises(FileNotFoundError):
103+
self._run_converter(path)
104+
105+
def test_invalid_json(self):
106+
"""Invalid JSON raises an error."""
107+
path = os.path.join(self.tmpdir, "_maintainership.json")
108+
with open(path, "w") as f:
109+
f.write("not valid json")
110+
with self.assertRaises(json.JSONDecodeError):
111+
self._run_converter(path)
112+
113+
114+
if __name__ == "__main__":
115+
unittest.main()

0 commit comments

Comments
 (0)