예제 #1
0
async def release(*,
                  github_access_token,
                  repo_info,
                  new_version,
                  branch=None,
                  commit_hash=None):
    """
    Run a release

    Args:
        github_access_token (str): The github access token
        repo_info (RepoInfo): RepoInfo for a repo
        new_version (str): The version of the new release
        branch (str): The branch to initialize the release from
        commit_hash (str): Commit hash to cherry pick in case of a hot fix
    """

    await validate_dependencies()
    async with init_working_dir(github_access_token,
                                repo_info.repo_url,
                                branch=branch) as working_dir:
        default_branch = await get_default_branch(working_dir)
        await check_call(["git", "checkout", "-qb", "release-candidate"],
                         cwd=working_dir)
        if commit_hash:
            try:
                await check_call(["git", "cherry-pick", commit_hash],
                                 cwd=working_dir)
            except CalledProcessError as ex:
                raise ReleaseException(
                    f"Cherry pick failed for the given hash {commit_hash}"
                ) from ex
        old_version = await update_version(
            repo_info=repo_info,
            new_version=new_version,
            working_dir=working_dir,
            readonly=False,
        )
        if parse_version(old_version) >= parse_version(new_version):
            raise ReleaseException(
                f"old version is {old_version} but the new version {new_version} is not newer"
            )
        base_branch = "release-candidate" if commit_hash else default_branch
        await verify_new_commits(old_version,
                                 base_branch=base_branch,
                                 root=working_dir)
        await update_release_notes(old_version,
                                   new_version,
                                   base_branch=base_branch,
                                   root=working_dir)
        await build_release(root=working_dir)
        return await generate_release_pr(
            github_access_token=github_access_token,
            repo_url=repo_info.repo_url,
            old_version=old_version,
            new_version=new_version,
            base_branch=base_branch,
            root=working_dir,
        )
예제 #2
0
async def release(github_access_token,
                  repo_url,
                  new_version,
                  branch=None,
                  commit_hash=None):
    """
    Run a release

    Args:
        github_access_token (str): The github access token
        repo_url (str): URL for a repo
        new_version (str): The version of the new release
        branch (str): The branch to initialize the release from
        commit_hash (str): Commit hash to cherry pick in case of a hot fix
    """

    await validate_dependencies()
    async with init_working_dir(github_access_token, repo_url,
                                branch=branch) as working_dir:
        await check_call(["git", "checkout", "-qb", "release-candidate"],
                         cwd=working_dir)
        if commit_hash:
            try:
                await check_call(["git", "cherry-pick", commit_hash],
                                 cwd=working_dir)
            except CalledProcessError:
                raise ReleaseException(
                    f"Cherry pick failed for the given hash {commit_hash}")
        old_version = update_version(new_version, working_dir=working_dir)
        if parse_version(old_version) >= parse_version(new_version):
            raise ReleaseException(
                "old version is {old} but the new version {new} is not newer".
                format(
                    old=old_version,
                    new=new_version,
                ))
        base_branch = "release-candidate" if commit_hash else "master"
        await verify_new_commits(old_version,
                                 base_branch=base_branch,
                                 root=working_dir)
        await update_release_notes(old_version,
                                   new_version,
                                   base_branch=base_branch,
                                   root=working_dir)
        await build_release(root=working_dir)
        await generate_release_pr(
            github_access_token=github_access_token,
            repo_url=repo_url,
            old_version=old_version,
            new_version=new_version,
            base_branch=base_branch,
            root=working_dir,
        )

    print(f"version {old_version} has been updated to {new_version}")
    print("Go tell engineers to check their work. PR is on the repo.")
    print("After they are done, run the finish_release.py script.")
예제 #3
0
def release(github_access_token, repo_url, new_version):
    """
    Run a release

    Args:
        github_access_token (str): The github access token
        repo_url (str): URL for a repo
        new_version (str): The version of the new release
    """

    validate_dependencies()

    with init_working_dir(github_access_token, repo_url):
        check_call(["git", "checkout", "-qb", "release-candidate"])
        old_version = update_version(new_version)
        if parse_version(old_version) >= parse_version(new_version):
            raise ReleaseException("old version is {old} but the new version {new} is not newer".format(
                old=old_version,
                new=new_version,
            ))
        verify_new_commits(old_version)
        update_release_notes(old_version, new_version)
        build_release()
        generate_release_pr(github_access_token, repo_url, old_version, new_version)

    print("version {old_version} has been updated to {new_version}".format(
        old_version=old_version,
        new_version=new_version,
    ))
    print("Go tell engineers to check their work. PR is on the repo.")
    print("After they are done, run the finish_release.py script.")
예제 #4
0
async def get_release_pr(*, github_access_token, org, repo):
    """
    Look up the pull request information for a release, or return None if it doesn't exist

    Args:
        github_access_token (str): The github access token
        org (str): The github organization (eg mitodl)
        repo (str): The github repository (eg micromasters)

    Returns:
        ReleasePR: The information about the release pull request, or None if there is no release PR in progress
    """
    pr = await get_pull_request(
        github_access_token=github_access_token,
        org=org,
        repo=repo,
        branch='release-candidate',
    )
    if pr is None:
        return None

    title = pr['title']
    match = re.match(r'^Release (?P<version>\d+\.\d+\.\d+)$', title)
    if not match:
        raise ReleaseException("Release PR title has an unexpected format")
    version = match.group('version')

    return ReleasePR(
        version=version,
        body=pr['body'],
        url=pr['html_url'],
    )
예제 #5
0
async def get_unchecked_authors(*, github_access_token, org, repo):
    """
    Returns list of authors who have not yet checked off their checkboxes

    Args:
        github_access_token (str): The github access token
        org (str): The github organization (eg mitodl)
        repo (str): The github repository (eg micromasters)

    Returns:
        set[str]: A set of github usernames
    """
    release_pr = await get_release_pr(
        github_access_token=github_access_token,
        org=org,
        repo=repo,
    )
    if not release_pr:
        raise ReleaseException("No release PR found")
    body = release_pr.body
    commits = parse_checkmarks(body)
    return {
        commit['author_name']
        for commit in commits if not commit['checked']
    }
예제 #6
0
    async def release_command(self, command_args):
        """
        Start a new release and wait for deployment

        Args:
            command_args (CommandArgs): The arguments for this command
        """
        repo_info = command_args.repo_info
        version = command_args.args[0]
        repo_url = repo_info.repo_url
        channel_id = repo_info.channel_id
        org, repo = get_org_and_repo(repo_url)
        pr = get_release_pr(self.github_access_token, org, repo)
        if pr:
            raise ReleaseException(
                "A release is already in progress: {}".format(pr.url))
        release(
            github_access_token=self.github_access_token,
            repo_url=repo_url,
            new_version=version,
        )

        await self.say(
            channel_id=channel_id,
            text=
            "Behold, my new evil scheme - release {version} for {project}! Now deploying to RC..."
            .format(
                version=version,
                project=repo_info.name,
            ),
        )

        await wait_for_deploy(
            github_access_token=self.github_access_token,
            repo_url=repo_url,
            hash_url=repo_info.rc_hash_url,
            watch_branch="release-candidate",
        )
        unchecked_authors = get_unchecked_authors(self.github_access_token,
                                                  org, repo)
        slack_usernames = self.translate_slack_usernames(unchecked_authors)
        pr = get_release_pr(self.github_access_token, org, repo)
        await self.say(
            channel_id=channel_id,
            text=
            "Release {version} for {project} was deployed! PR is up at {pr_url}."
            " These people have commits in this release: {authors}".format(
                version=version,
                authors=", ".join(slack_usernames),
                pr_url=pr.url,
                project=repo_info.name,
            ))

        await self.wait_for_checkboxes(repo_info, command_args.manager)
        command_args.loop.create_task(self.delay_message(repo_info))
예제 #7
0
    async def finish_release(self, command_args):
        """
        Merge the release candidate into the release branch, tag it, merge to master, and wait for deployment

        Args:
            command_args (CommandArgs): The arguments for this command
        """
        repo_info = command_args.repo_info
        channel_id = repo_info.channel_id
        repo_url = repo_info.repo_url
        org, repo = get_org_and_repo(repo_url)
        pr = get_release_pr(
            github_access_token=self.github_access_token,
            org=org,
            repo=repo,
        )
        if not pr:
            raise ReleaseException("No release currently in progress for {project}".format(project=repo_info.name))
        version = pr.version

        finish_release(
            github_access_token=self.github_access_token,
            repo_url=repo_url,
            version=version,
            timezone=self.timezone
        )

        await self.say(
            channel_id=channel_id,
            text="Merged evil scheme {version} for {project}! Now deploying to production...".format(
                version=version,
                project=repo_info.name,
            ),
        )
        await wait_for_deploy(
            github_access_token=self.github_access_token,
            repo_url=repo_url,
            hash_url=repo_info.prod_hash_url,
            watch_branch="release",
        )
        await self.say(
            channel_id=channel_id,
            text="My evil scheme {version} for {project} has been released to production. "
            "And by 'released', I mean completely...um...leased.".format(
                version=version,
                project=repo_info.name,
            )
        )
예제 #8
0
    async def release_command(self, command_args):
        """
        Start a new release and wait for deployment

        Args:
            command_args (CommandArgs): The arguments for this command
        """
        repo_info = command_args.repo_info
        repo_url = repo_info.repo_url
        org, repo = get_org_and_repo(repo_url)
        pr = get_release_pr(
            github_access_token=self.github_access_token,
            org=org,
            repo=repo,
        )
        if pr:
            raise ReleaseException("A release is already in progress: {}".format(pr.url))

        if repo_info.project_type == LIBRARY_TYPE:
            await self._library_release(command_args)
        elif repo_info.project_type == WEB_APPLICATION_TYPE:
            await self._web_application_release(command_args)
        else:
            raise Exception("Configuration error: unknown project type {}".format(repo_info.project_type))
예제 #9
0
def verify_new_commits(old_version):
    """Check if there are new commits to release"""
    if not any_new_commits(old_version):
        raise ReleaseException("No new commits to put in release")
예제 #10
0
async def verify_new_commits(old_version, *, base_branch, root):
    """Check if there are new commits to release"""
    if not await any_new_commits(
            old_version, base_branch=base_branch, root=root):
        raise ReleaseException("No new commits to put in release")