summaryrefslogtreecommitdiff
path: root/submodule-cache.py
blob: a9ed0bbf4c4e97e07840216b5902eaa08e7f1969 (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
#!/usr/bin/env python3

import subprocess
from os import path
import os
import warnings
import tempfile

def list_submodules():
    urls = {}
    for cfg_id, url in list_submodule_prop('url'):
        urls[cfg_id] = url.decode()

    out = {}
    for cfg_id, path in list_submodule_prop('path'):
        if cfg_id not in urls:
            warnings.warn('Submodule {cfg_id} has no URL configured! Skipping!')
        else:
            out[cfg_id] = (urls[cfg_id], path)

    leftover = urls.keys() - out.keys()
    for mod in leftover:
        warnings.warn('Submodule {mod} have no path configured! Skipping!')

    for cfg_id, _nocache in list_submodule_prop('noref'):
        del out[cfg_id]

    return out

def list_submodule_prop(prop):
    proc = subprocess.run('git config --file .gitmodules --get-regexp'.split() + [f'\.{prop}$'], check=True, capture_output=True)
    for line in proc.stdout.splitlines():
        key, value = line.split()
        #example key: submodule.fw/upstream/st-hal-f0.url
        _, cfg_name, _ = key.split(b'.')

        cfg_id = cfg_name.decode('utf-8')
        yield cfg_id, value

def get_submodule_prop(cfg_id, prop):
    try:
        proc = subprocess.run('git config --file .gitmodules --get'.split() + [f'submodule.{cfg_id}.{prop}'], check=True, capture_output=True)
        return proc.stdout.decode().strip()
    except subprocess.CalledProcessError as e:
        if e.returncode == 1:
            return None # key does not exist
        raise e

def cache_home():
    return os.environ.get('XDG_CACHE_HOME',
            path.join(os.environ['HOME'], '.cache'))

def get_global_cachedir(create=False, verbose=False):
    le_path = path.join(cache_home(), 'submodule-references')
    if create:
        if verbose:
            print(f'Cache dir {le_path} does not exist. Creating.')
        os.makedirs(le_path, exist_ok=True)
    return le_path

if __name__ == '__main__':
    import argparse
    parser = argparse.ArgumentParser()
    parser.add_argument('-f', '--force', action='store_true')
    parser.add_argument('-q', '--quiet', action='store_true')
    args = parser.parse_args()

    cachedir = get_global_cachedir(create=True)

    quiet_opts = ['--quiet'] if args.quiet else []
    force_opts = ['--force'] if args.force else []

    for cfg_id, (url, le_path) in list_submodules().items():
        short_id = cfg_id.split('/')[-1]
        refdir = path.join(cachedir, short_id)
        if not path.isdir(refdir):
            print(f'Submodule {short_id} is not cached. Cloning from {url}...')
            subprocess.run('git clone --bare'.split() + quiet_opts + [url, refdir], check=True)

        else:
            print(f'Updating submodule {short_id}...')

            #Sanity check
            proc = subprocess.run('git remote get-url origin'.split(), cwd=refdir, check=True, capture_output=True)
            cache_url = proc.stdout.decode().strip()
            if cache_url != url:
                raise SystemError(f'This repo configures submodule {short_id} with upstream URL {url}, but the cache uses upstream URL {cache_url}')

            subprocess.run('git fetch'.split() + force_opts, cwd=refdir, check=True)

        print(f'Checking out submodule {short_id}...')
        subprocess.run('git submodule update --init --reference'.split() + [refdir, le_path])