163 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			163 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Python
		
	
	
	
import os
 | 
						|
from pathlib import Path
 | 
						|
from subprocess import check_output, check_call
 | 
						|
 | 
						|
import invoke
 | 
						|
 | 
						|
 | 
						|
@invoke.task(help={
 | 
						|
    'version': 'version being released',
 | 
						|
})
 | 
						|
def announce(ctx, version):
 | 
						|
    """Generates a new release announcement entry in the docs."""
 | 
						|
    # Get our list of authors
 | 
						|
    stdout = check_output(["git", "describe", "--abbrev=0", '--tags'])
 | 
						|
    stdout = stdout.decode('utf-8')
 | 
						|
    last_version = stdout.strip()
 | 
						|
 | 
						|
    stdout = check_output(["git", "log", "{}..HEAD".format(last_version), "--format=%aN"])
 | 
						|
    stdout = stdout.decode('utf-8')
 | 
						|
 | 
						|
    contributors = set(stdout.splitlines())
 | 
						|
 | 
						|
    template_name = 'release.minor.rst' if version.endswith('.0') else 'release.patch.rst'
 | 
						|
    template_text = Path(__file__).parent.joinpath(template_name).read_text(encoding='UTF-8')
 | 
						|
 | 
						|
    contributors_text = '\n'.join('* {}'.format(name) for name in sorted(contributors)) + '\n'
 | 
						|
    text = template_text.format(version=version, contributors=contributors_text)
 | 
						|
 | 
						|
    target = Path(__file__).parent.joinpath('../doc/en/announce/release-{}.rst'.format(version))
 | 
						|
    target.write_text(text, encoding='UTF-8')
 | 
						|
    print("[generate.announce] Generated {}".format(target.name))
 | 
						|
 | 
						|
    # Update index with the new release entry
 | 
						|
    index_path = Path(__file__).parent.joinpath('../doc/en/announce/index.rst')
 | 
						|
    lines = index_path.read_text(encoding='UTF-8').splitlines()
 | 
						|
    indent = '   '
 | 
						|
    for index, line in enumerate(lines):
 | 
						|
        if line.startswith('{}release-'.format(indent)):
 | 
						|
            new_line = indent + target.stem
 | 
						|
            if line != new_line:
 | 
						|
                lines.insert(index, new_line)
 | 
						|
                index_path.write_text('\n'.join(lines) + '\n', encoding='UTF-8')
 | 
						|
                print("[generate.announce] Updated {}".format(index_path.name))
 | 
						|
            else:
 | 
						|
                print("[generate.announce] Skip {} (already contains release)".format(index_path.name))
 | 
						|
            break
 | 
						|
 | 
						|
    check_call(['git', 'add', str(target)])
 | 
						|
 | 
						|
 | 
						|
@invoke.task()
 | 
						|
def regen(ctx):
 | 
						|
    """Call regendoc tool to update examples and pytest output in the docs."""
 | 
						|
    print("[generate.regen] Updating docs")
 | 
						|
    check_call(['tox', '-e', 'regen'])
 | 
						|
 | 
						|
 | 
						|
@invoke.task()
 | 
						|
def make_tag(ctx, version):
 | 
						|
    """Create a new (local) tag for the release, only if the repository is clean."""
 | 
						|
    from git import Repo
 | 
						|
 | 
						|
    repo = Repo('.')
 | 
						|
    if repo.is_dirty():
 | 
						|
        print('Current repository is dirty. Please commit any changes and try again.')
 | 
						|
        raise invoke.Exit(code=2)
 | 
						|
 | 
						|
    tag_names = [x.name for x in repo.tags]
 | 
						|
    if version in tag_names:
 | 
						|
        print("[generate.make_tag] Delete existing tag {}".format(version))
 | 
						|
        repo.delete_tag(version)
 | 
						|
 | 
						|
    print("[generate.make_tag] Create tag {}".format(version))
 | 
						|
    repo.create_tag(version)
 | 
						|
 | 
						|
 | 
						|
@invoke.task()
 | 
						|
def devpi_upload(ctx, version, user, password=None):
 | 
						|
    """Creates and uploads a package to devpi for testing."""
 | 
						|
    if password:
 | 
						|
        print("[generate.devpi_upload] devpi login {}".format(user))
 | 
						|
        check_call(['devpi', 'login', user, '--password', password])
 | 
						|
 | 
						|
    check_call(['devpi', 'use', 'https://devpi.net/{}/dev'.format(user)])
 | 
						|
    
 | 
						|
    env = os.environ.copy()
 | 
						|
    env['SETUPTOOLS_SCM_PRETEND_VERSION'] = version
 | 
						|
    check_call(['devpi', 'upload', '--formats', 'sdist,bdist_wheel'], env=env)
 | 
						|
    print("[generate.devpi_upload] package uploaded")
 | 
						|
 | 
						|
 | 
						|
@invoke.task(help={
 | 
						|
    'version': 'version being released',
 | 
						|
    'user': 'name of the user on devpi to stage the generated package',
 | 
						|
    'password': 'user password on devpi to stage the generated package '
 | 
						|
                '(if not given assumed logged in)',
 | 
						|
})
 | 
						|
def pre_release(ctx, version, user, password=None):
 | 
						|
    """Generates new docs, release announcements and uploads a new release to devpi for testing."""
 | 
						|
    announce(ctx, version)
 | 
						|
    regen(ctx)
 | 
						|
    changelog(ctx, version, write_out=True)
 | 
						|
 | 
						|
    msg = 'Preparing release version {}'.format(version)
 | 
						|
    check_call(['git', 'commit', '-a', '-m', msg])
 | 
						|
    
 | 
						|
    make_tag(ctx, version)
 | 
						|
 | 
						|
    devpi_upload(ctx, version=version, user=user, password=password)
 | 
						|
    
 | 
						|
    print()
 | 
						|
    print('[generate.pre_release] Please push your branch and open a PR.')
 | 
						|
 | 
						|
 | 
						|
@invoke.task(help={
 | 
						|
    'version': 'version being released',
 | 
						|
    'user': 'name of the user on devpi to stage the generated package',
 | 
						|
    'pypi_name': 'name of the pypi configuration section in your ~/.pypirc',
 | 
						|
})
 | 
						|
def publish_release(ctx, version, user, pypi_name):
 | 
						|
    """Publishes a package previously created by the 'pre_release' command."""
 | 
						|
    from git import Repo
 | 
						|
    repo = Repo('.')
 | 
						|
    tag_names = [x.name for x in repo.tags]
 | 
						|
    if version not in tag_names:
 | 
						|
        print('Could not find tag for version {}, exiting...'.format(version))
 | 
						|
        raise invoke.Exit(code=2)
 | 
						|
 | 
						|
    check_call(['devpi', 'use', 'https://devpi.net/{}/dev'.format(user)])
 | 
						|
    check_call(['devpi', 'push', 'pytest=={}'.format(version), 'pypi:{}'.format(pypi_name)])
 | 
						|
    check_call(['git', 'push', 'git@github.com:pytest-dev/pytest.git', version])
 | 
						|
 | 
						|
    emails = [
 | 
						|
        'pytest-dev@python.org',
 | 
						|
        'python-announce-list@python.org'
 | 
						|
    ]
 | 
						|
    if version.endswith('.0'):
 | 
						|
        emails.append('testing-in-python@lists.idyll.org')
 | 
						|
    print('Version {} has been published to PyPI!'.format(version))
 | 
						|
    print()
 | 
						|
    print('Please send an email announcement with the contents from:')
 | 
						|
    print()
 | 
						|
    print('  doc/en/announce/release-{}.rst'.format(version))
 | 
						|
    print()
 | 
						|
    print('To the following mail lists:')
 | 
						|
    print()
 | 
						|
    print(' ', ','.join(emails))
 | 
						|
    print()
 | 
						|
    print('And announce it on twitter adding the #pytest hash tag.')
 | 
						|
 | 
						|
 | 
						|
@invoke.task(help={
 | 
						|
    'version': 'version being released',
 | 
						|
    'write_out': 'write changes to the actual changelog'
 | 
						|
})
 | 
						|
def changelog(ctx, version, write_out=False):
 | 
						|
    if write_out:
 | 
						|
        addopts = []
 | 
						|
    else:
 | 
						|
        addopts = ['--draft']
 | 
						|
    check_call(['towncrier', '--version', version] + addopts)
 | 
						|
 |