summaryrefslogtreecommitdiffstats
path: root/tools/fetch-distro.py
blob: 9fc5b1bfa62a377c1412b25d9101896ab91f3f6d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#!/usr/bin/env python3
# SPDX-License-Identifier: LGPL-2.1-or-later

"""
Check out pkg/{distribution}.
With -u, fetch commits, and if changed, commit the latest hash.
"""

import argparse
import json
import shlex
import subprocess
from pathlib import Path

def parse_args():
    p = argparse.ArgumentParser(
        description=__doc__,
    )
    p.add_argument(
        'distribution',
        nargs='+',
    )
    p.add_argument(
        '--no-fetch',
        dest='fetch',
        action='store_false',
        default=True,
    )
    p.add_argument(
        '--update', '-u',
        action='store_true',
        default=False,
    )
    return p.parse_args()

def read_config(distro: str):
    cmd = ['mkosi', '--json', '-d', distro, 'summary']
    print(f"+ {shlex.join(cmd)}")
    text = subprocess.check_output(cmd, text=True)

    data = json.loads(text)
    images = {image["Image"]: image for image in data["Images"]}
    return images["build"]

def commit_file(distro: str, file: Path, commit: str, changes: str):
    message = '\n'.join((
        f'mkosi: update {distro} commit reference',
        '',
        changes))

    cmd = ['git', 'commit', '-m', message, str(file)]
    print(f"+ {shlex.join(cmd)}")
    subprocess.check_call(cmd)

def checkout_distro(args, distro: str, config: dict):
    dest = Path(f'pkg/{distro}')
    if dest.exists():
        print(f'{dest} already exists.')
        return

    url = config['Environment']['GIT_URL']
    branch = config['Environment']['GIT_BRANCH']

    # Only debian uses source-git for now…
    reference = [f'--reference-if-able=.'] if distro == 'debian' else []

    cmd = [
        'git', 'clone', url,
        f'--branch={branch}',
        dest.as_posix(),
        *reference,
    ]
    print(f"+ {shlex.join(cmd)}")
    subprocess.check_call(cmd)

    args.fetch = False  # no need to fetch if we just cloned

def update_distro(args, distro: str, config: dict):
    branch = config['Environment']['GIT_BRANCH']
    old_commit = config['Environment']['GIT_COMMIT']

    cmd = ['git', '-C', f'pkg/{distro}', 'switch', branch]
    print(f"+ {shlex.join(cmd)}")
    subprocess.check_call(cmd)

    cmd = ['git', '-C', f'pkg/{distro}', 'fetch', 'origin', '-v',
           f'{branch}:remotes/origin/{branch}']
    print(f"+ {shlex.join(cmd)}")
    subprocess.check_call(cmd)

    cmd = ['git', '-C', f'pkg/{distro}', 'rev-parse', f'refs/remotes/origin/{branch}']
    print(f"+ {shlex.join(cmd)}")
    new_commit = subprocess.check_output(cmd, text=True).strip()

    if old_commit == new_commit:
        print(f'{distro}: commit {new_commit!s} is still fresh')
        return

    cmd = ['git', '-C', f'pkg/{distro}', 'log', '--graph',
           '--pretty=oneline', '--no-decorate', '--abbrev-commit', '--abbrev=10',
           f'{old_commit}..{new_commit}']
    print(f"+ {shlex.join(cmd)}")
    changes = subprocess.check_output(cmd, text=True).strip()

    conf_dir = Path('mkosi.images/build/mkosi.conf.d')
    files = conf_dir.glob('*/*.conf')
    for file in files:
        s = file.read_text()
        if old_commit in s:
            print(f'{distro}: {file}: found old hash, updating…')
            new = s.replace(old_commit, new_commit)
            assert new != s
            file.write_text(new)
            commit_file(distro, file, new_commit, changes)
            break
    else:
        raise ValueError(f'{distro}: hash {new_commit} not found under {conf_dir}')

if __name__ == '__main__':
    args = parse_args()

    for distro in args.distribution:
        config = read_config(distro)
        checkout_distro(args, distro, config)
        if args.update:
            update_distro(args, distro, config)