-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathupdater.py
executable file
·133 lines (94 loc) · 3.38 KB
/
updater.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
#!/usr/bin/env python3
"""
Usage:
updater.py check <repo> <path>
updater.py update <repo> <path>
"""
import os
import sys
import tarfile
from distutils.version import StrictVersion
from pathlib import Path
from shutil import rmtree
import requests
from docopt import docopt
ASSET_ID_FILE = ".asset_id"
CURRENT_PATH = "current"
HISTORY_COUNT = 5
RELEASES_PATH = "releases"
RELEASE_PREFIX = "website-v"
RELEASE_URL = "https://api.github.com/repos/{repo}/releases"
def _release_filter(release):
return not release["draft"] and not release["prerelease"] and release["assets"]
def get_latest_release_asset(repo):
with requests.get(RELEASE_URL.format(repo=repo)) as response:
response.raise_for_status()
data = response.json()
release = next(filter(_release_filter, data), None)
if release is None:
raise ValueError("Unable to find matching release")
return release["assets"][0]
def check_for_update(args):
repo = args["<repo>"]
deploy_path = Path(args["<path>"]).resolve()
if not deploy_path.is_dir():
sys.exit("Website not deployed")
asset_id_file = deploy_path / CURRENT_PATH / ASSET_ID_FILE
if not asset_id_file.exists():
sys.exit("No assets currently deployed")
with open(str(asset_id_file)) as f:
current_id = f.read().strip().splitlines()[0]
if not current_id:
sys.exit("No assets currently deployed")
asset = get_latest_release_asset(repo)
if str(asset["id"]) != current_id:
sys.exit("Assets out of date")
sys.exit(0)
def do_update(args):
deploy_path = Path(args["<path>"]).resolve()
releases_dir = deploy_path / RELEASES_PATH
current_link = deploy_path / CURRENT_PATH
asset = get_latest_release_asset(args["<repo>"])
# Requires that the asset be '<name>.tar.gz'
release_name = asset["name"].rsplit(".", 2)[0]
release_dir = releases_dir / release_name
if not release_dir.exists():
release_dir.mkdir(parents=True)
with requests.get(
asset["url"], stream=True, headers={"Accept": "application/octet-stream",}
) as response:
with tarfile.open(fileobj=response.raw, mode="r|*") as tarball:
tarball.extractall(path=str(release_dir))
# Record the ID of this asset for later update checks
with open(str(release_dir / ASSET_ID_FILE), "w") as f:
print(asset["id"], file=f)
if current_link.is_symlink():
current_link.unlink()
current_link.symlink_to(release_dir, target_is_directory=True)
print("Deployed", release_name)
do_cleanup(releases_dir, release_name)
def do_cleanup(releases_dir, latest_release):
directories = os.listdir(releases_dir)
if len(directories) <= HISTORY_COUNT:
print("Did not run clean up (too few historic releases)")
return
def key(d):
return StrictVersion(d.replace(RELEASE_PREFIX, ""))
directories.sort(key=key, reverse=True)
to_remove = directories[HISTORY_COUNT:]
for directory in to_remove:
rmtree(releases_dir / directory)
if to_remove:
print(f"Cleaned up {len(to_remove)} historic releases")
else:
print("No clean up required")
def main():
args = docopt(__doc__)
if args["check"]:
check_for_update(args)
elif args["update"]:
do_update(args)
else:
raise ValueError("Invalid arguments from docopt")
if __name__ == "__main__":
main()