aboutsummaryrefslogtreecommitdiffstats
path: root/scripts/obs/lib/git.py
blob: 0194f770ceafac7e3b2d10c16e5d5e073e520d69 (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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0-or-later
# Copyright 2022 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
import os
import re
import lib.config


def get_repo_path(project):
    return f"{lib.config.path_cache}/{os.path.basename(project)}"


def get_repo_url(project):
    if project in lib.config.git_url_other:
        return lib.config.git_url_other[project]
    return f"{lib.config.git_url_default}/{project}"


def get_latest_tag_pattern(project):
    if project in lib.config.git_latest_tag_pattern_other:
        return lib.config.git_latest_tag_pattern_other[project]
    return lib.config.git_latest_tag_pattern_default


def clone(project):
    fetch = lib.args.git_fetch
    repo_path = get_repo_path(project)
    url = get_repo_url(project)

    if os.path.exists(repo_path):
        if fetch:
            print(f"{project}: 'git fetch'")
            lib.run_cmd(["git", "fetch"], cwd=repo_path)
        else:
            print(f"{project}: using cached {url} (not cloning, not fetching)")
        return

    print(f"{project}: cloning {url}")
    os.makedirs(lib.config.path_cache, exist_ok=True)
    lib.run_cmd(["git", "clone", url, repo_path])

    lib.run_cmd(["git", "config", "user.name", "Osmocom OBS scripts"],
                cwd=repo_path)
    lib.run_cmd(["git", "config", "user.email", "info@osmocom.org"],
                cwd=repo_path)


def clean(project):
    repo_path = get_repo_path(project)
    print(f"{project}: 'git clean -ffxd'")
    lib.run_cmd(["git", "clean", "-ffxd"], cwd=repo_path)


def checkout(project, branch):
    repo_path = get_repo_path(project)
    if not lib.args.git_checkout:
        ref = lib.run_cmd(["git", "log", "--pretty=oneline", "--abbrev-commit",
                           "-1"], cwd=repo_path).output.rstrip()
        print(f"{project}: skipping git checkout, current commit: {ref}")
        return
    print(f"{project}: 'git checkout -f {branch}'")
    lib.run_cmd(["git", "checkout", "-f", branch], cwd=repo_path)
    print(f"{project}: 'git reset --hard {branch}'")
    lib.run_cmd(["git", "reset", "--hard", branch], cwd=repo_path)
    print(f"{project}: 'git submodule update --init'")
    lib.run_cmd(["git", "submodule", "update", "--init"], cwd=repo_path)


def checkout_from_review(project, gerrit_id):
    """ checkout a given gerrit ID """
    repo_path = get_repo_path(project)
    lib.run_cmd(["git", "review", "-s"], cwd=repo_path)
    lib.run_cmd(["git", "review", "-d", str(gerrit_id)], cwd=repo_path)


def get_default_branch(project):
    if project in lib.config.git_branch_other:
       return lib.config.git_branch_other[project]
    return lib.config.git_branch_default


def checkout_default_branch(project):
    branch = get_default_branch(project)
    checkout(project, f"origin/{branch}")


def get_head(project):
    repo_path = get_repo_path(project)
    ret = lib.run_cmd(["git", "rev-parse", "HEAD"], cwd=repo_path)
    return ret.output.rstrip()


def get_head_remote(project, branch, branch_missing_ok=True):
    if not branch:
        branch = get_default_branch(project)
    repo_url = get_repo_url(project)

    print(f"{project}: getting head from git remote for {branch}")
    ls_remote = lib.run_cmd(["git", "ls-remote", repo_url, f"heads/{branch}"])

    ret = ls_remote.output.split("\t")[0]

    # If the branch is missing from the remote, git ls-remote exits with 0 and
    # the output is empty
    if not ret:
        if branch_missing_ok:
            print(f"{project}: branch not found: {branch}")
            return None
        lib.exit_error_cmd(ls_remote, "failed to find head commit for"
                           f" {project} in output")

    return ret


def get_latest_tag(project):
    pattern_str = get_latest_tag_pattern(project)
    pattern = re.compile(pattern_str)
    repo_path = get_repo_path(project)

    git_tag_ret = lib.run_cmd(["git", "tag", "-l", "--sort=-v:refname"],
                              cwd=repo_path)

    for line in git_tag_ret.output.split('\n'):
        line = line.strip('\r')
        if pattern.match(line):
            return line

    lib.exit_error_cmd(git_tag_ret, f"couldn't find latest tag for {project},"
                       f" regex used on output: {pattern_str}")


def get_latest_tag_remote(project):
    pattern_str = get_latest_tag_pattern(project)
    pattern = re.compile(pattern_str)

    print(f"{project}: getting latest tag from git remote")
    ls_remote = lib.run_cmd(["git", "ls-remote", "--tags", "--sort=-v:refname",
                             get_repo_url(project)])
    for line in ls_remote.output.split('\n'):
        # Tags are listed twice, skip the ones with ^{} at the end
        if "^{}" in line:
            continue

        if "refs/tags/" not in line:
            continue

        line = line.rstrip().split("refs/tags/")[1]
        if pattern.match(line):
            return line

    # No tag found probably means the repository was just created and doesn't
    # have a release tag yet
    return None


def checkout_latest_tag(project):
    checkout(project, get_latest_tag(project))