forked from YunoHost/apps
-
Notifications
You must be signed in to change notification settings - Fork 0
/
list_builder.py
executable file
·193 lines (165 loc) · 5.97 KB
/
list_builder.py
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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
#!/usr/bin/env python2
import re
import os
import sys
import time
import json
import zlib
import argparse
import requests
from dateutil.parser import parse
## Regular expression patterns
"""GitHub repository URL."""
re_github_repo = re.compile(
r'^(http[s]?|git)://github.com/(?P<owner>[\w\-_]+)/(?P<repo>[\w\-_]+)(.git)?'
)
re_commit_author = re.compile(
r'^author (?P<name>.+) <(?P<email>.+)> (?P<time>\d+) (?P<tz>[+-]\d+)$',
re.MULTILINE
)
## Helpers
def fail(msg, retcode=1):
"""Show failure message and exit."""
print("Error: {0:s}".format(msg))
sys.exit(retcode)
## Main
# Create argument parser
parser = argparse.ArgumentParser(description='Process YunoHost application list.')
# Add arguments and options
parser.add_argument("input", help="Path to json input file")
parser.add_argument("-o", "--output", help="Path to result file. If not specified, '-build' suffix will be added to input filename.")
parser.add_argument("-g", "--github", help="Github token <username>:<password>")
# Parse args
args = parser.parse_args()
try:
# Retrieve apps list from json file
with open(args.input) as f:
apps_list = json.load(f)
except IOError as e:
fail("%s file not found" % args.input)
# Get list name from filename
list_name = os.path.splitext(os.path.basename(args.input))[0]
print(":: Building %s list..." % list_name)
# Args default
if not args.output:
args.output = '%s-build.json' % list_name
# GitHub credentials
if args.github:
token = (args.github.split(':')[0], args.github.split(':')[1])
else:
token = None
# Loop through every apps
result_dict = {}
for app, info in apps_list.items():
print("Processing '%s'..." % app)
# Store usefull values
app_url = info['url']
app_rev = info['revision']
manifest = {}
timestamp = None
## Hosted on GitHub
github_repo = re_github_repo.match(app_url)
if github_repo:
owner = github_repo.group('owner')
repo = github_repo.group('repo')
raw_url = 'https://raw.githubusercontent.com/%s/%s/%s/manifest.json' % (
owner, repo, app_rev
)
try:
# Retrieve and load manifest
r = requests.get(raw_url, auth=token)
r.raise_for_status()
manifest = r.json()
except requests.exceptions.RequestException as e:
print("-> Error: unable to request %s, %s" % (raw_url, e))
continue
except ValueError as e:
print("-> Error: unable to decode manifest.json, %s" % e)
continue
api_url = 'https://api.github.com/repos/%s/%s/commits/%s' % (
owner, repo, app_rev
)
try:
# Retrieve last commit information
r = requests.get(api_url, auth=token)
r.raise_for_status()
info2 = r.json()
except requests.exceptions.RequestException as e:
print("-> Error: unable to request %s, %s" % (api_url, e))
continue
except ValueError as e:
print("-> Error: unable to decode API response, %s" % e)
continue
else:
commit_date = parse(info2['commit']['author']['date'])
timestamp = int(time.mktime(commit_date.timetuple()))
## Git repository with HTTP/HTTPS (Gogs, GitLab, ...)
elif app_url.startswith('http') and app_url.endswith('.git'):
raw_url = '%s/raw/%s/manifest.json' % (app_url[:-4], app_rev)
try:
# Attempt to retrieve and load raw manifest
r = requests.get(raw_url, verify=False, auth=token)
r.raise_for_status()
manifest = r.json()
except requests.exceptions.RequestException as e:
print("-> Error: unable to request %s, %s" % (raw_url, e))
continue
except ValueError as e:
print("-> Error: unable to decode manifest.json, %s" % e)
continue
obj_url = '%s/objects/%s/%s' % (
app_url, app_rev[0:2], app_rev[2:]
)
try:
# Retrieve last commit information
r = requests.get(obj_url, verify=False)
r.raise_for_status()
commit = zlib.decompress(r.content).decode('utf-8').split('\x00')[1]
except requests.exceptions.RequestException as e:
print("-> Error: unable to request %s, %s" % (obj_url, e))
continue
except zlib.error as e:
print("-> Error: unable to decompress commit object, %s" % e)
continue
else:
# Extract author line and commit date
commit_author = re_commit_author.search(commit)
if not commit_author:
print("-> Error: author line in commit not found")
continue
# Construct UTC timestamp
timestamp = int(commit_author.group('time'))
tz = commit_author.group('tz')
if len(tz) != 5:
print("-> Error: unexpected timezone length in commit")
continue
elif tz != '+0000':
tdelta = (int(tz[1:3]) * 3600) + (int(tz[3:5]) * 60)
if tz[0] == '+':
timestamp -= tdelta
elif tz[0] == '-':
timestamp += tdelta
else:
print("-> Error: unexpected timezone format in commit")
continue
else:
print("-> Error: unsupported VCS and/or protocol")
continue
try:
result_dict[manifest['id']] = {
'git': {
'branch': info['branch'],
'revision': app_rev,
'url': app_url
},
'lastUpdate': timestamp,
'manifest': manifest,
'state': info['state']
}
except KeyError as e:
print("-> Error: invalid app info or manifest, %s" % e)
continue
# Write resulting file
with open(args.output , 'w') as f:
f.write(json.dumps(result_dict, sort_keys=True))
print("\nDone! Written in %s" % args.output)