debian-forge/sources/org.osbuild.ostree
Christian Kellner fa9c288988 sources: source itself controls cache sub-dir
Instead of supplying the full cache dir, i.e. the directory in
the store where the source will place the fetched resources, to
the source, only supply the root folder of the cache and let
the source itself create the desired sub-directory. This allows
the source to determine what type of resource it provides. This
makes the final directory independent of the name of the source:
a `org.osbuild.curl` source can place file-like resource in the
`org.osbuild.files` sub-directory. Then the `org.osbuild.files`
input can be used to get those from the cache directory.
2021-02-12 19:27:08 +01:00

155 lines
4 KiB
Python
Executable file

#!/usr/bin/python3
"""Fetch OSTree commits from an repository
Uses ostree to pull specific commits from (remote) repositories
at the provided `url`. Can verify the commit, if one or more
gpg keys are provided via `gpgkeys`.
"""
import json
import os
import sys
import subprocess
import uuid
SCHEMA = """
"additionalProperties": false,
"definitions": {
"item": {
"description": "The commits to fetch indexed their checksum",
"type": "object",
"additionalProperties": false,
"patternProperties": {
"[0-9a-f]{5,64}": {
"type": "object",
"additionalProperties": false,
"required": ["remote"],
"properties": {
"remote": {
"type": "object",
"additionalProperties": false,
"required": ["url"],
"properties": {
"url": {
"type": "string",
"description": "URL of the repository."
},
"gpgkeys": {
"type": "array",
"items": {
"type": "string",
"description": "GPG keys to verify the commits"
}
}
}
}
}
}
}
}
},
"properties": {
"items": {"$ref": "#/definitions/item"},
"commits": {"$ref": "#/definitions/item"}
},
"oneOf": [{
"required": ["items"]
}, {
"required": ["commits"]
}]
"""
def ostree(*args, _input=None, **kwargs):
args = list(args) + [f'--{k}={v}' for k, v in kwargs.items()]
print("ostree " + " ".join(args), file=sys.stderr)
subprocess.run(["ostree"] + args,
encoding="utf-8",
stdout=sys.stderr,
input=_input,
check=True)
def download(commits, checksums, cache):
# Prepare the cache and the output repo
repo_cache = os.path.join(cache, "repo")
ostree("init", mode="archive", repo=repo_cache)
# Make sure the cache repository uses locks to protect the metadata during
# shared access. This is the default since `2018.5`, but lets document this
# explicitly here.
ostree("config", "set", "repo.locking", "true", repo=repo_cache)
for commit in checksums:
remote = commits[commit]["remote"]
url = remote["url"]
gpg = remote.get("gpgkeys", [])
uid = str(uuid.uuid4())
verify_args = []
if not gpg:
verify_args = ["--no-gpg-verify"]
ostree("remote", "add",
uid, url,
*verify_args,
repo=repo_cache)
for key in gpg:
ostree("remote", "gpg-import", "--stdin", uid,
repo=repo_cache, _input=key)
# Transfer the commit: remote → cache
print(f"pulling {commit}", file=sys.stderr)
ostree("pull", uid, commit, repo=repo_cache)
# Remove the temporary remotes again
ostree("remote", "delete", uid,
repo=repo_cache)
def export(checksums, cache, output):
repo_cache = os.path.join(cache, "repo")
repo_out = os.path.join(output, "repo")
ostree("init", mode="archive", repo=repo_out)
for commit in checksums:
# Transfer the commit: remote → cache
print(f"exporting {commit}", file=sys.stderr)
ostree("pull-local", repo_cache, commit,
repo=repo_out)
json.dump({}, sys.stdout)
def main(commits, options, checksums, cache, output):
cache = os.path.join(cache, "org.osbuild.ostree")
if not commits:
commits = options.get("commits", {})
os.makedirs(cache, exist_ok=True)
download(commits, checksums, cache)
if not output:
json.dump({}, sys.stdout)
return 0
os.makedirs(output, exist_ok=True)
export(checksums, cache, output)
return 0
if __name__ == '__main__':
source_args = json.load(sys.stdin)
r = main(source_args["items"],
source_args["options"],
source_args["checksums"],
source_args["cache"],
source_args["output"])
sys.exit(r)