-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmr-version-differ.py
More file actions
executable file
·127 lines (97 loc) · 4.04 KB
/
mr-version-differ.py
File metadata and controls
executable file
·127 lines (97 loc) · 4.04 KB
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
#!/usr/bin/env python3
import requests
import argparse
import subprocess
from pick import pick
import datetime
class MrVersion:
def __init__(self, head_sha, created_at):
self.head_sha = head_sha
self.created_at = datetime.datetime.strptime(created_at, '%Y-%m-%dT%H:%M:%S.%fZ')
def __repr__(self):
return f'{self.created_at}, {self.head_sha}'
FETCH_REPO = '.fetch_repo'
def make_request(token, request):
response = requests.get(request, headers={'PRIVATE-TOKEN': token})
if not response.ok:
print(f'Request {request} returned error response: {response}')
return response
def get_api_url(address):
return f"{address}/api/v4"
def get_details_from_url(url):
#https://<gitlab-address>/<group>/<project_name>/-/merge_requests/<mr_id>
# 0 1 2 3 4 5 6 7
segment_idx_mr_string = 6
segment_idx_project = 4
segment_idx_mr_id = 7
min_len_segments = 8
url_segments = url.split('/')
# Sanity check
if len(url_segments) < min_len_segments \
or url_segments[segment_idx_mr_string] != 'merge_requests':
print(f'Invalid url: {url}')
exit(1)
# Extract gitlab address
address_end = url.find('/', len('https://'))
if (address_end == -1):
print(f"Corrupt url {url}")
exit(1)
address = url[:address_end]
return address, url_segments[segment_idx_project], url_segments[segment_idx_mr_id]
def get_mr_details(token, url):
address, project_name, mr_id = get_details_from_url(url)
# Find project by name
request = f'{get_api_url(address)}/search?scope=projects&search={project_name}'
response = make_request(token, request)
if not response.ok:
exit(1)
projects = response.json()
project = None
for p in projects:
if p.get('name') == project_name:
project = p
break
if not project:
print(f'Could not find project {project_name}.')
exit(1)
# Get versions of specified merge request
request = f"{get_api_url(address)}/projects/{project['id']}/merge_requests/{mr_id}/versions"
response = make_request(token, request)
if response.status_code == 404:
print(f'Could not find specified mr {mr_id}')
exit(1)
elif not response.ok:
exit(1)
mr_versions = [MrVersion(v['head_commit_sha'], v['created_at']) for v in response.json()]
return project, mr_versions
def generate_diff(project, ref_a, ref_b):
# Initialize a repo if not already exist and add remote
subprocess.call(['git', 'init', FETCH_REPO])
# Will generate error if already exists. Let it for now...
subprocess.call(['git', '-C', FETCH_REPO, 'remote', 'add',
project['name'], project['ssh_url_to_repo']])
# Need to fetch refs explicitly as they are not part of any ref group
subprocess.call(['git', '-C', FETCH_REPO, 'fetch',
project['name'], ref_a.head_sha, ref_b.head_sha])
# Do not store output as it will loose color information.
# Let it be outputted to stdout undisturbed
subprocess.call(['git', '-C', FETCH_REPO, 'range-diff',
f'{ref_b.head_sha}...{ref_a.head_sha}'])
def main():
parser = argparse.ArgumentParser(description='Range-diff GitLab merge requests.')
parser.add_argument('--token', required=True, help='Gitlab API token with read_api rights' \
' -- generate in profile menu.')
parser.add_argument('--url', required=True, help='Merge request url')
args = parser.parse_args()
project, mr_versions = get_mr_details(args.token, args.url)
if len(mr_versions) < 2:
print('Cannot diff MR with less than two versions...')
exit(1)
# Prompt user to select two refs
ref_a, _ = pick(mr_versions, 'Diff from (latest first):')
mr_versions.remove(ref_a)
ref_b, _ = pick(mr_versions, 'Diff to:')
# Generate diff. It will be visible in stdout
generate_diff(project, ref_a, ref_b)
if __name__ == '__main__':
main()