Include fmt 11.0.2
[openal-soft.git] / fmt-11.0.2 / support / manage.py
blob3467afb0f678e9a829ef6064e12237a13e930f59
1 #!/usr/bin/env python3
3 """Manage site and releases.
5 Usage:
6 manage.py release [<branch>]
7 manage.py site
9 For the release command $FMT_TOKEN should contain a GitHub personal access token
10 obtained from https://github.com/settings/tokens.
11 """
13 from __future__ import print_function
14 import datetime, docopt, errno, fileinput, json, os
15 import re, requests, shutil, sys
16 from contextlib import contextmanager
17 from subprocess import check_call
20 class Git:
21 def __init__(self, dir):
22 self.dir = dir
24 def call(self, method, args, **kwargs):
25 return check_call(['git', method] + list(args), **kwargs)
27 def add(self, *args):
28 return self.call('add', args, cwd=self.dir)
30 def checkout(self, *args):
31 return self.call('checkout', args, cwd=self.dir)
33 def clean(self, *args):
34 return self.call('clean', args, cwd=self.dir)
36 def clone(self, *args):
37 return self.call('clone', list(args) + [self.dir])
39 def commit(self, *args):
40 return self.call('commit', args, cwd=self.dir)
42 def pull(self, *args):
43 return self.call('pull', args, cwd=self.dir)
45 def push(self, *args):
46 return self.call('push', args, cwd=self.dir)
48 def reset(self, *args):
49 return self.call('reset', args, cwd=self.dir)
51 def update(self, *args):
52 clone = not os.path.exists(self.dir)
53 if clone:
54 self.clone(*args)
55 return clone
58 def clean_checkout(repo, branch):
59 repo.clean('-f', '-d')
60 repo.reset('--hard')
61 repo.checkout(branch)
64 class Runner:
65 def __init__(self, cwd):
66 self.cwd = cwd
68 def __call__(self, *args, **kwargs):
69 kwargs['cwd'] = kwargs.get('cwd', self.cwd)
70 check_call(args, **kwargs)
73 def create_build_env():
74 """Create a build environment."""
75 class Env:
76 pass
77 env = Env()
78 env.fmt_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
79 env.build_dir = 'build'
80 env.fmt_repo = Git(os.path.join(env.build_dir, 'fmt'))
81 return env
84 fmt_repo_url = 'git@github.com:fmtlib/fmt'
87 def update_site(env):
88 env.fmt_repo.update(fmt_repo_url)
90 doc_repo = Git(os.path.join(env.build_dir, 'fmt.dev'))
91 doc_repo.update('git@github.com:fmtlib/fmt.dev')
93 version = '11.0.0'
94 clean_checkout(env.fmt_repo, version)
95 target_doc_dir = os.path.join(env.fmt_repo.dir, 'doc')
97 # Build the docs.
98 html_dir = os.path.join(env.build_dir, 'html')
99 if os.path.exists(html_dir):
100 shutil.rmtree(html_dir)
101 include_dir = env.fmt_repo.dir
102 import build
103 build.build_docs(version, doc_dir=target_doc_dir,
104 include_dir=include_dir, work_dir=env.build_dir)
105 shutil.rmtree(os.path.join(html_dir, '.doctrees'))
106 # Copy docs to the website.
107 version_doc_dir = os.path.join(doc_repo.dir, version)
108 try:
109 shutil.rmtree(version_doc_dir)
110 except OSError as e:
111 if e.errno != errno.ENOENT:
112 raise
113 shutil.move(html_dir, version_doc_dir)
116 def release(args):
117 env = create_build_env()
118 fmt_repo = env.fmt_repo
120 branch = args.get('<branch>')
121 if branch is None:
122 branch = 'master'
123 if not fmt_repo.update('-b', branch, fmt_repo_url):
124 clean_checkout(fmt_repo, branch)
126 # Update the date in the changelog and extract the version and the first
127 # section content.
128 changelog = 'ChangeLog.md'
129 changelog_path = os.path.join(fmt_repo.dir, changelog)
130 is_first_section = True
131 first_section = []
132 for i, line in enumerate(fileinput.input(changelog_path, inplace=True)):
133 if i == 0:
134 version = re.match(r'# (.*) - TBD', line).group(1)
135 line = '# {} - {}\n'.format(
136 version, datetime.date.today().isoformat())
137 elif not is_first_section:
138 pass
139 elif line.startswith('#'):
140 is_first_section = False
141 else:
142 first_section.append(line)
143 sys.stdout.write(line)
144 if first_section[0] == '\n':
145 first_section.pop(0)
147 ns_version = None
148 base_h_path = os.path.join(fmt_repo.dir, 'include', 'fmt', 'base.h')
149 for line in fileinput.input(base_h_path):
150 m = re.match(r'\s*inline namespace v(.*) .*', line)
151 if m:
152 ns_version = m.group(1)
153 break
154 major_version = version.split('.')[0]
155 if not ns_version or ns_version != major_version:
156 raise Exception(f'Version mismatch {ns_version} != {major_version}')
158 # Workaround GitHub-flavored Markdown treating newlines as <br>.
159 changes = ''
160 code_block = False
161 stripped = False
162 for line in first_section:
163 if re.match(r'^\s*```', line):
164 code_block = not code_block
165 changes += line
166 stripped = False
167 continue
168 if code_block:
169 changes += line
170 continue
171 if line == '\n' or re.match(r'^\s*\|.*', line):
172 if stripped:
173 changes += '\n'
174 stripped = False
175 changes += line
176 continue
177 if stripped:
178 line = ' ' + line.lstrip()
179 changes += line.rstrip()
180 stripped = True
182 fmt_repo.checkout('-B', 'release')
183 fmt_repo.add(changelog)
184 fmt_repo.commit('-m', 'Update version')
186 # Build the docs and package.
187 run = Runner(fmt_repo.dir)
188 run('cmake', '.')
189 run('make', 'doc', 'package_source')
191 # Create a release on GitHub.
192 fmt_repo.push('origin', 'release')
193 auth_headers = {'Authorization': 'token ' + os.getenv('FMT_TOKEN')}
194 r = requests.post('https://api.github.com/repos/fmtlib/fmt/releases',
195 headers=auth_headers,
196 data=json.dumps({'tag_name': version,
197 'target_commitish': 'release',
198 'body': changes, 'draft': True}))
199 if r.status_code != 201:
200 raise Exception('Failed to create a release ' + str(r))
201 id = r.json()['id']
202 uploads_url = 'https://uploads.github.com/repos/fmtlib/fmt/releases'
203 package = 'fmt-{}.zip'.format(version)
204 r = requests.post(
205 '{}/{}/assets?name={}'.format(uploads_url, id, package),
206 headers={'Content-Type': 'application/zip'} | auth_headers,
207 data=open('build/fmt/' + package, 'rb'))
208 if r.status_code != 201:
209 raise Exception('Failed to upload an asset ' + str(r))
211 update_site(env)
213 if __name__ == '__main__':
214 args = docopt.docopt(__doc__)
215 if args.get('release'):
216 release(args)
217 elif args.get('site'):
218 update_site(create_build_env())