#!/usr/bin/python3
"""
Copy files from a source to the tree
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.
"""
import json
import os
import sys
import subprocess
import tempfile
import osbuild.sources
SCHEMA = """
"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(dir="/var/tmp") as _workdir:
r = main(stage_args["tree"],
stage_args["sources"],
stage_args["options"],
_workdir)
sys.exit(r)