aboutsummaryrefslogtreecommitdiffstats
path: root/release.py
diff options
context:
space:
mode:
Diffstat (limited to 'release.py')
-rw-r--r--release.py303
1 files changed, 45 insertions, 258 deletions
diff --git a/release.py b/release.py
index 175e1463..bad7e0ee 100644
--- a/release.py
+++ b/release.py
@@ -1,274 +1,61 @@
-#!/usr/bin/env python
-
"""
-Script to fully automate the release process. Requires Python 2.6+
-with sphinx installed and the 'hg' command on the path.
+Setuptools is released using 'jaraco.packaging.release'. To make a release,
+install jaraco.packaging and run 'python -m jaraco.packaging.release'
"""
-from __future__ import print_function
-
-import subprocess
-import shutil
-import os
-import sys
-import getpass
-import collections
-import itertools
import re
+import os
+import subprocess
-try:
- import urllib.request as urllib_request
-except ImportError:
- import urllib2 as urllib_request
-
-try:
- input = raw_input
-except NameError:
- pass
-
-try:
- import keyring
-except Exception:
- pass
-
-VERSION = '0.8'
-PACKAGE_INDEX = 'https://pypi.python.org/pypi'
-
-def set_versions():
- global VERSION
- version = input("Release as version [%s]> " % VERSION) or VERSION
- if version != VERSION:
- VERSION = bump_versions(version)
-
-def infer_next_version(version):
- """
- Infer a next version from the current version by incrementing the last
- number or appending a number.
-
- >>> infer_next_version('1.0')
- '1.1'
-
- >>> infer_next_version('1.0b')
- '1.0b1'
-
- >>> infer_next_version('1.0.9')
- '1.0.10'
-
- >>> infer_next_version('1')
- '2'
- >>> infer_next_version('')
- '1'
- """
- def incr(match):
- ver = int(match.group(0) or '0')
- return str(ver + 1)
- return re.sub('\d*$', incr, version)
+def before_upload():
+ _linkify('CHANGES.txt', 'CHANGES (links).txt')
+ _add_bootstrap_bookmark()
files_with_versions = (
- 'docs/conf.py', 'setup.py', 'release.py', 'ez_setup.py', 'README.txt',
- 'setuptools/__init__.py',
+ 'ez_setup.py', 'setuptools/__init__.py',
)
-def get_repo_name():
- """
- Get the repo name from the hgrc default path.
- """
- default = subprocess.check_output('hg paths default').strip()
- parts = default.split('/')
- if parts[-1] == '':
- parts.pop()
- return '/'.join(parts[-2:])
-
-def get_mercurial_creds(system='https://bitbucket.org', username=None):
- """
- Return named tuple of username,password in much the same way that
- Mercurial would (from the keyring).
- """
- # todo: consider getting this from .hgrc
- username = username or getpass.getuser()
- keyring_username = '@@'.join((username, system))
- system = 'Mercurial'
- password = (
- keyring.get_password(system, keyring_username)
- if 'keyring' in globals()
- else None
- )
- if not password:
- password = getpass.getpass()
- Credential = collections.namedtuple('Credential', 'username password')
- return Credential(username, password)
-
-def add_milestone_and_version(version):
- auth = 'Basic ' + ':'.join(get_mercurial_creds()).encode('base64').strip()
- headers = {
- 'Authorization': auth,
- }
- base = 'https://api.bitbucket.org'
- for type in 'milestones', 'versions':
- url = (base + '/1.0/repositories/{repo}/issues/{type}'
- .format(repo = get_repo_name(), type=type))
- req = urllib_request.Request(url = url, headers = headers,
- data='name='+version)
- try:
- urllib_request.urlopen(req)
- except urllib_request.HTTPError as e:
- print(e.fp.read())
-
-def bump_versions(target_ver):
- for filename in files_with_versions:
- bump_version(filename, target_ver)
- subprocess.check_call(['hg', 'ci', '-m',
- 'Bumped to {target_ver} in preparation for next '
- 'release.'.format(**vars())])
- return target_ver
-
-def bump_version(filename, target_ver):
- with open(filename, 'rb') as f:
- lines = [
- line.replace(VERSION.encode('ascii'), target_ver.encode('ascii'))
- for line in f
- ]
- with open(filename, 'wb') as f:
- f.writelines(lines)
-
-def do_release():
- assert all(map(os.path.exists, files_with_versions)), (
- "Expected file(s) missing")
-
- assert has_sphinx(), "You must have Sphinx installed to release"
-
- set_versions()
-
- res = raw_input('Have you read through the SCM changelog and '
- 'confirmed the changelog is current for releasing {VERSION}? '
- .format(**globals()))
- if not res.lower().startswith('y'):
- print("Please do that")
- raise SystemExit(1)
-
- print("Travis-CI tests: http://travis-ci.org/#!/jaraco/setuptools")
- res = raw_input('Have you or has someone verified that the tests '
- 'pass on this revision? ')
- if not res.lower().startswith('y'):
- print("Please do that")
- raise SystemExit(2)
-
- subprocess.check_call(['hg', 'tag', VERSION])
-
- subprocess.check_call(['hg', 'update', VERSION])
-
- upload_to_pypi()
-
- # update to the tip for the next operation
- subprocess.check_call(['hg', 'update'])
-
- # we just tagged the current version, bump for the next release.
- next_ver = bump_versions(infer_next_version(VERSION))
-
- # push the changes
- subprocess.check_call(['hg', 'push'])
-
- add_milestone_and_version(next_ver)
-
-def upload_to_pypi():
- linkify('CHANGES.txt', 'CHANGES (links).txt')
-
- has_docs = build_docs()
- if os.path.isdir('./dist'):
- shutil.rmtree('./dist')
- cmd = [
- sys.executable, 'setup.py', '-q',
- 'egg_info', '-RD', '-b', '',
- 'sdist',
- #'register', '-r', PACKAGE_INDEX,
- #'upload', '-r', PACKAGE_INDEX,
- ]
- if has_docs:
- cmd.extend([
- 'upload_docs', '-r', PACKAGE_INDEX
- ])
- env = os.environ.copy()
- env["SETUPTOOLS_INSTALL_WINDOWS_SPECIFIC_FILES"] = "1"
- subprocess.check_call(cmd, env=env)
-
-def has_sphinx():
- try:
- devnull = open(os.path.devnull, 'wb')
- subprocess.Popen(['sphinx-build', '--version'], stdout=devnull,
- stderr=subprocess.STDOUT).wait()
- except Exception:
- return False
- return True
-
-def build_docs():
- if not os.path.isdir('docs'):
- return
- if os.path.isdir('docs/build'):
- shutil.rmtree('docs/build')
- cmd = [
- 'sphinx-build',
- '-b', 'html',
- '-d', 'build/doctrees',
- '.',
- 'build/html',
- ]
- subprocess.check_call(cmd, cwd='docs')
- return True
-
-def linkify(source, dest):
- with open(source) as source:
- out = _linkified_text(source.read())
- with open(dest, 'w') as dest:
- dest.write(out)
-
-def _linkified(rst_path):
- "return contents of reStructureText file with linked issue references"
- rst_file = open(rst_path)
- rst_content = rst_file.read()
- rst_file.close()
-
- return _linkified_text(rst_content)
-
-def _linkified_text(rst_content):
- # first identify any existing HREFs so they're not changed
- HREF_pattern = re.compile('`.*?`_', re.MULTILINE | re.DOTALL)
-
- # split on the HREF pattern, returning the parts to be linkified
- plain_text_parts = HREF_pattern.split(rst_content)
- anchors = []
- linkified_parts = [_linkified_part(part, anchors)
- for part in plain_text_parts]
- pairs = itertools.izip_longest(
- linkified_parts,
- HREF_pattern.findall(rst_content),
- fillvalue='',
- )
- rst_content = ''.join(flatten(pairs))
-
- anchors = sorted(anchors)
+test_info = "Travis-CI tests: http://travis-ci.org/#!/jaraco/setuptools"
+
+os.environ["SETUPTOOLS_INSTALL_WINDOWS_SPECIFIC_FILES"] = "1"
+
+link_patterns = [
+ r"(Issue )?#(?P<issue>\d+)",
+ r"Distribute #(?P<distribute>\d+)",
+ r"Buildout #(?P<buildout>\d+)",
+ r"Old Setuptools #(?P<old_setuptools>\d+)",
+ r"Jython #(?P<jython>\d+)",
+ r"Python #(?P<python>\d+)",
+]
+
+issue_urls = dict(
+ issue='https://bitbucket.org/pypa/setuptools/issue/{issue}',
+ distribute='https://bitbucket.org/tarek/distribute/issue/{distribute}',
+ buildout='https://github.com/buildout/buildout/issues/{buildout}',
+ old_setuptools='http://bugs.python.org/setuptools/issue{old_setuptools}',
+ jython='http://bugs.jython.org/issue{jython}',
+ python='http://bugs.python.org/issue{python}',
+)
- bitroot = 'https://bitbucket.org/tarek/distribute'
- rst_content += "\n"
- for x in anchors:
- issue = re.findall(r'\d+', x)[0]
- rst_content += '.. _`%s`: %s/issue/%s\n' % (x, bitroot, issue)
- rst_content += "\n"
- return rst_content
-def flatten(listOfLists):
- "Flatten one level of nesting"
- return itertools.chain.from_iterable(listOfLists)
+def _linkify(source, dest):
+ pattern = '|'.join(link_patterns)
+ with open(source) as source:
+ out = re.sub(pattern, replacer, source.read())
+ with open(dest, 'w') as dest:
+ dest.write(out)
-def _linkified_part(text, anchors):
- """
- Linkify a part and collect any anchors generated
- """
- revision = re.compile(r'\b(issue\s+#?\d+)\b', re.M | re.I)
+def replacer(match):
+ text = match.group(0)
+ match_dict = match.groupdict()
+ for key in match_dict:
+ if match_dict[key]:
+ url = issue_urls[key].format(**match_dict)
+ return "`{text} <{url}>`_".format(text=text, url=url)
- anchors.extend(revision.findall(text)) # ['Issue #43', ...]
- return revision.sub(r'`\1`_', text)
-if __name__ == '__main__':
- do_release()
+def _add_bootstrap_bookmark():
+ cmd = ['hg', 'bookmark', '-i', 'bootstrap', '-f']
+ subprocess.Popen(cmd)