For fetching repositories for the build system I'm currently working on, the generic way of specifying a repository is the "git tree" repository, basically specified by "I want this git tree and in case you don't know how it looks, here is a command that, when executed, gets it somewhere below the working directory". Credentials provided, this works for essentially any version control system.
I noticed that people seem to wonder how one comes up and maintains such a repository description. The answer is simple: the file is generated, as it basically acts as a lock file anyway. Now, if we generate the repos.json anyway, it is enough to maintain what actually describes the tree to checkout,
{ "foo":
{ "cmd":
[ "cvs"
, "-d"
, ":ext:me@cvs.example.org:/var/cvs/fooproject"
, "export"
, "-D"
, "Oct 23, 2023 08:00 UTC"
, "foo"
]
, "subdir": "foo"
, "env": {"CVS_RSH": "ssh"}
}
, "bar":
{ "cmd":
[ "cvs"
, "-d"
, ":ext:cvs@cvs.example.org:/var/cvs/barproject"
, "export"
, "-r"
, "RELEASE_1_0"
, "bar-stable"
]
, "subdir": "bar-stable"
, "env": {"CVS_RSH": "ssh"}
}
}
#!/usr/bin/env python3
import json
import subprocess
import sys
import tempfile
def log(*args, **kwargs):
print(*args, file=sys.stderr, **kwargs)
def fail(s, exit_code=1):
log(f"Error: {s}")
sys.exit(exit_code)
def run_cmd(cmd,
*,
env=None,
stdout=subprocess.DEVNULL,
stdin=None,
cwd):
result = subprocess.run(cmd,
cwd=cwd,
env=env,
stdout=stdout,
stdin=stdin)
if result.returncode != 0:
fail("Command %s in %s failed" % (cmd, cwd))
return result.stdout
def get_root(desc):
cmd = desc["cmd"]
with tempfile.TemporaryDirectory() as d:
run_cmd(cmd, cwd=d)
run_cmd(["git", "init"], cwd=d)
run_cmd(["git", "add", "."], cwd=d)
run_cmd(["git", "commit", "-m", "%r" % (cmd,)], cwd=d)
tree = run_cmd(["git", "log", "-n", "1", "--format=%T"],
cwd=d, stdout=subprocess.PIPE).decode('utf-8').strip()
subdir = desc.get("subdir")
if subdir not in [None, "", "."]:
tree = subprocess.Popen(
["git", "cat-file", "--batch-check=%(objectname)"],
stdout=subprocess.PIPE,
stdin=subprocess.PIPE,
cwd=d).communicate(
input=("%s:%s" %
(tree, subdir)).encode())[0].decode('utf-8').strip()
root ={"type": "git tree",
"id": tree,
"cmd": cmd}
if "env" in desc:
root["env"] = desc["env"]
return {"repository": root }
def main():
config = json.load(sys.stdin)
with open(sys.argv[1]) as f:
tasks = json.load(f)
for name, desc in tasks.items():
root = get_root(desc)
config["repositories"][name] = root
print(json.dumps(config))
if __name__ == "__main__":
main()
... | "${ROOT}/bin/import-actions.py" "${ROOT}/etc/actions.template" | ...