From 4ba68239fb96ef51b0c5c8113522b35b21f7e558 Mon Sep 17 00:00:00 2001 From: Jonathan Lebon Date: Fri, 16 Feb 2024 16:51:47 -0500 Subject: [PATCH] Move versionary script here When we emptied this script out of https://github.com/coreos/fedora-coreos-releng-automation/ in [[1]], I'm not sure why I decided to move it to cosa instead of this repo, where it clearly belongs much better. [1]: https://github.com/coreos/coreos-assembler/pull/3113 --- versionary | 173 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100755 versionary diff --git a/versionary b/versionary new file mode 100755 index 0000000000..09d49f735f --- /dev/null +++ b/versionary @@ -0,0 +1,173 @@ +#!/usr/bin/python3 -u + +# This file originally lived in +# https://github.com/coreos/fedora-coreos-releng-automation. See that repo for +# archeological git research. + +''' + Implements the Fedora CoreOS versioning scheme as per: + https://github.com/coreos/fedora-coreos-tracker/issues/81 + https://github.com/coreos/fedora-coreos-tracker/issues/211 +''' + +import argparse +import json +import os +import re +import subprocess +import sys +import time +import yaml + +from datetime import datetime + +# streams which don't use lockfiles +UNLOCKED_STREAMS = [ + 'rawhide', + 'branched', + 'bodhi-updates-testing', + 'bodhi-updates', +] + +# https://github.com/coreos/fedora-coreos-tracker/issues/211#issuecomment-543547587 +STREAM_TO_NUM = { + 'next': 1, + 'testing': 2, + 'stable': 3, + 'next-devel': 10, + 'testing-devel': 20, + 'rawhide': 91, + 'branched': 92, + 'bodhi-updates-testing': 93, + 'bodhi-updates': 94, +} + + +def main(): + args = parse_args() + if args.workdir is not None: + os.chdir(args.workdir) + assert os.path.isdir('builds'), 'Missing builds/ dir' + + manifest = get_flattened_manifest() + x, y, z = (get_x(manifest), get_y(manifest), get_z(manifest)) + n = get_next_iteration(x, y, z) + new_version = f'{x}.{y}.{z}.{n}' + + # sanity check the new version by trying to re-parse it + assert parse_version(new_version) is not None + print(new_version) + + +def parse_args(): + parser = argparse.ArgumentParser() + parser.add_argument('--workdir', help="path to cosa workdir") + return parser.parse_args() + + +def get_x(manifest): + """ + X is the Fedora release version on which we're based. + """ + releasever = manifest['releasever'] + eprint(f"x: {releasever} (from manifest)") + return int(releasever) + + +def get_y(manifest): + """ + Y is the base snapshot date in YYYYMMDD format of Fedora. We derive + this using the timestamp in the base lockfile. + """ + + stream = manifest['add-commit-metadata']['fedora-coreos.stream'] + + # XXX: should sanity check that the lockfiles for all the basearches have + # matching timestamps + exts = ['json', 'yaml'] + for ext in exts: + try: + with open(f"src/config/manifest-lock.x86_64.{ext}") as f: + lockfile = yaml.safe_load(f) + generated = lockfile.get('metadata', {}).get('generated') + if not generated: + raise Exception("Missing 'metadata.generated' key " + f"from {lockfile}") + dt = datetime.strptime(generated, '%Y-%m-%dT%H:%M:%SZ') + assert stream not in UNLOCKED_STREAMS + msg_src = "from lockfile" + break + except FileNotFoundError: + continue + else: + # must be an unlocked stream + assert stream in UNLOCKED_STREAMS + msg_src = "unlocked stream" + dt = datetime.now() + + ymd = dt.strftime('%Y%m%d') + eprint(f"y: {ymd} ({msg_src})") + return int(ymd) + + +def get_z(manifest): + """ + Z is the stream indicator. + """ + stream = manifest['add-commit-metadata']['fedora-coreos.stream'] + assert stream in STREAM_TO_NUM, f"Unknown stream: {stream}" + mapped = STREAM_TO_NUM[stream] + eprint(f"z: {mapped} (mapped from stream {stream})") + return mapped + + +def get_next_iteration(x, y, z): + try: + with open('builds/builds.json') as f: + builds = json.load(f) + except FileNotFoundError: + builds = {'builds': []} + + if len(builds['builds']) == 0: + eprint("n: 0 (no previous builds)") + return 0 + + last_buildid = builds['builds'][0]['id'] + last_version = parse_version(last_buildid) + if not last_version: + eprint(f"n: 0 (previous version {last_buildid} does not match scheme)") + return 0 + + if (x, y, z) != last_version[:3]: + eprint(f"n: 0 (previous version {last_buildid} x.y.z does not match)") + return 0 + + n = last_version[3] + 1 + eprint(f"n: {n} (incremented from previous version {last_buildid})") + return n + + +def get_flattened_manifest(): + return yaml.safe_load( + subprocess.check_output(['rpm-ostree', 'compose', 'tree', + '--print-only', 'src/config/manifest.yaml'])) + + +def parse_version(version): + m = re.match(r'^([0-9]{2})\.([0-9]{8})\.([0-9]+)\.([0-9]+)$', version) + if m is None: + return None + # sanity-check date + try: + time.strptime(m.group(2), '%Y%m%d') + except ValueError: + return None + return tuple(map(int, m.groups())) + + +def eprint(*args): + print(*args, file=sys.stderr) + + +if __name__ == "__main__": + sys.exit(main())