Skip to content

Commit 67f1306

Browse files
committed
Add typer cli interface
In addition to docopt also support the typer commandline interface. This is relevant for kiwi versions that has moved to typer. OSInside/kiwi#2751
1 parent 28aff4b commit 67f1306

File tree

7 files changed

+413
-102
lines changed

7 files changed

+413
-102
lines changed

kiwi_stackbuild_plugin/cli.py

+171
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
# Copyright (c) 2025 SUSE LLC. All rights reserved.
2+
#
3+
# This file is part of kiwi-stackbuild.
4+
#
5+
# kiwi-stackbuild is free software: you can redistribute it and/or modify
6+
# it under the terms owf the GNU General Public License as published by
7+
# the Free Software Foundation, either version 3 of the License, or
8+
# (at your option) any later version.
9+
#
10+
# kiwi-stackbuild is distributed in the hope that it will be useful,
11+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
# GNU General Public License for more details.
14+
#
15+
# You should have received a copy of the GNU General Public License
16+
# along with kiwi-stackbuild. If not, see <http://www.gnu.org/licenses/>
17+
#
18+
import typer
19+
import itertools
20+
from pathlib import Path
21+
from typing import (
22+
Annotated, Optional, List, Union, no_type_check
23+
)
24+
25+
typers = {
26+
'stackbuild': typer.Typer(
27+
add_completion=False
28+
),
29+
'stash': typer.Typer(
30+
add_completion=False
31+
)
32+
}
33+
34+
system_stackbuild = typers['stackbuild']
35+
system_stash = typers['stash']
36+
37+
38+
@no_type_check
39+
@system_stackbuild.command(
40+
context_settings={
41+
'allow_extra_args': True,
42+
'ignore_unknown_options': True
43+
}
44+
)
45+
def kiwi(
46+
ctx: typer.Context
47+
):
48+
"""
49+
List of command parameters as supported by the kiwi-ng
50+
build or create command. The information given here is passed
51+
along to the kiwi-ng system build or the kiwi-ng system create
52+
command depending on the presence of the --description
53+
option.
54+
"""
55+
Cli = ctx.obj
56+
args = ctx.args
57+
for option in list(set(args)):
58+
if type(option) is not str or not option.startswith('-'):
59+
continue
60+
k: List[Union[str, List]] = [option]
61+
v = []
62+
indexes = [n for n, x in enumerate(args) if x == option]
63+
if len(indexes) > 1:
64+
for index in indexes:
65+
v.append(args[index + 1])
66+
for index in sorted(indexes, reverse=True):
67+
del args[index + 1]
68+
del args[index]
69+
k.append(v)
70+
args += k
71+
Cli.subcommand_args['stackbuild']['system_build_or_create'] = \
72+
dict(itertools.zip_longest(*[iter(args)] * 2))
73+
Cli.global_args['command'] = 'stackbuild'
74+
Cli.global_args['system'] = True
75+
Cli.cli_ok = True
76+
77+
78+
@system_stackbuild.callback(
79+
help='Build an image based on a given stash container root. '
80+
'If no KIWI --description parameter is provided, '
81+
'stackbuild rebuilds the image from the stash container. '
82+
'In this case, the given kiwi parameters are passed to the '
83+
'kiwi-ng system create command. If a KIWI description is '
84+
'provided, this description takes over precedence and a new '
85+
'image from this description based on the given stash container '
86+
'root will be built. In this case, the given kiwi parameters '
87+
'are passed to the kiwi-ng system build command.',
88+
invoke_without_command=False,
89+
subcommand_metavar='kiwi [OPTIONS]'
90+
)
91+
def stackbuild(
92+
ctx: typer.Context,
93+
stash: Annotated[
94+
List[str], typer.Option(
95+
help='<name> Name of the stash container. See system stash --list '
96+
'for available stashes. Multiple --stash options will be stacked '
97+
'together in the given order'
98+
)
99+
],
100+
target_dir: Annotated[
101+
Path, typer.Option(
102+
help='<directory> The target directory to store the '
103+
'system image file(s)'
104+
)
105+
],
106+
description: Annotated[
107+
Optional[Path], typer.Option(
108+
help='<directory> Path to KIWI image description'
109+
)
110+
] = None,
111+
from_registry: Annotated[
112+
Optional[str], typer.Option(
113+
help='<URI> Pull given stash container name from the '
114+
'provided registry URI'
115+
)
116+
] = None,
117+
):
118+
Cli = ctx.obj
119+
Cli.subcommand_args['stackbuild'] = {
120+
'--stash': stash,
121+
'--target-dir': target_dir,
122+
'--description': description,
123+
'--from-registry': from_registry,
124+
'help': False
125+
}
126+
127+
128+
@system_stash.callback(
129+
help='Create a container from the given root directory',
130+
invoke_without_command=True,
131+
subcommand_metavar=''
132+
)
133+
def stash(
134+
ctx: typer.Context,
135+
root: Annotated[
136+
Optional[Path], typer.Option(
137+
help='<directory> The path to the root directory, '
138+
'usually the result of a former system prepare or '
139+
'build call'
140+
)
141+
] = None,
142+
tag: Annotated[
143+
Optional[str], typer.Option(
144+
help='<name> The tag name for the container. '
145+
'By default set to: latest'
146+
)
147+
] = None,
148+
container_name: Annotated[
149+
Optional[str], typer.Option(
150+
help='<name> The name of the container. By default '
151+
'set to the image name of the stash'
152+
)
153+
] = None,
154+
stash_list: Annotated[
155+
Optional[bool], typer.Option(
156+
'--list',
157+
help='List the available stashes'
158+
)
159+
] = False
160+
):
161+
Cli = ctx.obj
162+
Cli.subcommand_args['stash'] = {
163+
'--root': root,
164+
'--tag': tag,
165+
'--container-name': container_name,
166+
'--list': stash_list,
167+
'help': False
168+
}
169+
Cli.global_args['command'] = 'stash'
170+
Cli.global_args['system'] = True
171+
Cli.cli_ok = True

kiwi_stackbuild_plugin/tasks/system_stackbuild.py

+43-33
Original file line numberDiff line numberDiff line change
@@ -182,23 +182,28 @@ def process(self) -> None:
182182
def _validate_kiwi_create_command(
183183
self, kiwi_create_command: List[str]
184184
) -> List[str]:
185-
# construct create command from given command line
186-
kiwi_create_command += self.command_args.get(
187-
'<kiwi_create_command_args>'
188-
)
189-
if '--' in kiwi_create_command:
190-
kiwi_create_command.remove('--')
191-
# validate create command through docopt from the original
192-
# kiwi.tasks.system_create docopt information
193-
log.debug(
194-
'Validating kiwi_create_command_args:{0} {1}'.format(
195-
os.linesep, kiwi_create_command
185+
if self.command_args.get('<kiwi_create_command_args>'):
186+
# construct create command from docopt command line
187+
kiwi_create_command += self.command_args.get(
188+
'<kiwi_create_command_args>'
196189
)
197-
)
198-
validated_create_command = docopt(
199-
kiwi.tasks.system_create.__doc__,
200-
argv=kiwi_create_command
201-
)
190+
if '--' in kiwi_create_command:
191+
kiwi_create_command.remove('--')
192+
# validate create command through docopt from the original
193+
# kiwi.tasks.system_create docopt information
194+
log.debug(
195+
'Validating kiwi_create_command_args:{0} {1}'.format(
196+
os.linesep, kiwi_create_command
197+
)
198+
)
199+
validated_create_command = docopt(
200+
kiwi.tasks.system_create.__doc__,
201+
argv=kiwi_create_command
202+
)
203+
else:
204+
validated_create_command = \
205+
self.command_args.get('system_build_or_create')
206+
202207
# rebuild kiwi create command from validated docopt parser result
203208
return self._rebuild_kiwi_command(
204209
validated_create_command, 'create'
@@ -207,24 +212,29 @@ def _validate_kiwi_create_command(
207212
def _validate_kiwi_build_command(
208213
self, kiwi_build_command: List[str]
209214
) -> List[str]:
210-
# construct build command from given command line
211-
kiwi_build_command += self.command_args.get(
212-
'<kiwi_build_command_args>'
213-
)
214-
if '--' in kiwi_build_command:
215-
kiwi_build_command.remove('--')
216-
# validate build command through docopt from the original
217-
# kiwi.tasks.system_build docopt information
218-
log.debug(
219-
'Validating kiwi_build_command_args:{0} {1}'.format(
220-
os.linesep, kiwi_build_command
215+
if self.command_args.get('<kiwi_build_command_args>'):
216+
# construct build command from given command line
217+
kiwi_build_command += self.command_args.get(
218+
'<kiwi_build_command_args>'
221219
)
222-
)
223-
validated_build_command = docopt(
224-
kiwi.tasks.system_build.__doc__,
225-
argv=kiwi_build_command
226-
)
227-
# rebuild kiwi build command from validated docopt parser result
220+
if '--' in kiwi_build_command:
221+
kiwi_build_command.remove('--')
222+
# validate build command through docopt from the original
223+
# kiwi.tasks.system_build docopt information
224+
log.debug(
225+
'Validating kiwi_build_command_args:{0} {1}'.format(
226+
os.linesep, kiwi_build_command
227+
)
228+
)
229+
validated_build_command = docopt(
230+
kiwi.tasks.system_build.__doc__,
231+
argv=kiwi_build_command
232+
)
233+
else:
234+
validated_build_command = \
235+
self.command_args.get('system_build_or_create')
236+
237+
# rebuild kiwi build command from validated parser result
228238
return self._rebuild_kiwi_command(
229239
validated_build_command, 'build'
230240
)

0 commit comments

Comments
 (0)