def main(): parser = argparse.ArgumentParser( description="Build operating system images") parser.add_argument( "pipeline_path", metavar="PIPELINE", help="json file containing the pipeline that should be built") parser.add_argument( "--build-pipeline", metavar="PIPELINE", type=os.path.abspath, help="json file containing the pipeline to create a build environment") parser.add_argument( "--store", metavar="DIRECTORY", type=os.path.abspath, default=".osbuild/store", help="the directory where intermediary os trees are stored") parser.add_argument( "-l", "--libdir", metavar="DIRECTORY", type=os.path.abspath, help= "the directory containing stages, assemblers, and the osbuild library") requiredNamed = parser.add_argument_group('required named arguments') requiredNamed.add_argument( "-o", "--output", dest="output_dir", metavar="DIRECTORY", type=os.path.abspath, help="provide the empty DIRECTORY as output argument to the last stage", required=True) args = parser.parse_args() with open(args.pipeline_path) as f: pipeline = osbuild.load(json.load(f)) if args.build_pipeline: with open(args.build_pipeline) as f: build = osbuild.load(json.load(f)) pipeline.prepend_build_pipeline(build) try: pipeline.run(args.output_dir, args.store, interactive=True, libdir=args.libdir) except KeyboardInterrupt: print() print(f"{RESET}{BOLD}{RED}Aborted{RESET}") sys.exit(130) except (osbuild.StageFailed, osbuild.AssemblerFailed) as error: print() print( f"{RESET}{BOLD}{RED}{error.name} failed with code {error.returncode}{RESET}" ) sys.exit(1)
def main(): parser = argparse.ArgumentParser(description="Build operating system images") parser.add_argument("pipeline_path", metavar="PIPELINE", help="json file containing the pipeline that should be built, or a '-' to read from stdin") parser.add_argument("--build-pipeline", metavar="PIPELINE", type=os.path.abspath, help="json file containing the pipeline to create a build environment") parser.add_argument("--store", metavar="DIRECTORY", type=os.path.abspath, default=".osbuild", help="the directory where intermediary os trees are stored") parser.add_argument("-l", "--libdir", metavar="DIRECTORY", type=os.path.abspath, help="the directory containing stages, assemblers, and the osbuild library") parser.add_argument("--json", action="store_true", help="output results in JSON format") args = parser.parse_args() if args.pipeline_path == "-": f = sys.stdin else: f = open(args.pipeline_path) pipeline = osbuild.load(json.load(f)) f.close() if args.build_pipeline: with open(args.build_pipeline) as f: build = osbuild.load(json.load(f)) pipeline.prepend_build_pipeline(build) try: pipeline.run(args.store, interactive=not args.json, libdir=args.libdir) except KeyboardInterrupt: print() print(f"{RESET}{BOLD}{RED}Aborted{RESET}") return 130 except (osbuild.StageFailed, osbuild.AssemblerFailed) as error: print() print(f"{RESET}{BOLD}{RED}{error.name} failed with code {error.returncode}{RESET}") if args.json: print(error.output) return 1 if args.json: json.dump({ "tree_id": pipeline.tree_id, "output_id": pipeline.output_id, }, sys.stdout) sys.stdout.write("\n") else: print("tree id:", pipeline.tree_id) print("output id:", pipeline.output_id) return 0
def test_canonical(self): """Degenerate case. Make sure we always return the same canonical description when passing empty or null values.""" cases = [ {}, {"assembler": None}, {"stages": []}, {"build": {}}, {"build": None} ] for pipeline in cases: with self.subTest(pipeline): self.assertEqual(osbuild.load(pipeline, {}).description(), {})
def treeid_from_manifest(manifest_data): """Calculate Tree ID This takes an in-memory manifest, inspects it, and returns the ID of the final tree of the stage-array. This returns `None` if no stages are defined. """ manifest_json = json.loads(manifest_data) manifest_pipeline = manifest_json.get("pipeline", {}) manifest_sources = manifest_json.get("sources", {}) manifest_parsed = osbuild.load(manifest_pipeline, manifest_sources) return manifest_parsed.tree_id
def run_osbuild(self, pipeline, input=None): osbuild_cmd = ["python3", "-m", "osbuild", "--json", "--store", self.store, "--libdir", ".", pipeline] build_env = os.getenv("OSBUILD_TEST_BUILD_ENV", None) if build_env: osbuild_cmd.append("--build-env") osbuild_cmd.append(build_env) # Create a checkpoint at the last stage, i.e. # commit the final tree to the store, so that # tests can use it to compare against if input: manifest = json.loads(input) else: with open(pipeline, "r") as f: manifest = json.load(f) parsed = osbuild.load(manifest.get("pipeline", {}), manifest.get("sources", {})) if parsed.tree_id: osbuild_cmd.append("--checkpoint") osbuild_cmd.append(parsed.tree_id) stdin = subprocess.PIPE if input else None p = subprocess.Popen(osbuild_cmd, encoding="utf-8", stdin=stdin, stdout=subprocess.PIPE) try: output, _ = p.communicate(input) if p.returncode != 0: print(output) self.assertEqual(p.returncode, 0) except KeyboardInterrupt: # explicitly wait again to let osbuild clean up p.wait() raise result = json.loads(output) return result.get("tree_id"), result.get("output_id")
def osbuild_cli(): args = parse_arguments(sys.argv) manifest = parse_manifest(args.manifest_path) # first thing after parsing is validation of the input index = osbuild.meta.Index(args.libdir) res = osbuild.meta.validate(manifest, index) if not res: if args.json or args.inspect: json.dump(res.as_dict(), sys.stdout) sys.stdout.write("\n") else: show_validation(res, args.manifest_path) return 2 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.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 if args.inspect: result = {"pipeline": pipeline.description(with_id=True)} if sources_options: result["sources"] = sources_options json.dump(result, sys.stdout) sys.stdout.write("\n") return 0 if not args.output_directory and not args.checkpoint: print("No output directory or checkpoints specified, exited without building.") return 0 monitor_name = "NullMonitor" if args.json else "LogMonitor" monitor = osbuild.monitor.make(monitor_name, sys.stdout.fileno()) try: r = pipeline.run( args.store, monitor, args.libdir, 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}") sys.exit(0 if r["success"] else 1)
def main(): parser = argparse.ArgumentParser(description="Build operating system images") parser.add_argument("pipeline_path", metavar="PIPELINE", help="json file containing the pipeline 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") args = parser.parse_args() if args.pipeline_path == "-": f = sys.stdin else: f = open(args.pipeline_path) manifest = json.load(f) f.close() if "pipeline" in manifest: pipeline = manifest["pipeline"] sources_options = manifest.get("sources", {}) else: # backwards compatibility pipeline = manifest sources_options = {} 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 ) 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