Merge ppa-dev-tools:set-command-basic-options into ppa-dev-tools:main

Proposed by Bryce Harrington
Status: Merged
Merge reported by: Bryce Harrington
Merged at revision: 321446afc19cda9b835c1e2514605a97eb42e6f2
Proposed branch: ppa-dev-tools:set-command-basic-options
Merge into: ppa-dev-tools:main
Diff against target: 462 lines (+222/-52)
6 files modified
ppa/constants.py (+2/-1)
ppa/ppa_group.py (+9/-7)
scripts/ppa (+120/-5)
tests/helpers.py (+16/-0)
tests/test_ppa_group.py (+0/-18)
tests/test_scripts_ppa.py (+75/-21)
Reviewer Review Type Date Requested Status
Andreas Hasenack (community) Approve
Canonical Server Reporter Pending
Review via email: mp+434785@code.launchpad.net

Description of the change

True testing of all this stuff requires running against Launchpad, which
the unit tests don't do. Here's some example commandlines I used in my
own testing:

    # Basic creation
    $ ./scripts/ppa create test-set-command

    # Architectures
    $ ./scripts/ppa set test-set-command --architecture armhf,powerpc,s390x
    $ ./scripts/ppa set test-set-command --default-architectures
    $ ./scripts/ppa set test-set-command --all-architectures

After running each command, load up the PPA webpage in the browser and
verify the settings took. I didn't run into any problems with any of
these, and would expect them to Just Work(tm) for everyone, so if you
spot any issues let me know.

To post a comment you must log in.
Revision history for this message
Andreas Hasenack (ahasenack) :
Revision history for this message
Andreas Hasenack (ahasenack) :
Revision history for this message
Andreas Hasenack (ahasenack) :
Revision history for this message
Andreas Hasenack (ahasenack) :
review: Needs Information
Revision history for this message
Bryce Harrington (bryce) :
Revision history for this message
Bryce Harrington (bryce) wrote :

Fixed help text for --disable

Revision history for this message
Andreas Hasenack (ahasenack) wrote :

+1

review: Approve
Revision history for this message
Bryce Harrington (bryce) wrote :

Total 0 (delta 0), reused 0 (delta 0)
To git+ssh://git.launchpad.net/ppa-dev-tools
   01d9b53..321446a main -> main

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/ppa/constants.py b/ppa/constants.py
index f6e7b01..4270392 100644
--- a/ppa/constants.py
+++ b/ppa/constants.py
@@ -12,7 +12,8 @@
12"""Global constants"""12"""Global constants"""
1313
14ARCHES_ALL = ["amd64", "arm64", "armhf", "armel", "i386", "powerpc", "ppc64el", "s390x", "riscv64"]14ARCHES_ALL = ["amd64", "arm64", "armhf", "armel", "i386", "powerpc", "ppc64el", "s390x", "riscv64"]
15ARCHES_PPA = ["amd64", "arm64", "armhf", "i386", "powerpc", "ppc64el", "s390x"]15ARCHES_PPA_DEFAULT = ["amd64", "i386"]
16ARCHES_PPA_ALL = ["amd64", "arm64", "armhf", "i386", "powerpc", "ppc64el", "s390x"]
16ARCHES_PPA_EXTRA = ["riscv64"]17ARCHES_PPA_EXTRA = ["riscv64"]
17ARCHES_AUTOPKGTEST = ["amd64", "arm64", "armhf", "i386", "ppc64el", "s390x"]18ARCHES_AUTOPKGTEST = ["amd64", "arm64", "armhf", "i386", "ppc64el", "s390x"]
1819
diff --git a/ppa/ppa_group.py b/ppa/ppa_group.py
index 622cf76..7bccbb9 100755
--- a/ppa/ppa_group.py
+++ b/ppa/ppa_group.py
@@ -88,7 +88,7 @@ class PpaGroup:
88 """88 """
89 return self.service.people[self.name]89 return self.service.people[self.name]
9090
91 def create(self, ppa_name='ppa', ppa_description=None):91 def create(self, ppa_name='ppa', ppa_description=None, **kwargs):
92 """Registers a new PPA with Launchpad.92 """Registers a new PPA with Launchpad.
9393
94 If a description is not provided a default one will be generated.94 If a description is not provided a default one will be generated.
@@ -100,16 +100,18 @@ class PpaGroup:
100100
101 :raises PpaAlreadyExists: Raised if a PPA by this name already exists in Launchpad.101 :raises PpaAlreadyExists: Raised if a PPA by this name already exists in Launchpad.
102 """102 """
103 ppa_displayname = ''103 ppa_settings = {
104 if ppa_description is None:104 'description': ppa_description,
105 ppa_description = ppa_name105 'displayname': ppa_name,
106 ppa_displayname = ppa_name106 }
107 for k in kwargs.keys():
108 ppa_settings[k] = kwargs[k]
107109
108 try:110 try:
109 self.team.createPPA(111 self.team.createPPA(
110 name=ppa_name,112 name=ppa_name,
111 description=ppa_description,113 **ppa_settings
112 displayname=ppa_displayname)114 )
113 self.team.lp_save()115 self.team.lp_save()
114 except BadRequest as e:116 except BadRequest as e:
115 if "You already have a PPA" in o2str(e.content):117 if "You already have a PPA" in o2str(e.content):
diff --git a/scripts/ppa b/scripts/ppa
index a3bad54..fbf65f2 100755
--- a/scripts/ppa
+++ b/scripts/ppa
@@ -65,7 +65,8 @@ if '__file__' in globals():
6565
66from ppa._version import __version__66from ppa._version import __version__
67from ppa.constants import (67from ppa.constants import (
68 ARCHES_PPA,68 ARCHES_PPA_ALL,
69 ARCHES_PPA_DEFAULT,
69 ARCHES_AUTOPKGTEST,70 ARCHES_AUTOPKGTEST,
70 URL_AUTOPKGTEST,71 URL_AUTOPKGTEST,
71)72)
@@ -137,6 +138,69 @@ def add_global_options(parser) -> None:
137 help="Minimize output during processing")138 help="Minimize output during processing")
138139
139140
141def add_basic_config_options(parser) -> None:
142 """Adds to a parser the command line options to configure the PPA.
143
144 The config options are supported by the 'create' and 'set' command,
145 to allow configuring the PPA at creation time, or after, respectively.
146
147 These options represent what can be set as a user from the Launchpad
148 web interface.
149 """
150 # Architectures
151 parser.add_argument(
152 '-a', '--arches', '--arch', '--architectures',
153 dest="architectures",
154 action='store',
155 default=None,
156 help="Comma-separated list of hardware architectures to use"
157 )
158 parser.add_argument(
159 '--all-arches', '--all-architectures',
160 dest="architectures",
161 action='store_const',
162 const=','.join(ARCHES_PPA_ALL),
163 help="Enable all available architectures for the PPA"
164 )
165 parser.add_argument(
166 '--default-arches', '--default-architectures',
167 dest="architectures",
168 action='store_const',
169 const=','.join(ARCHES_PPA_DEFAULT),
170 help="Enable all available architectures for the PPA"
171 )
172
173 # Displayname
174 parser.add_argument(
175 '--displayname',
176 dest="displayname",
177 action='store',
178 help="A short title for the PPA's web page."
179 )
180
181 # Description
182 parser.add_argument(
183 '--description',
184 dest="description",
185 action='store',
186 help="A short description of the archive. URLs will be rendered as links. (See also 'ppa desc'.)"
187 )
188
189 # Enable/Disable uploading
190 parser.add_argument(
191 '--enable',
192 dest="set_enabled",
193 action='store_true',
194 help="Accept and build packages uploaded to the PPA."
195 )
196 parser.add_argument(
197 '--disable',
198 dest="set_disabled",
199 action='store_true',
200 help="Do not accept or build packages uploaded to the PPA."
201 )
202
203
140def create_arg_parser():204def create_arg_parser():
141 """Sets up the command line parser object.205 """Sets up the command line parser object.
142206
@@ -164,9 +228,7 @@ def create_arg_parser():
164 create_parser.add_argument('ppa_name', metavar='ppa-name',228 create_parser.add_argument('ppa_name', metavar='ppa-name',
165 action='store',229 action='store',
166 help="Name of the PPA to be created")230 help="Name of the PPA to be created")
167 create_parser.add_argument('-a', '--arches', '--arch', '--architectures',231 add_basic_config_options(create_parser)
168 dest="architectures", action='store',
169 help="Comma-separated list of hardware architectures to use")
170232
171 # Desc Command233 # Desc Command
172 desc_parser = subparser.add_parser(234 desc_parser = subparser.add_parser(
@@ -207,6 +269,19 @@ def create_arg_parser():
207 nargs='?', default='me',269 nargs='?', default='me',
208 help="Name of the PPA to list")270 help="Name of the PPA to list")
209271
272 # Set Command
273 set_parser = subparser.add_parser(
274 'set',
275 argument_default=argparse.SUPPRESS,
276 help='set help',
277 prog=progname,
278 )
279 add_global_options(set_parser)
280 set_parser.add_argument('ppa_name', metavar='ppa-name',
281 action='store',
282 help="Name of the PPA to be set config values on")
283 add_basic_config_options(set_parser)
284
210 # Show Command285 # Show Command
211 show_parser = subparser.add_parser(286 show_parser = subparser.add_parser(
212 'show',287 'show',
@@ -351,6 +426,13 @@ def create_config(lp, args):
351 else:426 else:
352 warn(f"Invalid releases '{args.releases}'")427 warn(f"Invalid releases '{args.releases}'")
353 return None428 return None
429 elif args.command == "set":
430 if args.architectures is not None:
431 if args.architectures:
432 config['architectures'] = args.architectures.split(',')
433 else:
434 warn(f"Invalid architectures '{args.architectures}'")
435 return None
354436
355 return config437 return config
356438
@@ -382,7 +464,7 @@ def command_create(lp, config):
382 warn("Could not determine team name")464 warn("Could not determine team name")
383 return os.EX_USAGE465 return os.EX_USAGE
384466
385 architectures = config.get('architectures', ARCHES_PPA)467 architectures = config.get('architectures', ARCHES_PPA_ALL)
386468
387 try:469 try:
388 if not config.get('dry_run', False):470 if not config.get('dry_run', False):
@@ -519,6 +601,38 @@ def command_exists(lp, config):
519 return 1601 return 1
520602
521603
604def command_set(lp, config):
605 """Sets one or more properties of PPA in Launchpad.
606
607 :param Lp lp: The Launchpad wrapper object.
608 :param dict config: Configuration param:value map.
609 :rtype: int
610 :returns: Status code OK (0) on success, non-zero on error.
611 """
612 try:
613 the_ppa = get_ppa(lp, config)
614
615 if 'architectures' in config:
616 the_ppa.set_architectures(config['architectures'])
617
618 if 'description' in config:
619 the_ppa.archive.description = config['description']
620
621 if 'displayname' in config:
622 the_ppa.archive.displayname = config['displayname']
623
624 return the_ppa.archive.lp_save()
625 except PpaDoesNotExist as e:
626 print(e)
627 except ValueError as e:
628 print(f"Error: {e}")
629 return os.EX_USAGE
630 except KeyboardInterrupt:
631 return 2
632 print("Unhandled error")
633 return 1
634
635
522def command_show(lp, config):636def command_show(lp, config):
523 """Displays details about the given PPA.637 """Displays details about the given PPA.
524638
@@ -752,6 +866,7 @@ COMMANDS = {
752 'desc': (command_desc, None),866 'desc': (command_desc, None),
753 'destroy': (command_destroy, None),867 'destroy': (command_destroy, None),
754 'list': (command_list, None),868 'list': (command_list, None),
869 'set': (command_set, None),
755 'show': (command_show, None),870 'show': (command_show, None),
756 'status': (command_status, None),871 'status': (command_status, None),
757 'tests': (command_tests, None),872 'tests': (command_tests, None),
diff --git a/tests/helpers.py b/tests/helpers.py
index 30a82bb..c6a55eb 100644
--- a/tests/helpers.py
+++ b/tests/helpers.py
@@ -18,6 +18,16 @@ from ppa.ppa import Ppa
18from ppa.ppa_group import PpaAlreadyExists18from ppa.ppa_group import PpaAlreadyExists
1919
2020
21class ArchiveMock:
22 """A stand-in for a Launchpad Archive object."""
23 def __init__(self, name, description):
24 self.displayname = name
25 self.description = description
26
27 def lp_save(self):
28 return True
29
30
21class PersonMock:31class PersonMock:
22 """A stand-in for a Launchpad Person object."""32 """A stand-in for a Launchpad Person object."""
23 def __init__(self, name):33 def __init__(self, name):
@@ -32,6 +42,12 @@ class PersonMock:
32 self._ppas.append(new_ppa)42 self._ppas.append(new_ppa)
33 return True43 return True
3444
45 def getPPAByName(self, name):
46 for ppa in self._ppas:
47 if ppa.name == name:
48 return ArchiveMock(name, ppa.description)
49 return None
50
35 def lp_save(self):51 def lp_save(self):
36 return True52 return True
3753
diff --git a/tests/test_ppa_group.py b/tests/test_ppa_group.py
index 9bfd382..d9d83de 100644
--- a/tests/test_ppa_group.py
+++ b/tests/test_ppa_group.py
@@ -69,24 +69,6 @@ def test_create_with_team():
69 assert ppa.address == 'ppa:test_team_name/ppa_test_name'69 assert ppa.address == 'ppa:test_team_name/ppa_test_name'
7070
7171
72def test_create_for_lpbug():
73 """Check associating a bug # when creating a PPA."""
74 ppa_group = PpaGroup(service=LpServiceMock(), name='me')
75 lpbug = '1234567'
76 ppa = ppa_group.create('lp' + lpbug)
77 assert ppa is not None
78 assert lpbug in ppa.description
79
80
81def test_create_for_merge_proposal():
82 """Check associating a merge proposal when creating a PPA."""
83 ppa_group = PpaGroup(service=LpServiceMock(), name='me')
84 version = '1.2.3-4'
85 ppa = ppa_group.create('merge.' + version)
86 assert ppa is not None
87 assert version in ppa.description
88
89
90def test_list_ppas():72def test_list_ppas():
91 """Check listing the PPAs for a PPA group."""73 """Check listing the PPAs for a PPA group."""
92 test_ppa_list = ['a', 'b', 'c', 'd']74 test_ppa_list = ['a', 'b', 'c', 'd']
diff --git a/tests/test_scripts_ppa.py b/tests/test_scripts_ppa.py
index e393bd8..10acc30 100644
--- a/tests/test_scripts_ppa.py
+++ b/tests/test_scripts_ppa.py
@@ -11,6 +11,7 @@
11"""ppa command-line script tests"""11"""ppa command-line script tests"""
1212
13import os13import os
14import io
14import sys15import sys
15import types16import types
1617
@@ -22,6 +23,12 @@ SCRIPT_NAME = "ppa"
22BASE_PATH = os.path.realpath(os.path.join(os.path.dirname(os.path.realpath(__file__)), ".."))23BASE_PATH = os.path.realpath(os.path.join(os.path.dirname(os.path.realpath(__file__)), ".."))
23sys.path.insert(0, BASE_PATH)24sys.path.insert(0, BASE_PATH)
2425
26from ppa.constants import (
27 ARCHES_PPA_ALL,
28 ARCHES_PPA_DEFAULT
29)
30from tests.helpers import LpServiceMock
31
25if '.pybuild' in BASE_PATH:32if '.pybuild' in BASE_PATH:
26 python_version = '.'.join([str(v) for v in sys.version_info[0:2]])33 python_version = '.'.join([str(v) for v in sys.version_info[0:2]])
27 scripts_path = os.path.join(34 scripts_path = os.path.join(
@@ -41,7 +48,7 @@ loader.exec_module(script)
41def fake_config():48def fake_config():
42 return {49 return {
43 'ppa_name': 'testing',50 'ppa_name': 'testing',
44 'team_name': 'tester',51 'team_name': 'me',
45 'wait_seconds': 0.152 'wait_seconds': 0.1
46 }53 }
4754
@@ -145,6 +152,7 @@ def test_create_arg_parser():
145 'desc',152 'desc',
146 'destroy',153 'destroy',
147 'list',154 'list',
155 'set',
148 'show',156 'show',
149 'status',157 'status',
150 'tests',158 'tests',
@@ -152,36 +160,80 @@ def test_create_arg_parser():
152 ]160 ]
153161
154162
155def test_create_arg_parser_create():163@pytest.mark.parametrize('command', ['create', 'set'])
156 """Checks argument parsing for the 'create' command."""164def test_create_arg_parser_basic_config(command):
165 """Checks argument parsing for the basic PPA config options.
166
167 This test covers the set of options used by 'create' and 'set' for
168 configuring various properties of the PPA's behaviors, such as
169 dependencies, publication policy, access control, etc. It does not
170 cover settings that require Launchpad administrator involvement.
171
172 The testing checks only that the options are being received and
173 registered as expected, and does not cover the processing of the
174 inputs nor the actual underlying functionality.
175 """
157 parser = script.create_arg_parser()176 parser = script.create_arg_parser()
158177
159 # Check ppa_name178 # Check command and ppa_name
160 args = parser.parse_args(['create', 'test-ppa'])179 args = parser.parse_args([command, 'test-ppa'])
180 assert args.command == command
161 assert args.ppa_name == 'test-ppa'181 assert args.ppa_name == 'test-ppa'
162182
163 # Check that command args can come before or after the ppa name183 # Check that command args can come before or after the ppa name
164 args = parser.parse_args(['create', 'test-ppa', '-a', 'x'])184 args = parser.parse_args([command, 'test-ppa', '-a', 'x'])
165 assert args.architectures == 'x'185 assert args.architectures == 'x'
166 args.architectures = None186 args.architectures = None
167 args = parser.parse_args(['create', '-a', 'x', 'test-ppa'])187 args = parser.parse_args([command, '-a', 'x', 'test-ppa'])
168 assert args.architectures == 'x'188 assert args.architectures == 'x'
169 args.architectures = None189 args.architectures = None
170190
191 # Check --all-arches and --default-arches
192 args = parser.parse_args([command, 'test-ppa', '--all-arches'])
193 assert args.architectures == ','.join(ARCHES_PPA_ALL)
194 args.architectures = None
195 args = parser.parse_args([command, 'test-ppa', '--all-architectures'])
196 assert args.architectures == ','.join(ARCHES_PPA_ALL)
197 args.architectures = None
198 args = parser.parse_args([command, 'test-ppa', '--default-arches'])
199 assert args.architectures == ','.join(ARCHES_PPA_DEFAULT)
200 args.architectures = None
201 args = parser.parse_args([command, 'test-ppa', '--default-architectures'])
202 assert args.architectures == ','.join(ARCHES_PPA_DEFAULT)
203 args.architectures = None
204
171 # Check -a, --arch, --arches, --architectures205 # Check -a, --arch, --arches, --architectures
172 args = parser.parse_args(['create', 'test-ppa', '-a', 'x'])206 args = parser.parse_args([command, 'test-ppa', '-a', 'x'])
173 assert args.architectures == 'x'207 assert args.architectures == 'x'
174 args.architectures = None208 args.architectures = None
175 args = parser.parse_args(['create', 'test-ppa', '--arch', 'x'])209 args = parser.parse_args([command, 'test-ppa', '--arch', 'x'])
176 assert args.architectures == 'x'210 assert args.architectures == 'x'
177 args.architectures = None211 args.architectures = None
178 args = parser.parse_args(['create', 'test-ppa', '--arches', 'x'])212 args = parser.parse_args([command, 'test-ppa', '--arches', 'x'])
179 assert args.architectures == 'x'213 assert args.architectures == 'x'
180 args.architectures = None214 args.architectures = None
181 args = parser.parse_args(['create', 'test-ppa', '--architectures', 'a,b,c'])215 args = parser.parse_args([command, 'test-ppa', '--architectures', 'a,b,c'])
182 assert args.architectures == 'a,b,c'216 assert args.architectures == 'a,b,c'
183 args.architectures = None217 args.architectures = None
184218
219 # Check --displayname
220 args = parser.parse_args([command, 'test-ppa', '--displayname', 'x'])
221 assert args.displayname == 'x'
222 args.displayname = None
223
224 # Check --description
225 args = parser.parse_args([command, 'test-ppa', '--description', 'x'])
226 assert args.description == 'x'
227 args.description = None
228
229 # Check --enable / -D|--disable
230 args = parser.parse_args([command, 'test-ppa', '--enable'])
231 assert args.set_enabled is True
232 args.set_enabled = False
233 args = parser.parse_args([command, 'test-ppa', '--disable'])
234 assert args.set_disabled is True
235 args.set_disabled = False
236
185237
186def test_create_arg_parser_show():238def test_create_arg_parser_show():
187 """Checks argument parsing for the 'show' command."""239 """Checks argument parsing for the 'show' command."""
@@ -312,16 +364,18 @@ def test_create_config():
312 pass364 pass
313365
314366
315@pytest.mark.xfail(reason="Unimplemented")367def test_command_create(fake_config, monkeypatch):
316def test_command_create(fake_config):368 lp = LpServiceMock()
317 assert script.command_create(fake_config) == 0369 monkeypatch.setattr("sys.stdin", io.StringIO('test description'))
318370 assert script.command_create(lp, fake_config) == 0
319 # TODO: Specify a ppa_name and verify it gets used properly371
320 # TODO: Specify a team name372 the_ppa = lp.me.getPPAByName(fake_config['ppa_name'])
321 # TODO: Verify if no team name specified, the lp_user is used373
322 # instead374 # Check basic attributes (further coverage is in test_ppa_group.py)
323 # TODO: Verify the ppa_address is set as expected375 assert the_ppa.displayname == fake_config['ppa_name']
324 pass376 assert the_ppa.description == 'test description'
377
378 # TODO: Check processors
325379
326380
327@pytest.mark.xfail(reason="Unimplemented")381@pytest.mark.xfail(reason="Unimplemented")

Subscribers

People subscribed via source and target branches

to all changes: