async def test_issue_release_notes(doof, test_repo, mocker):
    """issue release notes should list closed issues over the last seven days"""
    org, repo = get_org_and_repo(test_repo.repo_url)
    channel_id = test_repo.channel_id
    pr = make_pr(123, "A PR")
    fetch_prs = mocker.patch('bot.fetch_pull_requests_since_date', return_value=[pr])
    tups = [
        (pr, [(make_issue(333), make_parsed_issue(333, False))])
    ]
    fetch_issues = mocker.patch('bot.fetch_issues_for_pull_requests', return_value=async_gen_wrapper(tups))
    notes = "some release notes"
    make_release_notes = mocker.patch('bot.make_issue_release_notes', return_value=notes)
    await doof.issue_release_notes(CommandArgs(
        repo_info=test_repo,
        channel_id=test_repo.channel_id,
        args=[],
        manager="me"
    ))

    assert doof.said("Release notes for issues closed by PRs", channel_id=channel_id)
    assert doof.said(notes, channel_id=channel_id)

    fetch_prs.assert_called_once_with(
        github_access_token=GITHUB_ACCESS,
        org=org,
        repo=repo,
        since=(now_in_utc() - timedelta(days=7)).date()
    )
    fetch_issues.assert_called_once_with(
        github_access_token=GITHUB_ACCESS,
        pull_requests=[pr],
    )
    make_release_notes.assert_called_once_with(
        tups,
    )
async def test_release_notes(doof, test_repo, test_repo_directory, mocker):
    """Doof should show release notes"""
    old_version = "0.1.2"
    update_version_mock = mocker.patch('bot.update_version', autospec=True, return_value=old_version)
    mocker.patch(
        'bot.init_working_dir', side_effect=async_context_manager_yielder(test_repo_directory)
    )
    notes = "some notes"
    create_release_notes_mock = mocker.async_patch('bot.create_release_notes', return_value=notes)
    any_new_commits_mock = mocker.async_patch('bot.any_new_commits', return_value=True)
    org, repo = get_org_and_repo(test_repo.repo_url)
    release_pr = ReleasePR('version', f'https://github.com/{org}/{repo}/pulls/123456', 'body')
    get_release_pr_mock = mocker.async_patch('bot.get_release_pr', return_value=release_pr)

    await doof.run_command(
        manager='mitodl_user',
        channel_id=test_repo.channel_id,
        words=['release', 'notes'],
    )

    update_version_mock.assert_called_once_with("9.9.9", working_dir=test_repo_directory)
    create_release_notes_mock.assert_called_once_with(
        old_version, with_checkboxes=False, base_branch="master", root=test_repo_directory
    )
    any_new_commits_mock.assert_called_once_with(old_version, base_branch="master", root=test_repo_directory)
    get_release_pr_mock.assert_called_once_with(github_access_token=GITHUB_ACCESS, org=org, repo=repo)

    assert doof.said("Release notes since {}".format(old_version))
    assert doof.said(notes)
    assert doof.said(f"And also! There is a release already in progress: {release_pr.url}")
async def test_release(doof, repo_info, event_loop, mocker, command):
    """
    Doof should do a release when asked
    """
    version = '1.2.3'
    pr = ReleasePR(
        version=version,
        url='http://new.url',
        body='Release PR body',
    )
    get_release_pr_mock = mocker.patch('bot.get_release_pr', autospec=True, side_effect=[None, pr, pr])
    release_mock = mocker.patch('bot.release', autospec=True)

    wait_for_deploy_sync_mock = mocker.Mock()

    async def wait_for_deploy_fake(*args, **kwargs):
        """await cannot be used with mock objects"""
        wait_for_deploy_sync_mock(*args, **kwargs)

    mocker.patch('bot.wait_for_deploy', wait_for_deploy_fake)
    authors = ['author1', 'author2']
    mocker.patch('bot.get_unchecked_authors', return_value=authors)

    wait_for_checkboxes_sync_mock = mocker.Mock()
    async def wait_for_checkboxes_fake(*args, **kwargs):
        """await cannot be used with mock objects"""
        wait_for_checkboxes_sync_mock(*args, **kwargs)
    mocker.patch('bot.wait_for_checkboxes', wait_for_checkboxes_fake)

    command_words = command.split() + [version]
    me = 'mitodl_user'
    await doof.run_command(
        manager=me,
        channel_id=repo_info.channel_id,
        words=command_words,
        loop=event_loop,
    )

    org, repo = get_org_and_repo(repo_info.repo_url)
    get_release_pr_mock.assert_any_call(GITHUB_ACCESS, org, repo)
    release_mock.assert_called_once_with(
        github_access_token=GITHUB_ACCESS,
        repo_url=repo_info.repo_url,
        new_version=pr.version,
    )
    wait_for_deploy_sync_mock.assert_called_once_with(
        github_access_token=GITHUB_ACCESS,
        repo_url=repo_info.repo_url,
        hash_url=repo_info.rc_hash_url,
        watch_branch='release-candidate',
    )
    assert doof.said("Now deploying to RC...")
    assert doof.said("These people have commits in this release: {}".format(', '.join(authors)))
    wait_for_checkboxes_sync_mock.assert_called_once_with(GITHUB_ACCESS, org, repo)
    assert doof.said(
        "Release {version} is ready for the Merginator {name}".format(
            version=pr.version,
            name=format_user_id(me),
        )
    )
Exemple #4
0
async def test_get_org_and_repo():
    """get_org_and_repo should get the GitHub organization and repo from the directory"""
    for git_url in [
            "[email protected]:mitodl/release-script.git",
            "https://github.com/mitodl/release-script.git",
    ]:
        assert get_org_and_repo(git_url) == ("mitodl", "release-script")
Exemple #5
0
async def test_webhook_finish_release(doof, event_loop, mocker):
    """
    Finish the release
    """
    wait_for_deploy_sync_mock = Mock()

    async def wait_for_deploy_fake(*args, **kwargs):
        """await cannot be used with mock objects"""
        wait_for_deploy_sync_mock(*args, **kwargs)

    pr_body = ReleasePR(
        version='version',
        url='url',
        body='body',
    )
    get_release_pr_mock = mocker.patch('bot.get_release_pr',
                                       autospec=True,
                                       return_value=pr_body)
    finish_release_mock = mocker.patch('bot.finish_release', autospec=True)
    mocker.patch('bot.wait_for_deploy', wait_for_deploy_fake)

    await doof.handle_webhook(
        loop=event_loop,
        webhook_dict={
            "token": "token",
            "callback_id": FINISH_RELEASE_ID,
            "channel": {
                "id": "doof"
            },
            "user": {
                "id": "doofenshmirtz"
            },
            "message_ts": "123.45",
            "original_message": {
                "text": "Doof's original text",
            }
        },
    )

    repo_url = WEB_TEST_REPO_INFO.repo_url
    hash_url = WEB_TEST_REPO_INFO.prod_hash_url
    org, repo = get_org_and_repo(repo_url)
    wait_for_deploy_sync_mock.assert_any_call(
        github_access_token=doof.github_access_token,
        hash_url=hash_url,
        repo_url=repo_url,
        watch_branch='release',
    )
    get_release_pr_mock.assert_any_call(
        github_access_token=doof.github_access_token,
        org=org,
        repo=repo,
    )
    finish_release_mock.assert_any_call(
        github_access_token=doof.github_access_token,
        repo_url=repo_url,
        version=pr_body.version,
        timezone=doof.timezone)
    assert doof.said("Merging...")
    assert not doof.said("Error")
async def test_release_notes_no_new_notes(doof, test_repo, test_repo_directory, mocker):
    """Doof should show that there are no new commits"""
    mocker.patch(
        'bot.init_working_dir', side_effect=async_context_manager_yielder(test_repo_directory)
    )
    old_version = "0.1.2"
    update_version_mock = mocker.patch('bot.update_version', autospec=True, return_value=old_version)
    notes = "no new commits"
    create_release_notes_mock = mocker.async_patch('bot.create_release_notes', return_value=notes)
    org, repo = get_org_and_repo(test_repo.repo_url)
    get_release_pr_mock = mocker.async_patch('bot.get_release_pr', return_value=None)
    any_new_commits_mock = mocker.async_patch('bot.any_new_commits', return_value=False)

    await doof.run_command(
        manager='mitodl_user',
        channel_id=test_repo.channel_id,
        words=['release', 'notes'],
    )

    any_new_commits_mock.assert_called_once_with(old_version, base_branch="master", root=test_repo_directory)
    update_version_mock.assert_called_once_with("9.9.9", working_dir=test_repo_directory)
    create_release_notes_mock.assert_called_once_with(
        old_version, with_checkboxes=False, base_branch="master", root=test_repo_directory
    )
    get_release_pr_mock.assert_called_once_with(github_access_token=GITHUB_ACCESS, org=org, repo=repo)

    assert doof.said("Release notes since {}".format(old_version))
    assert not doof.said("Start a new release?")
Exemple #7
0
    async def wait_for_checkboxes(self, repo_info, manager):
        """
        Poll the Release PR and wait until all checkboxes are checked off

        Args:
            repo_info (RepoInfo): Information for a repo
            manager (str): User id for the release manager
        """
        channel_id = repo_info.channel_id
        await self.say(
            channel_id=channel_id,
            text=
            "Wait, wait. Time out. My evil plan for {project} isn't evil enough "
            "until all the checkboxes are checked...".format(
                project=repo_info.name, ))
        org, repo = get_org_and_repo(repo_info.repo_url)
        await wait_for_checkboxes(self.github_access_token, org, repo)
        pr = get_release_pr(self.github_access_token, org, repo)
        await self.say(
            channel_id=channel_id,
            text=
            "All checkboxes checked off. Release {version} is ready for the Merginator {name}!"
            .format(name=format_user_id(manager), version=pr.version),
            attachments=[{
                "fallback":
                "Finish the release",
                "callback_id":
                FINISH_RELEASE_ID,
                "actions": [{
                    "name": "finish_release",
                    "text": "Finish the release",
                    "type": "button",
                }]
            }])
async def test_wait_for_deploy_rc(doof, test_repo, mocker):
    """Bot._wait_for_deploy_prod should wait until repo has been deployed to RC"""
    wait_for_deploy_mock = mocker.async_patch('bot.wait_for_deploy')
    get_unchecked_patch = mocker.async_patch(
        'bot.get_unchecked_authors', return_value={'author1', 'author2', 'author3'}
    )
    org, repo = get_org_and_repo(test_repo.repo_url)
    release_pr = ReleasePR('version', f'https://github.com/{org}/{repo}/pulls/123456', 'body')
    get_release_pr_mock = mocker.async_patch('bot.get_release_pr', return_value=release_pr)

    await doof._wait_for_deploy_rc(repo_info=test_repo)  # pylint: disable=protected-access

    assert doof.said('These people have commits in this release')
    wait_for_deploy_mock.assert_called_once_with(
        github_access_token=GITHUB_ACCESS,
        repo_url=test_repo.repo_url,
        hash_url=test_repo.rc_hash_url,
        watch_branch='release-candidate'
    )
    get_unchecked_patch.assert_called_once_with(
        github_access_token=GITHUB_ACCESS,
        org=org,
        repo=repo,
    )
    get_release_pr_mock.assert_called_once_with(
        github_access_token=GITHUB_ACCESS,
        org=org,
        repo=repo,
    )
Exemple #9
0
async def test_release_library(doof, library_test_repo, event_loop, mocker):
    """Do a library release"""
    version = '1.2.3'
    pr = ReleasePR(
        version=version,
        url='http://new.url',
        body='Release PR body',
    )
    get_release_pr_mock = mocker.patch('bot.get_release_pr',
                                       autospec=True,
                                       side_effect=[None, pr, pr])
    release_mock = mocker.patch('bot.release', autospec=True)
    finish_release_mock = mocker.patch('bot.finish_release', autospec=True)

    wait_for_travis_sync_mock = mocker.Mock()
    wait_for_travis_sync_mock.return_value = TRAVIS_SUCCESS

    async def wait_for_travis_fake(*args, **kwargs):
        """await cannot be used with mock objects"""
        return wait_for_travis_sync_mock(*args, **kwargs)

    mocker.patch('bot.wait_for_travis', wait_for_travis_fake)

    command_words = ['release', version]
    me = 'mitodl_user'
    await doof.run_command(
        manager=me,
        channel_id=library_test_repo.channel_id,
        words=command_words,
        loop=event_loop,
    )

    org, repo = get_org_and_repo(library_test_repo.repo_url)
    release_mock.assert_called_once_with(
        github_access_token=GITHUB_ACCESS,
        repo_url=library_test_repo.repo_url,
        new_version=pr.version,
    )
    wait_for_travis_sync_mock.assert_called_once_with(
        github_access_token=GITHUB_ACCESS,
        org=org,
        repo=repo,
        branch='release-candidate',
    )
    get_release_pr_mock.assert_called_once_with(
        github_access_token=GITHUB_ACCESS,
        org=org,
        repo=repo,
    )
    finish_release_mock.assert_called_once_with(
        github_access_token=GITHUB_ACCESS,
        repo_url=library_test_repo.repo_url,
        version=version,
        timezone=doof.timezone)
    assert doof.said(
        "My evil scheme {version} for {project} has been merged!".format(
            version=pr.version,
            project=library_test_repo.name,
        ))
Exemple #10
0
async def test_release_library(doof, library_test_repo, mocker):
    """Do a library release"""
    version = '1.2.3'
    pr = ReleasePR(
        version=version,
        url='http://new.url',
        body='Release PR body',
    )
    get_release_pr_mock = mocker.async_patch('bot.get_release_pr', side_effect=[None, pr, pr])
    release_mock = mocker.async_patch('bot.release')
    finish_release_mock = mocker.async_patch('bot.finish_release')

    wait_for_travis_sync_mock = mocker.async_patch('bot.wait_for_travis', return_value=TRAVIS_SUCCESS)

    command_words = ['release', version]
    me = 'mitodl_user'
    await doof.run_command(
        manager=me,
        channel_id=library_test_repo.channel_id,
        words=command_words,
    )

    org, repo = get_org_and_repo(library_test_repo.repo_url)
    release_mock.assert_called_once_with(
        github_access_token=GITHUB_ACCESS,
        repo_url=library_test_repo.repo_url,
        new_version=pr.version,
    )
    wait_for_travis_sync_mock.assert_called_once_with(
        github_access_token=GITHUB_ACCESS,
        org=org,
        repo=repo,
        branch='release-candidate',
    )
    get_release_pr_mock.assert_called_once_with(
        github_access_token=GITHUB_ACCESS,
        org=org,
        repo=repo,
    )
    finish_release_mock.assert_called_once_with(
        github_access_token=GITHUB_ACCESS,
        repo_url=library_test_repo.repo_url,
        version=version,
        timezone=doof.timezone
    )
    assert doof.said(
        f"Merging evil scheme {pr.version} for {library_test_repo.name}..."
    )
    assert doof.said(
        f"My evil scheme {pr.version} for {library_test_repo.name} has been released! Waiting for Travis..."
    )
    assert doof.said(
        "My evil scheme {version} for {project} has been merged!".format(
            version=pr.version,
            project=library_test_repo.name,
        )
    )
def test_get_org_and_repo():
    """get_org_and_repo should get the GitHub organization and repo from the directory"""
    # I would be fine with testing this on cwd but Travis has a really old version of git that doesn't support
    # get-url
    for git_url in [
            "[email protected]:mitodl/release-script.git",
            "https://github.com/mitodl/release-script.git"
    ]:
        assert get_org_and_repo(git_url) == ("mitodl", "release-script")
Exemple #12
0
def url_with_access_token(github_access_token, repo_url):
    """
    Inserts the access token into the URL

    Returns:FILE_VERSION
        str: The URL formatted with an access token
    """
    org, repo = get_org_and_repo(repo_url)
    return f"https://{github_access_token}@github.com/{org}/{repo}.git"
Exemple #13
0
async def test_release(doof, test_repo, mocker, command):
    """
    Doof should do a release when asked
    """
    version = '1.2.3'
    pr = ReleasePR(
        version=version,
        url='http://new.url',
        body='Release PR body',
    )
    get_release_pr_mock = mocker.async_patch('bot.get_release_pr',
                                             side_effect=[None, pr, pr])
    release_mock = mocker.async_patch('bot.release')

    wait_for_deploy_sync_mock = mocker.async_patch('bot.wait_for_deploy')
    authors = {'author1', 'author2'}
    mocker.async_patch('bot.get_unchecked_authors', return_value=authors)

    wait_for_checkboxes_sync_mock = mocker.async_patch(
        'bot.Bot.wait_for_checkboxes')

    command_words = command.split() + [version]
    me = 'mitodl_user'
    await doof.run_command(
        manager=me,
        channel_id=test_repo.channel_id,
        words=command_words,
    )

    org, repo = get_org_and_repo(test_repo.repo_url)
    get_release_pr_mock.assert_any_call(
        github_access_token=GITHUB_ACCESS,
        org=org,
        repo=repo,
    )
    release_mock.assert_called_once_with(
        github_access_token=GITHUB_ACCESS,
        repo_url=test_repo.repo_url,
        new_version=pr.version,
    )
    wait_for_deploy_sync_mock.assert_called_once_with(
        github_access_token=GITHUB_ACCESS,
        repo_url=test_repo.repo_url,
        hash_url=test_repo.rc_hash_url,
        watch_branch='release-candidate',
    )
    assert doof.said("Now deploying to RC...")
    for channel_id in [test_repo.channel_id, ANNOUNCEMENTS_CHANNEL.channel_id]:
        assert doof.said(
            "These people have commits in this release",
            channel_id=channel_id,
        )
        for author in authors:
            assert doof.said(author, channel_id=channel_id)
    assert wait_for_checkboxes_sync_mock.called is True
Exemple #14
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))
Exemple #15
0
def url_with_access_token(github_access_token, repo_url):
    """
    Inserts the access token into the URL

    Returns:
        str: The URL formatted with an access token
    """
    org, repo = get_org_and_repo(repo_url)
    return "https://{token}@github.com/{org}/{repo}.git".format(
        token=github_access_token,
        org=org,
        repo=repo,
    )
Exemple #16
0
    async def _web_application_release(self, command_args):
        """Do a web application release"""
        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)

        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(
            github_access_token=self.github_access_token,
            org=org,
            repo=repo,
        )
        slack_usernames = self.translate_slack_usernames(unchecked_authors)
        pr = get_release_pr(
            github_access_token=self.github_access_token,
            org=org,
            repo=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))
Exemple #17
0
async def test_webhook_finish_release(doof, mocker):
    """
    Finish the release
    """
    pr_body = ReleasePR(
        version='version',
        url='url',
        body='body',
    )
    get_release_pr_mock = mocker.async_patch('bot.get_release_pr', return_value=pr_body)
    finish_release_mock = mocker.async_patch('bot.finish_release')
    wait_for_deploy_prod_mock = mocker.async_patch('bot.Bot._wait_for_deploy_prod')

    await doof.handle_webhook(
        webhook_dict={
            "token": "token",
            "callback_id": FINISH_RELEASE_ID,
            "channel": {
                "id": "doof"
            },
            "user": {
                "id": "doofenshmirtz"
            },
            "message_ts": "123.45",
            "original_message": {
                "text": "Doof's original text",
            }
        },
    )

    repo_url = WEB_TEST_REPO_INFO.repo_url
    org, repo = get_org_and_repo(repo_url)
    wait_for_deploy_prod_mock.assert_any_call(
        doof,
        repo_info=WEB_TEST_REPO_INFO,
    )
    get_release_pr_mock.assert_any_call(
        github_access_token=doof.github_access_token,
        org=org,
        repo=repo,
    )
    finish_release_mock.assert_any_call(
        github_access_token=doof.github_access_token,
        repo_url=repo_url,
        version=pr_body.version,
        timezone=doof.timezone
    )
    assert doof.said("Merging...")
    assert not doof.said("Error")
Exemple #18
0
async def test_finish_release(doof, test_repo, event_loop, mocker):
    """
    Doof should finish a release when asked
    """
    version = '1.2.3'
    pr = ReleasePR(
        version=version,
        url='http://new.url',
        body='Release PR body',
    )
    get_release_pr_mock = mocker.patch('bot.get_release_pr',
                                       autospec=True,
                                       return_value=pr)
    finish_release_mock = mocker.patch('bot.finish_release', autospec=True)

    wait_for_deploy_sync_mock = mocker.Mock()

    async def wait_for_deploy_fake(*args, **kwargs):
        """await cannot be used with mock objects"""
        wait_for_deploy_sync_mock(*args, **kwargs)

    mocker.patch('bot.wait_for_deploy', wait_for_deploy_fake)

    await doof.run_command(
        manager='mitodl_user',
        channel_id=test_repo.channel_id,
        words=['finish', 'release'],
        loop=event_loop,
    )

    org, repo = get_org_and_repo(test_repo.repo_url)
    get_release_pr_mock.assert_called_once_with(
        github_access_token=GITHUB_ACCESS,
        org=org,
        repo=repo,
    )
    finish_release_mock.assert_called_once_with(
        github_access_token=GITHUB_ACCESS,
        repo_url=test_repo.repo_url,
        version=version,
        timezone=doof.timezone)
    assert doof.said('deploying to production...')
    wait_for_deploy_sync_mock.assert_called_once_with(
        github_access_token=GITHUB_ACCESS,
        repo_url=test_repo.repo_url,
        hash_url=test_repo.prod_hash_url,
        watch_branch='release',
    )
    assert doof.said('has been released to production')
Exemple #19
0
async def test_release_library_failure(doof, library_test_repo, event_loop,
                                       mocker):
    """If a library release fails we shouldn't merge it"""
    version = '1.2.3'
    pr = ReleasePR(
        version=version,
        url='http://new.url',
        body='Release PR body',
    )
    mocker.patch('bot.get_release_pr',
                 autospec=True,
                 side_effect=[None, pr, pr])
    release_mock = mocker.patch('bot.release', autospec=True)
    finish_release_mock = mocker.patch('bot.finish_release', autospec=True)

    wait_for_travis_sync_mock = mocker.Mock()
    wait_for_travis_sync_mock.return_value = TRAVIS_FAILURE

    async def wait_for_travis_fake(*args, **kwargs):
        """await cannot be used with mock objects"""
        return wait_for_travis_sync_mock(*args, **kwargs)

    mocker.patch('bot.wait_for_travis', wait_for_travis_fake)

    command_words = ['release', version]
    me = 'mitodl_user'
    await doof.run_command(
        manager=me,
        channel_id=library_test_repo.channel_id,
        words=command_words,
        loop=event_loop,
    )

    org, repo = get_org_and_repo(library_test_repo.repo_url)
    release_mock.assert_called_once_with(
        github_access_token=GITHUB_ACCESS,
        repo_url=library_test_repo.repo_url,
        new_version=pr.version,
    )
    wait_for_travis_sync_mock.assert_called_once_with(
        github_access_token=GITHUB_ACCESS,
        org=org,
        repo=repo,
        branch='release-candidate',
    )
    assert finish_release_mock.call_count == 0
    assert doof.said(
        "Uh-oh, it looks like, uh, coffee break's over. During the release Travis had a failure."
    )
async def test_finish_release_no_release(doof, repo_info, event_loop, mocker):
    """
    If there's no release to finish doof should complain
    """
    get_release_pr_mock = mocker.patch('bot.get_release_pr', autospec=True, return_value=None)
    with pytest.raises(ReleaseException) as ex:
        await doof.run_command(
            manager='mitodl_user',
            channel_id=repo_info.channel_id,
            words=['finish', 'release'],
            loop=event_loop,
        )
    assert 'No release currently in progress' in ex.value.args[0]
    org, repo = get_org_and_repo(repo_info.repo_url)
    get_release_pr_mock.assert_called_once_with(GITHUB_ACCESS, org, repo)
Exemple #21
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,
            )
        )
Exemple #22
0
async def test_finish_release_no_release(doof, test_repo, mocker):
    """
    If there's no release to finish doof should complain
    """
    get_release_pr_mock = mocker.async_patch('bot.get_release_pr', return_value=None)
    with pytest.raises(ReleaseException) as ex:
        await doof.run_command(
            manager='mitodl_user',
            channel_id=test_repo.channel_id,
            words=['finish', 'release'],
        )
    assert 'No release currently in progress' in ex.value.args[0]
    org, repo = get_org_and_repo(test_repo.repo_url)
    get_release_pr_mock.assert_called_once_with(
        github_access_token=GITHUB_ACCESS,
        org=org,
        repo=repo,
    )
Exemple #23
0
    async def _library_release(self, command_args):
        """Do a library release"""
        repo_info = command_args.repo_info
        version = command_args.args[0]
        repo_url = repo_info.repo_url
        channel_id = repo_info.channel_id

        release(
            github_access_token=self.github_access_token,
            repo_url=repo_url,
            new_version=version,
        )

        org, repo = get_org_and_repo(repo_url)
        status = await wait_for_travis(
            github_access_token=self.github_access_token,
            org=org,
            repo=repo,
            branch="release-candidate",
        )
        if status != TRAVIS_SUCCESS:
            await self.say(
                channel_id=channel_id,
                text="Uh-oh, it looks like, uh, coffee break's over. During the release Travis had a {}.".format(
                    status,
                )
            )
            return

        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="My evil scheme {version} for {project} has been merged!".format(
                version=version,
                project=repo_info.name,
            )
        )
Exemple #24
0
    async def message_if_unchecked(self, repo_info):
        """
        Send a message next morning if any boxes are not yet checked off

        Args:
            repo_info (RepoInfo): Information for a repo
        """
        org, repo = get_org_and_repo(repo_info.repo_url)
        unchecked_authors = get_unchecked_authors(self.github_access_token,
                                                  org, repo)
        if unchecked_authors:
            slack_usernames = self.translate_slack_usernames(unchecked_authors)
            await self.say(
                channel_id=repo_info.channel_id,
                text="What an unexpected surprise! "
                "The following authors have not yet checked off their boxes for {project}: {names}"
                .format(
                    names=", ".join(slack_usernames),
                    project=repo_info.name,
                ))
Exemple #25
0
async def test_release_notes_buttons(doof, test_repo, test_repo_directory, mocker):
    """Doof should show release notes and then offer buttons to start a release"""
    mocker.patch(
        'bot.init_working_dir', side_effect=async_context_manager_yielder(test_repo_directory)
    )
    old_version = "0.1.2"
    update_version_mock = mocker.patch('bot.update_version', autospec=True, return_value=old_version)
    notes = "some notes"
    create_release_notes_mock = mocker.async_patch('bot.create_release_notes', return_value=notes)
    org, repo = get_org_and_repo(test_repo.repo_url)
    get_release_pr_mock = mocker.async_patch('bot.get_release_pr', return_value=None)
    any_new_commits_mock = mocker.async_patch('bot.any_new_commits', return_value=True)

    await doof.run_command(
        manager='mitodl_user',
        channel_id=test_repo.channel_id,
        words=['release', 'notes'],
    )

    any_new_commits_mock.assert_called_once_with(old_version, base_branch="master", root=test_repo_directory)
    update_version_mock.assert_called_once_with("9.9.9", working_dir=test_repo_directory)
    create_release_notes_mock.assert_called_once_with(
        old_version, with_checkboxes=False, base_branch="master", root=test_repo_directory
    )
    get_release_pr_mock.assert_called_once_with(github_access_token=GITHUB_ACCESS, org=org, repo=repo)

    assert doof.said("Release notes since {}".format(old_version))
    assert doof.said(notes)
    minor_version, patch_version = next_versions(old_version)
    assert doof.said("Start a new release?", attachments=[
        {
            'fallback': 'New release',
            'callback_id': 'new_release',
            'actions': [
                {'name': 'minor_release', 'text': minor_version, 'value': minor_version, 'type': 'button'},
                {'name': 'patch_release', 'text': patch_version, 'value': patch_version, 'type': 'button'},
                {'name': 'cancel', 'text': "Dismiss", 'value': "cancel", 'type': 'button', "style": "danger"}
            ]
        }
    ])
    assert not doof.said(f"And also! There is a release already in progress")
Exemple #26
0
async def test_release_library_failure(doof, library_test_repo, mocker):
    """If a library release fails we shouldn't merge it"""
    version = '1.2.3'
    pr = ReleasePR(
        version=version,
        url='http://new.url',
        body='Release PR body',
    )
    mocker.async_patch('bot.get_release_pr', side_effect=[None, pr, pr])
    release_mock = mocker.async_patch('bot.release')
    finish_release_mock = mocker.async_patch('bot.finish_release')

    wait_for_travis_sync_mock = mocker.async_patch('bot.wait_for_travis', return_value=TRAVIS_FAILURE)

    command_words = ['release', version]
    me = 'mitodl_user'
    await doof.run_command(
        manager=me,
        channel_id=library_test_repo.channel_id,
        words=command_words,
    )

    org, repo = get_org_and_repo(library_test_repo.repo_url)
    release_mock.assert_called_once_with(
        github_access_token=GITHUB_ACCESS,
        repo_url=library_test_repo.repo_url,
        new_version=pr.version,
    )
    wait_for_travis_sync_mock.assert_called_once_with(
        github_access_token=GITHUB_ACCESS,
        org=org,
        repo=repo,
        branch='release-candidate',
    )
    assert finish_release_mock.call_count == 0
    assert doof.said(
        f"Merging evil scheme {pr.version} for {library_test_repo.name}..."
    )
    assert doof.said(
        "Uh-oh, it looks like, uh, coffee break's over. During the release Travis had a failure."
    )
Exemple #27
0
async def test_finish_release(doof, mocker, project_type):
    """
    Doof should finish a release when asked
    """
    version = '1.2.3'
    pr = ReleasePR(
        version=version,
        url='http://new.url',
        body='Release PR body',
    )
    get_release_pr_mock = mocker.async_patch('bot.get_release_pr',
                                             return_value=pr)
    finish_release_mock = mocker.async_patch('bot.finish_release')

    wait_for_deploy_prod_mock = mocker.async_patch(
        'bot.Bot._wait_for_deploy_prod')

    test_repo = LIBRARY_TEST_REPO_INFO if project_type == LIBRARY_TYPE else WEB_TEST_REPO_INFO

    await doof.run_command(
        manager='mitodl_user',
        channel_id=test_repo.channel_id,
        words=['finish', 'release'],
    )

    org, repo = get_org_and_repo(test_repo.repo_url)
    get_release_pr_mock.assert_called_once_with(
        github_access_token=GITHUB_ACCESS,
        org=org,
        repo=repo,
    )
    finish_release_mock.assert_called_once_with(
        github_access_token=GITHUB_ACCESS,
        repo_url=test_repo.repo_url,
        version=version,
        timezone=doof.timezone)
    assert doof.said(f"Merged evil scheme {version} for {test_repo.name}!")
    if project_type == WEB_APPLICATION_TYPE:
        assert doof.said('deploying to production...')
        wait_for_deploy_prod_mock.assert_called_once_with(doof,
                                                          repo_info=test_repo)
Exemple #28
0
async def test_webhook_start_release(doof, test_repo, mocker):
    """
    Start a new release
    """
    org, repo = get_org_and_repo(test_repo.repo_url)
    get_release_pr_mock = mocker.async_patch('bot.get_release_pr',
                                             return_value=None)

    release_mock = mocker.async_patch('bot.Bot._web_application_release')

    version = "3.4.5"
    await doof.handle_webhook(webhook_dict={
        "token":
        "token",
        "callback_id":
        NEW_RELEASE_ID,
        "channel": {
            "id": "doof"
        },
        "user": {
            "id": "doofenshmirtz"
        },
        "message_ts":
        "123.45",
        "original_message": {
            "text": "Doof's original text",
        },
        "actions": [{
            "value": version,
            "name": "minor_release",
        }]
    }, )

    assert doof.said(f"Starting release {version}...")
    assert release_mock.call_count == 1
    assert release_mock.call_args[0][1].args == [version]
    assert not doof.said("Error")
    get_release_pr_mock.assert_called_once_with(
        github_access_token=GITHUB_ACCESS, org=org, repo=repo)
Exemple #29
0
async def test_finish_release(doof, test_repo, mocker):
    """
    Doof should finish a release when asked
    """
    version = '1.2.3'
    pr = ReleasePR(
        version=version,
        url='http://new.url',
        body='Release PR body',
    )
    get_release_pr_mock = mocker.async_patch('bot.get_release_pr', return_value=pr)
    finish_release_mock = mocker.async_patch('bot.finish_release')

    wait_for_deploy_prod_mock = mocker.async_patch('bot.Bot._wait_for_deploy_prod')

    await doof.run_command(
        manager='mitodl_user',
        channel_id=test_repo.channel_id,
        words=['finish', 'release'],
    )

    org, repo = get_org_and_repo(test_repo.repo_url)
    get_release_pr_mock.assert_called_once_with(
        github_access_token=GITHUB_ACCESS,
        org=org,
        repo=repo,
    )
    finish_release_mock.assert_called_once_with(
        github_access_token=GITHUB_ACCESS,
        repo_url=test_repo.repo_url,
        version=version,
        timezone=doof.timezone
    )
    assert doof.said('deploying to production...')
    wait_for_deploy_prod_mock.assert_called_once_with(
        doof,
        repo_info=test_repo
    )
Exemple #30
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))