mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
- Move to pyproject.toml metadata - Update references to `setup.py` - Use pypa/build - Update workflows and tooling
184 lines
5.7 KiB
Python
Executable File
184 lines
5.7 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
import argparse
|
|
import os
|
|
import re
|
|
import sys
|
|
from contextlib import contextmanager
|
|
from datetime import datetime
|
|
|
|
script_dir = os.path.dirname(__file__)
|
|
package_dir = os.path.abspath(os.path.join(script_dir, '..'))
|
|
|
|
RELEASE_TYPE = {'a': 'alpha', 'b': 'beta'}
|
|
|
|
|
|
def stringify_version(version_info, in_develop=True):
|
|
version = '.'.join(str(v) for v in version_info[:3])
|
|
if not in_develop and version_info[3] != 'final':
|
|
version += version_info[3][0] + str(version_info[4])
|
|
|
|
return version
|
|
|
|
|
|
def bump_version(path, version_info, in_develop=True):
|
|
version = stringify_version(version_info, in_develop)
|
|
|
|
with open(path, 'r', encoding='utf-8') as f:
|
|
lines = f.read().splitlines()
|
|
|
|
for i, line in enumerate(lines):
|
|
if line.startswith('__version__ = '):
|
|
lines[i] = f"__version__ = '{version}'"
|
|
continue
|
|
if line.startswith('version_info = '):
|
|
lines[i] = f'version_info = {version_info}'
|
|
continue
|
|
if line.startswith('_in_development = '):
|
|
lines[i] = f'_in_development = {in_develop}'
|
|
continue
|
|
|
|
with open(path, 'w', encoding='utf-8') as f:
|
|
f.write('\n'.join(lines) + '\n')
|
|
|
|
|
|
def parse_version(version):
|
|
matched = re.search(r'^(\d+)\.(\d+)$', version)
|
|
if matched:
|
|
major, minor = matched.groups()
|
|
return (int(major), int(minor), 0, 'final', 0)
|
|
|
|
matched = re.search(r'^(\d+)\.(\d+)\.(\d+)$', version)
|
|
if matched:
|
|
major, minor, rev = matched.groups()
|
|
return (int(major), int(minor), int(rev), 'final', 0)
|
|
|
|
matched = re.search(r'^(\d+)\.(\d+)\s*(a|b|alpha|beta)(\d+)$', version)
|
|
if matched:
|
|
major, minor, typ, relver = matched.groups()
|
|
release = RELEASE_TYPE.get(typ, typ)
|
|
return (int(major), int(minor), 0, release, int(relver))
|
|
|
|
matched = re.search(r'^(\d+)\.(\d+)\.(\d+)\s*(a|b|alpha|beta)(\d+)$', version)
|
|
if matched:
|
|
major, minor, rev, typ, relver = matched.groups()
|
|
release = RELEASE_TYPE.get(typ, typ)
|
|
return (int(major), int(minor), int(rev), release, int(relver))
|
|
|
|
raise RuntimeError('Unknown version: %s' % version)
|
|
|
|
|
|
class Skip(Exception):
|
|
pass
|
|
|
|
|
|
@contextmanager
|
|
def processing(message):
|
|
try:
|
|
print(message + ' ... ', end='')
|
|
yield
|
|
except Skip as exc:
|
|
print('skip: %s' % exc)
|
|
except Exception:
|
|
print('error')
|
|
raise
|
|
else:
|
|
print('done')
|
|
|
|
|
|
class Changes:
|
|
def __init__(self, path):
|
|
self.path = path
|
|
self.fetch_version()
|
|
|
|
def fetch_version(self):
|
|
with open(self.path, encoding='utf-8') as f:
|
|
version = f.readline().strip()
|
|
matched = re.search(r'^Release (.*) \((.*)\)$', version)
|
|
if matched is None:
|
|
raise RuntimeError('Unknown CHANGES format: %s' % version)
|
|
|
|
self.version, self.release_date = matched.groups()
|
|
self.version_info = parse_version(self.version)
|
|
if self.release_date == 'in development':
|
|
self.in_development = True
|
|
else:
|
|
self.in_development = False
|
|
|
|
def finalize_release_date(self):
|
|
release_date = datetime.now().strftime('%b %d, %Y')
|
|
heading = 'Release %s (released %s)' % (self.version, release_date)
|
|
|
|
with open(self.path, 'r+', encoding='utf-8') as f:
|
|
f.readline() # skip first two lines
|
|
f.readline()
|
|
body = f.read()
|
|
|
|
f.seek(0)
|
|
f.truncate(0)
|
|
f.write(heading + '\n')
|
|
f.write('=' * len(heading) + '\n')
|
|
f.write(self.filter_empty_sections(body))
|
|
|
|
def add_release(self, version_info):
|
|
if version_info[-2:] in (('beta', 0), ('final', 0)):
|
|
version = stringify_version(version_info)
|
|
else:
|
|
reltype = version_info[3]
|
|
version = '%s %s%s' % (stringify_version(version_info),
|
|
RELEASE_TYPE.get(reltype, reltype),
|
|
version_info[4] or '')
|
|
heading = 'Release %s (in development)' % version
|
|
|
|
with open(os.path.join(script_dir, 'CHANGES_template'), encoding='utf-8') as f:
|
|
f.readline() # skip first two lines
|
|
f.readline()
|
|
tmpl = f.read()
|
|
|
|
with open(self.path, 'r+', encoding='utf-8') as f:
|
|
body = f.read()
|
|
|
|
f.seek(0)
|
|
f.truncate(0)
|
|
f.write(heading + '\n')
|
|
f.write('=' * len(heading) + '\n')
|
|
f.write(tmpl)
|
|
f.write('\n')
|
|
f.write(body)
|
|
|
|
def filter_empty_sections(self, body):
|
|
return re.sub('^\n.+\n-{3,}\n+(?=\n.+\n[-=]{3,}\n)', '', body, flags=re.M)
|
|
|
|
|
|
def parse_options(argv):
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument('version', help='A version number (cf. 1.6b0)')
|
|
parser.add_argument('--in-develop', action='store_true')
|
|
options = parser.parse_args(argv)
|
|
options.version = parse_version(options.version)
|
|
return options
|
|
|
|
|
|
def main():
|
|
options = parse_options(sys.argv[1:])
|
|
|
|
with processing("Rewriting sphinx/__init__.py"):
|
|
bump_version(os.path.join(package_dir, 'sphinx/__init__.py'),
|
|
options.version, options.in_develop)
|
|
|
|
with processing('Rewriting CHANGES'):
|
|
changes = Changes(os.path.join(package_dir, 'CHANGES'))
|
|
if changes.version_info == options.version:
|
|
if changes.in_development:
|
|
changes.finalize_release_date()
|
|
else:
|
|
raise Skip('version not changed')
|
|
else:
|
|
if changes.in_development:
|
|
print('WARNING: last version is not released yet: %s' % changes.version)
|
|
changes.add_release(options.version)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|