osbuild: extract CLI to prepare for additional entrypoints
This extracts the CLI entrypoint into `main_cli.py` and prepares the codebase for the introduction of additional entrypoints. This should not contain any functional changes. The idea behind this is to add `main_api.py` (and maybe more in the future), which will be similar to `main_cli.py` but contain the `osbuild-api` entrypoint. This will make all entrypoints nicely symetric and the only difference will be `setup.py` selecting the right entrypoint for each executable, as well as `__main__.py` selecting the entrypoint for the module itself (which we will keep to the CLI for compatibility).
This commit is contained in:
parent
20cf5dba6a
commit
421414ef0b
4 changed files with 153 additions and 114 deletions
|
|
@ -1,3 +1,13 @@
|
|||
"""OSBuild Module
|
||||
|
||||
The `osbuild` module provides access to the internal features of OSBuild. It
|
||||
provides parsers for the input and output formats of osbuild, access to shared
|
||||
infrastructure of osbuild stages, as well as a pipeline executor.
|
||||
|
||||
The utility module `osbuild.util` provides access to common functionality
|
||||
independent of osbuild but used across the osbuild codebase.
|
||||
"""
|
||||
|
||||
|
||||
from .pipeline import Assembler, load, load_build, Pipeline, Stage
|
||||
|
||||
|
|
|
|||
|
|
@ -1,118 +1,12 @@
|
|||
import argparse
|
||||
import json
|
||||
import os
|
||||
"""OSBuild Main
|
||||
|
||||
This specifies the entrypoint of the osbuild module when run as executable. For
|
||||
compatibility we will continue to run the CLI.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import osbuild
|
||||
|
||||
|
||||
RESET = "\033[0m"
|
||||
BOLD = "\033[1m"
|
||||
RED = "\033[31m"
|
||||
|
||||
|
||||
def mark_checkpoints(pipeline, checkpoints):
|
||||
points = set(checkpoints)
|
||||
|
||||
def mark_stage(stage):
|
||||
c = stage.id
|
||||
if c in points:
|
||||
stage.checkpoint = True
|
||||
points.remove(c)
|
||||
|
||||
def mark_pipeline(pl):
|
||||
for stage in pl.stages:
|
||||
mark_stage(stage)
|
||||
if pl.build:
|
||||
mark_pipeline(pl.build)
|
||||
|
||||
mark_pipeline(pipeline)
|
||||
return points
|
||||
|
||||
|
||||
# pylint: disable=too-many-branches
|
||||
# pylint: disable=too-many-statements
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Build operating system images")
|
||||
parser.add_argument("manifest_path", metavar="MANIFEST",
|
||||
help="json file containing the manifest that should be built, or a '-' to read from stdin")
|
||||
parser.add_argument("--build-env", metavar="FILE", type=os.path.abspath,
|
||||
help="json file containing a description of the build environment")
|
||||
parser.add_argument("--store", metavar="DIRECTORY", type=os.path.abspath,
|
||||
default=".osbuild",
|
||||
help="directory where intermediary os trees are stored")
|
||||
parser.add_argument("--sources", metavar="FILE", type=os.path.abspath,
|
||||
help="json file containing a dictionary of source configuration")
|
||||
parser.add_argument("--secrets", metavar="FILE", type=os.path.abspath,
|
||||
help="json file containing a dictionary of secrets that are passed to sources")
|
||||
parser.add_argument("-l", "--libdir", metavar="DIRECTORY", type=os.path.abspath,
|
||||
help="the directory containing stages, assemblers, and the osbuild library")
|
||||
parser.add_argument("--checkpoint", metavar="ID", action="append", type=str, default=None,
|
||||
help="stage to commit to the object store during build (can be passed multiple times)")
|
||||
parser.add_argument("--json", action="store_true",
|
||||
help="output results in JSON format")
|
||||
parser.add_argument("--output-directory", metavar="DIRECTORY", type=os.path.abspath,
|
||||
help="directory where result objects are stored")
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.manifest_path == "-":
|
||||
f = sys.stdin
|
||||
else:
|
||||
f = open(args.manifest_path)
|
||||
manifest = json.load(f)
|
||||
f.close()
|
||||
|
||||
pipeline = manifest.get("pipeline", {})
|
||||
sources_options = manifest.get("sources", {})
|
||||
|
||||
if args.sources:
|
||||
with open(args.sources) as f:
|
||||
sources_options = json.load(f)
|
||||
|
||||
pipeline = osbuild.load(pipeline, sources_options)
|
||||
|
||||
if args.build_env:
|
||||
with open(args.build_env) as f:
|
||||
build_pipeline, runner = osbuild.load_build(json.load(f), sources_options)
|
||||
pipeline.prepend_build_env(build_pipeline, runner)
|
||||
|
||||
secrets = {}
|
||||
if args.secrets:
|
||||
with open(args.secrets) as f:
|
||||
secrets = json.load(f)
|
||||
|
||||
if args.checkpoint:
|
||||
missed = mark_checkpoints(pipeline, args.checkpoint)
|
||||
if missed:
|
||||
for checkpoint in missed:
|
||||
print(f"Checkpoint {BOLD}{checkpoint}{RESET} not found!")
|
||||
print(f"{RESET}{BOLD}{RED}Failed{RESET}")
|
||||
return 1
|
||||
|
||||
try:
|
||||
r = pipeline.run(
|
||||
args.store,
|
||||
interactive=not args.json,
|
||||
libdir=args.libdir,
|
||||
secrets=secrets,
|
||||
output_directory=args.output_directory
|
||||
)
|
||||
except KeyboardInterrupt:
|
||||
print()
|
||||
print(f"{RESET}{BOLD}{RED}Aborted{RESET}")
|
||||
return 130
|
||||
|
||||
if args.json:
|
||||
json.dump(r, sys.stdout)
|
||||
sys.stdout.write("\n")
|
||||
else:
|
||||
if r["success"]:
|
||||
print("tree id:", pipeline.tree_id)
|
||||
print("output id:", pipeline.output_id)
|
||||
else:
|
||||
print()
|
||||
print(f"{RESET}{BOLD}{RED}Failed{RESET}")
|
||||
|
||||
return 0 if r["success"] else 1
|
||||
from osbuild.main_cli import main_cli as main
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
|||
133
osbuild/main_cli.py
Normal file
133
osbuild/main_cli.py
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
"""Entrypoints for osbuild
|
||||
|
||||
This module contains the application and API entrypoints of `osbuild`, the
|
||||
command-line-interface to osbuild. The `osbuild_cli()` entrypoint can be safely
|
||||
used from tests to run the cli.
|
||||
"""
|
||||
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import osbuild
|
||||
|
||||
|
||||
RESET = "\033[0m"
|
||||
BOLD = "\033[1m"
|
||||
RED = "\033[31m"
|
||||
|
||||
|
||||
def mark_checkpoints(pipeline, checkpoints):
|
||||
points = set(checkpoints)
|
||||
|
||||
def mark_stage(stage):
|
||||
c = stage.id
|
||||
if c in points:
|
||||
stage.checkpoint = True
|
||||
points.remove(c)
|
||||
|
||||
def mark_pipeline(pl):
|
||||
for stage in pl.stages:
|
||||
mark_stage(stage)
|
||||
if pl.build:
|
||||
mark_pipeline(pl.build)
|
||||
|
||||
mark_pipeline(pipeline)
|
||||
return points
|
||||
|
||||
|
||||
# pylint: disable=too-many-branches
|
||||
# pylint: disable=too-many-statements
|
||||
def osbuild_cli(*, sys_argv=[]):
|
||||
parser = argparse.ArgumentParser(description="Build operating system images")
|
||||
parser.add_argument("manifest_path", metavar="MANIFEST",
|
||||
help="json file containing the manifest that should be built, or a '-' to read from stdin")
|
||||
parser.add_argument("--build-env", metavar="FILE", type=os.path.abspath,
|
||||
help="json file containing a description of the build environment")
|
||||
parser.add_argument("--store", metavar="DIRECTORY", type=os.path.abspath,
|
||||
default=".osbuild",
|
||||
help="directory where intermediary os trees are stored")
|
||||
parser.add_argument("--sources", metavar="FILE", type=os.path.abspath,
|
||||
help="json file containing a dictionary of source configuration")
|
||||
parser.add_argument("--secrets", metavar="FILE", type=os.path.abspath,
|
||||
help="json file containing a dictionary of secrets that are passed to sources")
|
||||
parser.add_argument("-l", "--libdir", metavar="DIRECTORY", type=os.path.abspath,
|
||||
help="the directory containing stages, assemblers, and the osbuild library")
|
||||
parser.add_argument("--checkpoint", metavar="ID", action="append", type=str, default=None,
|
||||
help="stage to commit to the object store during build (can be passed multiple times)")
|
||||
parser.add_argument("--json", action="store_true",
|
||||
help="output results in JSON format")
|
||||
parser.add_argument("--output-directory", metavar="DIRECTORY", type=os.path.abspath,
|
||||
help="directory where result objects are stored")
|
||||
args = parser.parse_args(sys_argv[1:])
|
||||
|
||||
if args.manifest_path == "-":
|
||||
f = sys.stdin
|
||||
else:
|
||||
f = open(args.manifest_path)
|
||||
manifest = json.load(f)
|
||||
f.close()
|
||||
|
||||
pipeline = manifest.get("pipeline", {})
|
||||
sources_options = manifest.get("sources", {})
|
||||
|
||||
if args.sources:
|
||||
with open(args.sources) as f:
|
||||
sources_options = json.load(f)
|
||||
|
||||
pipeline = osbuild.load(pipeline, sources_options)
|
||||
|
||||
if args.build_env:
|
||||
with open(args.build_env) as f:
|
||||
build_pipeline, runner = osbuild.load_build(json.load(f), sources_options)
|
||||
pipeline.prepend_build_env(build_pipeline, runner)
|
||||
|
||||
secrets = {}
|
||||
if args.secrets:
|
||||
with open(args.secrets) as f:
|
||||
secrets = json.load(f)
|
||||
|
||||
if args.checkpoint:
|
||||
missed = mark_checkpoints(pipeline, args.checkpoint)
|
||||
if missed:
|
||||
for checkpoint in missed:
|
||||
print(f"Checkpoint {BOLD}{checkpoint}{RESET} not found!")
|
||||
print(f"{RESET}{BOLD}{RED}Failed{RESET}")
|
||||
return 1
|
||||
|
||||
try:
|
||||
r = pipeline.run(
|
||||
args.store,
|
||||
interactive=not args.json,
|
||||
libdir=args.libdir,
|
||||
secrets=secrets,
|
||||
output_directory=args.output_directory
|
||||
)
|
||||
except KeyboardInterrupt:
|
||||
print()
|
||||
print(f"{RESET}{BOLD}{RED}Aborted{RESET}")
|
||||
return 130
|
||||
|
||||
if args.json:
|
||||
json.dump(r, sys.stdout)
|
||||
sys.stdout.write("\n")
|
||||
else:
|
||||
if r["success"]:
|
||||
print("tree id:", pipeline.tree_id)
|
||||
print("output id:", pipeline.output_id)
|
||||
else:
|
||||
print()
|
||||
print(f"{RESET}{BOLD}{RED}Failed{RESET}")
|
||||
|
||||
return 0 if r["success"] else 1
|
||||
|
||||
|
||||
def main_cli():
|
||||
"""osbuild-cli entrypoint
|
||||
|
||||
This is the entrypoint used by the `osbuild` executable. We simply fetch the
|
||||
global configuration and parameters necessary and invoke the API entrypoint.
|
||||
"""
|
||||
|
||||
sys.exit(osbuild_cli(sys_argv=sys.argv))
|
||||
4
setup.py
4
setup.py
|
|
@ -7,6 +7,8 @@ setuptools.setup(
|
|||
packages=["osbuild", "osbuild.util"],
|
||||
license='Apache-2.0',
|
||||
entry_points={
|
||||
"console_scripts": ["osbuild = osbuild.__main__:main"]
|
||||
"console_scripts": [
|
||||
"osbuild = osbuild.main_cli:main_cli"
|
||||
]
|
||||
},
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue