|
| 1 | +#!/usr/bin/python3 -u |
| 2 | + |
| 3 | +''' |
| 4 | + Implements versioning for RHEL CoreOS and CentOS Stream CoreOS. Initially based |
| 5 | + on the Fedora CoreOS versionary script. |
| 6 | +''' |
| 7 | + |
| 8 | +import argparse |
| 9 | +import json |
| 10 | +import os |
| 11 | +import re |
| 12 | +import subprocess |
| 13 | +import sys |
| 14 | +import time |
| 15 | +import yaml |
| 16 | + |
| 17 | +from datetime import datetime |
| 18 | + |
| 19 | + |
| 20 | +def main(): |
| 21 | + args = parse_args() |
| 22 | + if args.workdir is not None: |
| 23 | + os.chdir(args.workdir) |
| 24 | + assert os.path.isdir('builds'), 'Missing builds/ dir' |
| 25 | + |
| 26 | + manifest = get_flattened_manifest() |
| 27 | + |
| 28 | + # we'll want to move the source of truth for this somewhere else in the future but for now... |
| 29 | + prefix = manifest['automatic-version-prefix'] |
| 30 | + placeholder = '<date:%Y%m%d>' |
| 31 | + assert placeholder in prefix |
| 32 | + dt = datetime.now() |
| 33 | + xyz = prefix.replace(placeholder, dt.strftime('%Y%m%d')) |
| 34 | + x, y, z = map(int, xyz.split('.')) |
| 35 | + n = get_next_iteration(x, y, z, args.last_version) |
| 36 | + dev = '.dev' if args.dev else '' |
| 37 | + |
| 38 | + print(f'{xyz}-{n}{dev}') |
| 39 | + |
| 40 | + |
| 41 | +def parse_args(): |
| 42 | + parser = argparse.ArgumentParser() |
| 43 | + parser.add_argument('--workdir', help="path to cosa workdir") |
| 44 | + parser.add_argument('--last-version', help="override last version (for testing)") |
| 45 | + parser.add_argument('--dev', action='store_true', help="generate a developer version") |
| 46 | + return parser.parse_args() |
| 47 | + |
| 48 | + |
| 49 | +def get_next_iteration(x, y, z, last_version_override): |
| 50 | + try: |
| 51 | + with open('builds/builds.json') as f: |
| 52 | + builds = json.load(f) |
| 53 | + except FileNotFoundError: |
| 54 | + builds = {'builds': []} |
| 55 | + |
| 56 | + if last_version_override is not None: |
| 57 | + last_version = last_version_override |
| 58 | + else: |
| 59 | + if len(builds['builds']) == 0: |
| 60 | + eprint("n: 0 (no previous builds)") |
| 61 | + return 0 |
| 62 | + |
| 63 | + last_version = builds['builds'][0]['id'] |
| 64 | + |
| 65 | + last_version_tuple = parse_version(last_version) |
| 66 | + |
| 67 | + if not last_version_tuple: |
| 68 | + eprint(f"n: 0 (previous version {last_version} does not match scheme)") |
| 69 | + return 0 |
| 70 | + |
| 71 | + if (x, y, z) != last_version_tuple[:3]: |
| 72 | + eprint(f"n: 0 (previous version {last_version} x.y.z does not match)") |
| 73 | + return 0 |
| 74 | + |
| 75 | + n = last_version_tuple[3] + 1 |
| 76 | + eprint(f"n: {n} (incremented from previous version {last_version})") |
| 77 | + return n |
| 78 | + |
| 79 | + |
| 80 | +def get_flattened_manifest(): |
| 81 | + try: |
| 82 | + with open('src/config.json') as f: |
| 83 | + j = json.load(f) |
| 84 | + variant = j["coreos-assembler.config-variant"] |
| 85 | + manifest = f'src/config/manifest-{variant}.yaml' |
| 86 | + except FileNotFoundError: |
| 87 | + manifest = 'src/config/manifest.yaml' |
| 88 | + return yaml.safe_load( |
| 89 | + subprocess.check_output(['rpm-ostree', 'compose', 'tree', |
| 90 | + '--print-only', manifest])) |
| 91 | + |
| 92 | + |
| 93 | +def parse_version(version): |
| 94 | + # since RHCOS/SCOS follows semver, we could actually instead use a library |
| 95 | + # for this, but we do actually only expect a particular subset in our case |
| 96 | + m = re.match(r'^([0-9]+)\.([0-9]+)\.([0-9]{8})-([0-9]+)(\.dev)?$', version) |
| 97 | + if m is None: |
| 98 | + return None |
| 99 | + # sanity-check date |
| 100 | + try: |
| 101 | + time.strptime(m.group(3), '%Y%m%d') |
| 102 | + except ValueError: |
| 103 | + return None |
| 104 | + return tuple(map(int, m.groups()[:4])) |
| 105 | + |
| 106 | + |
| 107 | +def eprint(*args): |
| 108 | + print(*args, file=sys.stderr) |
| 109 | + |
| 110 | + |
| 111 | +if __name__ == "__main__": |
| 112 | + sys.exit(main()) |
0 commit comments