Skip to content

Commit 0019fcc

Browse files
psafontydirson
authored andcommitted
cli: change interface to use subcommands
This alineates usage of the tools with the trend from the last 5 years or so, hopefully making it easier to use. The CLI has been split into 3 actions: shell, run and build. They have independent arguments, and they are visible when using --help. It also requires users to specify the development environment or backend that they want to use to complete the action. Currently only container is supported. Examples of commands: - xcp-ng-dev container shell --rm --branch 8.3 - xcp-ng-dev container run --rm --branch 8.3 dune build @check - xcp-ng-dev container build --rm --branch 8.3 ./xapi Signed-off-by: Pau Ruiz Safont <pau.safont@vates.tech>
1 parent 3a94340 commit 0019fcc

File tree

3 files changed

+96
-47
lines changed

3 files changed

+96
-47
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ git clone https://github.com/xcp-ng-rpms/xapi.git
8686
# ... Here add your patches ...
8787

8888
# Build.
89-
xcp-ng-dev -b 8.2 --build-local xapi/ --rm
89+
xcp-ng-dev container build -b 8.2 --rm xapi/
9090
```
9191

9292
**Important switches**
@@ -106,7 +106,7 @@ fully automated.
106106
`%autopatch` in the `%prep` block; add `BuildRequires: quilt`
107107
2. let quilt apply them in a 8.3 buildenv (`quilt` in 8.3 is only in EPEL) and get you a shell:
108108
```sh
109-
xcp-ng-dev --rm -b 8.3 -l . --rpmbuild-stage=p -n --enablerepo=epel
109+
xcp-ng-dev container build --rm -b 8.3 --rpmbuild-stage=p -n --enablerepo=epel .
110110
```
111111
3. ask `quilt` to refresh all your patches (alternatively just the one you want)
112112
```sh
@@ -156,5 +156,5 @@ example, if I clone some repos into a directory on my host, say `/work/code/`,
156156
then I can mount it inside the container as follows:
157157

158158
```sh
159-
xcp-ng-dev -b 8.2 -v /work/code:/mnt/repos
159+
xcp-ng-dev container shell -b 8.2 -v /work/code:/mnt/repos
160160
```

src/xcp_ng_dev/cli.py

Lines changed: 91 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -37,38 +37,27 @@ def is_podman(runner):
3737
return True
3838
return False
3939

40-
def main():
41-
""" Main entry point. """
42-
parser = argparse.ArgumentParser()
40+
def add_common_args(parser):
4341
parser.add_argument('-b', '--branch',
4442
help='XCP-ng version: 7.6, %s, etc. If not set, '
4543
'will default to %s.' % (DEFAULT_BRANCH, DEFAULT_BRANCH))
46-
parser.add_argument('-l', '--build-local',
47-
help="Install dependencies for the spec file(s) found in the SPECS/ subdirectory "
48-
"of the directory passed as parameter, then build the RPM(s). "
49-
"Built RPMs and SRPMs will be in RPMS/ and SRPMS/ subdirectories. "
50-
"Any preexisting BUILD, BUILDROOT, RPMS or SRPMS directories will be removed first. "
51-
"If --output-dir is set, the RPMS and SRPMS directories will be copied to it "
52-
"after the build.")
53-
parser.add_argument('--define',
54-
help="Definitions to be passed to rpmbuild (if --build-local is "
55-
"passed too). Example: --define 'xcp_ng_section extras', for building the 'extras' "
56-
"version of a package which exists in both 'base' and 'extras' versions.")
57-
parser.add_argument('--rpmbuild-opts', action='append',
58-
help="Pass additional option(s) to rpmbuild")
59-
parser.add_argument('--rpmbuild-stage', action='store',
60-
help=f"Request given -bX stage rpmbuild, X in [{RPMBUILD_STAGES}]")
61-
parser.add_argument('-o', '--output-dir',
62-
help="Output directory for --build-local.")
6344
parser.add_argument('-n', '--no-exit', action='store_true',
64-
help='After executing either an automated build or a custom command passed as parameter, '
65-
'drop user into a shell')
45+
help='After finishing the execution of the action, drop user into a shell')
6646
parser.add_argument('-d', '--dir', action='append',
6747
help='Local dir to mount in the '
6848
'image. Will be mounted at /external/<dirname>')
6949
parser.add_argument('-e', '--env', action='append',
7050
help='Environment variables passed directly to '
7151
f'{RUNNER} -e')
52+
parser.add_argument('-a', '--enablerepo',
53+
help='additional repositories to enable before installing build dependencies. '
54+
'Same syntax as yum\'s --enablerepo parameter. Available additional repositories: '
55+
'check files/xcp-ng.repo.*.x.in.')
56+
parser.add_argument('--disablerepo',
57+
help='disable repositories. Same syntax as yum\'s --disablerepo parameter. '
58+
'If both --enablerepo and --disablerepo are set, --disablerepo will be applied first')
59+
60+
def add_container_args(parser):
7261
parser.add_argument('-v', '--volume', action='append',
7362
help=f'Volume mounts passed directly to {RUNNER} -v')
7463
parser.add_argument('--rm', action='store_true',
@@ -78,26 +67,78 @@ def main():
7867
parser.add_argument('--name', help='Assign a name to the container')
7968
parser.add_argument('--ulimit', action='append',
8069
help=f'Ulimit options passed directly to {RUNNER} run')
81-
parser.add_argument('-a', '--enablerepo',
82-
help='additional repositories to enable before installing build dependencies. '
83-
'Same syntax as yum\'s --enablerepo parameter. Available additional repositories: '
84-
'check files/xcp-ng.repo.*.x.in.')
85-
parser.add_argument('--disablerepo',
86-
help='disable repositories. Same syntax as yum\'s --disablerepo parameter. '
87-
'If both --enablerepo and --disablerepo are set, --disablerepo will be applied first')
8870
parser.add_argument('--platform', action='store',
8971
help="Override the default platform for the build container. "
9072
"Can notably be used to workaround podman bug #6185 fixed in v5.5.1.")
9173
parser.add_argument('--fail-on-error', action='store_true',
9274
help='If container initialisation fails, exit rather than dropping the user '
93-
'into a command shell')
75+
'into a shell')
9476
parser.add_argument('--debug', action='store_true',
9577
help='Enable script tracing in container initialization (sh -x)')
96-
parser.add_argument('command', nargs=argparse.REMAINDER,
97-
help='Command to run inside the prepared container')
9878

99-
args = parser.parse_args(sys.argv[1:])
10079

80+
def buildparser():
81+
parser = argparse.ArgumentParser()
82+
subparsers_env = parser.add_subparsers(
83+
required=True, title="Development environments",
84+
help="Available environments")
85+
86+
# container-based workflow
87+
parser_container = subparsers_env.add_parser('container', help="Use a local container to build a package")
88+
parser_container.set_defaults(func=container)
89+
subparsers_container = parser_container.add_subparsers(
90+
dest='action', required=True,
91+
help="Actions available for developing packages")
92+
93+
# build -- build an rpm using a container
94+
parser_build = subparsers_container.add_parser(
95+
'build',
96+
help="Install dependencies for the spec file(s) found in the SPECS/ subdirectory "
97+
"of the directory passed as parameter, then build the RPM(s). "
98+
"Built RPMs and SRPMs will be in RPMS/ and SRPMS/ subdirectories. "
99+
"Any preexisting BUILD, BUILDROOT, RPMS or SRPMS directories will be removed first.")
100+
parser_build.add_argument('build_local',
101+
help="Root path where SPECS/ and SOURCES are available")
102+
parser_build.add_argument(
103+
'--define',
104+
help="Definitions to be passed to rpmbuild. Example: --define "
105+
"'xcp_ng_section extras', for building the 'extras' "
106+
"version of a package which exists in both 'base' and 'extras' versions.")
107+
parser_build.add_argument(
108+
'-o', '--output-dir',
109+
help="Directory where the RPMs, SRPMs and the build logs will appear. "
110+
"The directory is created if it doesn't exist")
111+
parser_build.add_argument(
112+
'--rpmbuild-opts', action='append',
113+
help="Pass additional option(s) to rpmbuild")
114+
parser_build.add_argument(
115+
'--rpmbuild-stage', action='store',
116+
help=f"Request given -bX stage rpmbuild, X in [{RPMBUILD_STAGES}]")
117+
add_container_args(parser_build)
118+
add_common_args(parser_build)
119+
120+
# run -- execute commands inside a container
121+
parser_run = subparsers_container.add_parser(
122+
'run',
123+
help='Execute a command inside a container')
124+
parser_run.add_argument(
125+
'command', nargs=argparse.REMAINDER,
126+
help='Command to run inside the prepared container')
127+
add_container_args(parser_run)
128+
add_common_args(parser_run)
129+
130+
# shell -- like run bash
131+
parser_shell = argparse.ArgumentParser()
132+
parser_shell = subparsers_container.add_parser(
133+
'shell',
134+
help='Drop a shell into the prepared container')
135+
add_container_args(parser_shell)
136+
add_common_args(parser_shell)
137+
138+
return parser
139+
140+
def container(args):
141+
build = args.action == 'build'
101142
branch = args.branch or DEFAULT_BRANCH
102143
docker_arch = args.platform or ("linux/amd64/v2" if branch == "9.0" else "linux/amd64")
103144

@@ -110,24 +151,24 @@ def main():
110151
if args.rm:
111152
docker_args += ["--rm=true"]
112153

113-
if args.command != []:
154+
if hasattr(args, 'command') and args.command != []:
114155
docker_args += ["-e", "COMMAND=%s" % ' '.join(args.command)]
115-
if args.build_local:
156+
if build:
116157
docker_args += ["-v", "%s:/home/builder/rpmbuild" %
117158
os.path.abspath(args.build_local)]
118159
docker_args += ["-e", "BUILD_LOCAL=1"]
119-
if args.define:
160+
if hasattr(args, 'define') and args.define:
120161
docker_args += ["-e", "RPMBUILD_DEFINE=%s" % args.define]
121-
if args.rpmbuild_opts:
162+
if hasattr(args, 'rpmbuild_opts') and args.rpmbuild_opts:
122163
docker_args += ["-e", "RPMBUILD_OPTS=%s" % ' '.join(args.rpmbuild_opts)]
123-
if args.rpmbuild_stage:
164+
if hasattr(args, 'rpmbuild_stage') and args.rpmbuild_stage:
124165
if args.rpmbuild_stage not in RPMBUILD_STAGES:
125166
parser.error(f"--rpmbuild-stage={args.rpmbuild_stage} not in '{RPMBUILD_STAGES}'")
126167
docker_args += ["-e", f"RPMBUILD_STAGE={args.rpmbuild_stage}"]
127-
if args.output_dir:
168+
if hasattr(args, 'output_dir') and args.output_dir:
128169
if not os.path.isdir(args.output_dir):
129-
parser.error("%s is not a valid output directory." %
130-
args.output_dir)
170+
print(f"{args.output_dir} is not a valid output directory.")
171+
sys.exit(1)
131172
docker_args += ["-v", "%s:/home/builder/output" %
132173
os.path.abspath(args.output_dir)]
133174
if args.no_exit:
@@ -171,7 +212,15 @@ def main():
171212
docker_args += ["%s:%s" % (CONTAINER_PREFIX, branch),
172213
"/usr/local/bin/init-container.sh"]
173214
print("Launching docker with args %s" % docker_args, file=sys.stderr)
174-
return_code = subprocess.call(docker_args)
215+
return subprocess.call(docker_args)
216+
217+
def main():
218+
""" Main entry point. """
219+
parser = buildparser()
220+
221+
args = parser.parse_args()
222+
223+
return_code = args.func(args)
175224

176225
sys.exit(return_code)
177226

test/test.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ for REPO in ${REPOS}; do
1414
REPO_PATH=/tmp/"$REPO"
1515
git clone --branch "$TARGET_XCP_NG_VERSION" https://github.com/xcp-ng-rpms/"$REPO" "$REPO_PATH"
1616

17-
xcp-ng-dev --name "$CONTAINER_NAME" \
17+
xcp-ng-dev container build "$REPO_PATH" \
18+
--name "$CONTAINER_NAME" \
1819
--fail-on-error \
19-
-l "$REPO_PATH" \
2020
-b "$TARGET_XCP_NG_VERSION" \
2121
--rm
2222

0 commit comments

Comments
 (0)