diff --git a/stages/org.osbuild.copy b/stages/org.osbuild.copy new file mode 100755 index 00000000..880ecd6a --- /dev/null +++ b/stages/org.osbuild.copy @@ -0,0 +1,138 @@ +#!/usr/bin/python3 + +import json +import os +import sys +import subprocess +import tempfile + +import osbuild.sources + + +STAGE_DESC = "Copy files from a source to the tree" +STAGE_INFO = """ +Copies files obtained via a `source` to the tree. Multiple files or +directories can be copied by specifying multiple entries in `paths`. +If no paths are specified the whole contents of `source` is copied. +The source and the target path for each individual path entry might +optionally be specified via `from` and `to`, respectively; if no +path is given for any of the two, the root `/` is assumed. + +Supported sources are currently: + + o `archive`: Fetch an archive via the org.osbuild.files source and + extract it to a temporary directory. Currently only tar archives + are supported. + +""" +STAGE_OPTS = """ +"additionalProperties": false, +"definitions": { + "source-archive": { + "description": "Fetch an via org.osbuild.files and extract it", + "type": "object", + "required": ["type", "checksum"], + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "description": "The type of the source, here 'archive'", + "enum": ["archive"] + }, + "checksum": { + "type": "string", + "description": "The checksum of the archive to fetch" + }, + "strip-components": { + "type": "number", + "description": "Strip 'N' leading components from file names on extraction", + "default": 1 + } + } + } +}, +"properties": { + "source": { + "oneOf": [{ "$ref": "#/definitions/source-archive" }] + }, + "paths": { + "description": "Array of items to copy", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "from": { + "type": "string", + "description": "The source path", + "default": "/" + }, + "to": { + "type": "string", + "description": "The destination path", + "default": "/" + } + } + } + } +} +""" + + +def get_archive(options, srcdir, workdir): + checksum = options["checksum"] + strip = options.get("strip-components", 1) + source_path = os.path.join(srcdir, "org.osbuild.files") + + target = os.path.join(workdir, checksum) + os.makedirs(target) + + # actually fetch the archive + res = osbuild.sources.get("org.osbuild.files", [checksum]) + + tar = [ + "tar", + "--strip-components=" + str(strip), + "-x", + "-C", target, + "-f", os.path.join(source_path, checksum) + ] + + subprocess.run(tar, check=True) + + return target, res + + +def main(tree, srcdir, options, workdir): + source_options = options["source"] + source_type = source_options["type"] + paths = options.get("paths", [{}]) + + if source_type == "archive": + source = get_archive + else: + raise ValueError(f"Unsupported source: {source_type}") + + # Fetch the requested data via the selected source + source_path, _ = source(source_options, srcdir, workdir) + + for path in paths: + src = path.get("from", "/") + dst = path.get("to", "/") + + subprocess.run(["cp", "--reflink=auto", "-a", + f"{source_path}{src}", + f"{tree}{dst}"], + check=True) + + +if __name__ == '__main__': + stage_args = json.load(sys.stdin) + + with tempfile.TemporaryDirectory() as _workdir: + r = main(stage_args["tree"], + stage_args["sources"], + stage_args["options"], + _workdir) + + sys.exit(r)