mirror of https://github.com/docker/compose.git
Add release validation and tagging script release.py
Signed-off-by: Ulysses Souza <ulyssessouza@gmail.com>
This commit is contained in:
parent
d9af02eddf
commit
b5c4f4fc0f
|
@ -1,6 +1,8 @@
|
||||||
|
Click==7.0
|
||||||
coverage==5.0.3
|
coverage==5.0.3
|
||||||
ddt==1.2.2
|
ddt==1.2.2
|
||||||
flake8==3.7.9
|
flake8==3.7.9
|
||||||
|
gitpython==2.1.14
|
||||||
mock==3.0.5
|
mock==3.0.5
|
||||||
pytest==5.3.4; python_version >= '3.5'
|
pytest==5.3.4; python_version >= '3.5'
|
||||||
pytest==4.6.5; python_version < '3.5'
|
pytest==4.6.5; python_version < '3.5'
|
||||||
|
|
|
@ -4,6 +4,15 @@ The release process is fully automated by `Release.Jenkinsfile`.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
1. edit `compose/__init__.py` to set release version number
|
1. In the appropriate branch, run `./scripts/release/release tag <version>`
|
||||||
1. commit and tag as `{major}.{minor}.{patch}`
|
|
||||||
1. edit `compose/__init__.py` again to set next development version number
|
By appropriate, we mean for a version `1.26.0` or `1.26.0-rc1` you should run the script in the `1.26.x` branch.
|
||||||
|
|
||||||
|
The script should check the above then ask for changelog modifications.
|
||||||
|
|
||||||
|
After the executions, you should have a commit with the proper bumps for `docker-compose version` and `run.sh`
|
||||||
|
|
||||||
|
2. Run `git push --tags upstream <version_branch>`
|
||||||
|
This should trigger a new CI build on the new tag. When the CI finishes with the tests and builds a new draft release would be available on github's releases page.
|
||||||
|
|
||||||
|
3. Check and confirm the release on github's release page.
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
from __future__ import absolute_import
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
REPO_ROOT = os.path.join(os.path.dirname(__file__), '..', '..')
|
|
@ -0,0 +1,126 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
from __future__ import absolute_import
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
import click
|
||||||
|
from git import Repo
|
||||||
|
from utils import update_init_py_version
|
||||||
|
from utils import update_run_sh_version
|
||||||
|
from utils import yesno
|
||||||
|
|
||||||
|
VALID_VERSION_PATTERN = re.compile(r"^\d+\.\d+\.\d+(-rc\d+)?$")
|
||||||
|
|
||||||
|
|
||||||
|
class Version(str):
|
||||||
|
def matching_groups(self):
|
||||||
|
match = VALID_VERSION_PATTERN.match(self)
|
||||||
|
if not match:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return match.groups()
|
||||||
|
|
||||||
|
def is_ga_version(self):
|
||||||
|
groups = self.matching_groups()
|
||||||
|
if not groups:
|
||||||
|
return False
|
||||||
|
|
||||||
|
rc_suffix = groups[1]
|
||||||
|
return not rc_suffix
|
||||||
|
|
||||||
|
def validate(self):
|
||||||
|
return len(self.matching_groups()) > 0
|
||||||
|
|
||||||
|
def branch_name(self):
|
||||||
|
if not self.validate():
|
||||||
|
return None
|
||||||
|
|
||||||
|
rc_part = self.matching_groups()[0]
|
||||||
|
ver = self
|
||||||
|
if rc_part:
|
||||||
|
ver = ver[:-len(rc_part)]
|
||||||
|
|
||||||
|
tokens = ver.split(".")
|
||||||
|
tokens[-1] = 'x'
|
||||||
|
|
||||||
|
return ".".join(tokens)
|
||||||
|
|
||||||
|
|
||||||
|
def create_bump_commit(repository, version):
|
||||||
|
print('Creating bump commit...')
|
||||||
|
repository.commit('-a', '-s', '-m "Bump {}"'.format(version), '--no-verify')
|
||||||
|
|
||||||
|
|
||||||
|
def validate_environment(version, repository):
|
||||||
|
if not version.validate():
|
||||||
|
print('Version "{}" has an invalid format. This should follow D+.D+.D+(-rcD+). '
|
||||||
|
'Like: 1.26.0 or 1.26.0-rc1'.format(version))
|
||||||
|
return False
|
||||||
|
|
||||||
|
expected_branch = version.branch_name()
|
||||||
|
if str(repository.active_branch) != expected_branch:
|
||||||
|
print('Cannot tag in this branch with version "{}". '
|
||||||
|
'Please checkout "{}" to tag'.format(version, version.branch_name()))
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@click.group()
|
||||||
|
def cli():
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@cli.command()
|
||||||
|
@click.argument('version')
|
||||||
|
def tag(version):
|
||||||
|
"""
|
||||||
|
Updates the version related files and tag
|
||||||
|
"""
|
||||||
|
repo = Repo(".")
|
||||||
|
version = Version(version)
|
||||||
|
if not validate_environment(version, repo):
|
||||||
|
return
|
||||||
|
|
||||||
|
update_init_py_version(version)
|
||||||
|
update_run_sh_version(version)
|
||||||
|
|
||||||
|
input('Please add the release notes to the CHANGELOG.md file, then press Enter to continue.')
|
||||||
|
proceed = False
|
||||||
|
while not proceed:
|
||||||
|
print(repo.git.diff())
|
||||||
|
proceed = yesno('Are these changes ok? y/N ', default=False)
|
||||||
|
|
||||||
|
if repo.git.diff():
|
||||||
|
create_bump_commit(repo.git, version)
|
||||||
|
else:
|
||||||
|
print('No changes to commit. Exiting...')
|
||||||
|
return
|
||||||
|
|
||||||
|
repo.create_tag(version)
|
||||||
|
|
||||||
|
print('Please, check the changes. If everything is OK, you just need to push with:\n'
|
||||||
|
'$ git push --tags upstream {}'.format(version.branch_name()))
|
||||||
|
|
||||||
|
|
||||||
|
@cli.command()
|
||||||
|
@click.argument('version')
|
||||||
|
def push_latest(version):
|
||||||
|
"""
|
||||||
|
TODO Pushes the latest tag pointing to a certain GA version
|
||||||
|
"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
|
@cli.command()
|
||||||
|
@click.argument('version')
|
||||||
|
def ghtemplate(version):
|
||||||
|
"""
|
||||||
|
TODO Generates the github release page content
|
||||||
|
"""
|
||||||
|
version = Version(version)
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
cli()
|
|
@ -0,0 +1,47 @@
|
||||||
|
from __future__ import absolute_import
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
|
||||||
|
from const import REPO_ROOT
|
||||||
|
|
||||||
|
|
||||||
|
def update_init_py_version(version):
|
||||||
|
path = os.path.join(REPO_ROOT, 'compose', '__init__.py')
|
||||||
|
with open(path, 'r') as f:
|
||||||
|
contents = f.read()
|
||||||
|
contents = re.sub(r"__version__ = '[0-9a-z.-]+'", "__version__ = '{}'".format(version), contents)
|
||||||
|
with open(path, 'w') as f:
|
||||||
|
f.write(contents)
|
||||||
|
|
||||||
|
|
||||||
|
def update_run_sh_version(version):
|
||||||
|
path = os.path.join(REPO_ROOT, 'script', 'run', 'run.sh')
|
||||||
|
with open(path, 'r') as f:
|
||||||
|
contents = f.read()
|
||||||
|
contents = re.sub(r'VERSION="[0-9a-z.-]+"', 'VERSION="{}"'.format(version), contents)
|
||||||
|
with open(path, 'w') as f:
|
||||||
|
f.write(contents)
|
||||||
|
|
||||||
|
|
||||||
|
def yesno(prompt, default=None):
|
||||||
|
"""
|
||||||
|
Prompt the user for a yes or no.
|
||||||
|
|
||||||
|
Can optionally specify a default value, which will only be
|
||||||
|
used if they enter a blank line.
|
||||||
|
|
||||||
|
Unrecognised input (anything other than "y", "n", "yes",
|
||||||
|
"no" or "") will return None.
|
||||||
|
"""
|
||||||
|
answer = input(prompt).strip().lower()
|
||||||
|
|
||||||
|
if answer == "y" or answer == "yes":
|
||||||
|
return True
|
||||||
|
elif answer == "n" or answer == "no":
|
||||||
|
return False
|
||||||
|
elif answer == "":
|
||||||
|
return default
|
||||||
|
else:
|
||||||
|
return None
|
Loading…
Reference in New Issue