Exemple #1
0
def build_package_from_pr_number(gh_token, sdk_id, pr_number, output_folder, *, with_comment=False):
    """Will clone the given PR branch and vuild the package with the given name."""

    con = Github(gh_token)
    repo = con.get_repo(sdk_id)
    sdk_pr = repo.get_pull(pr_number)
    package_names = get_package_names(sdk_pr)

    absolute_output_folder = Path(output_folder).resolve()

    with tempfile.TemporaryDirectory() as temp_dir, manage_git_folder(
        gh_token, Path(temp_dir) / Path("sdk"), sdk_id, pr_number=pr_number
    ) as sdk_repo_root:

        for _, package_name in package_names:
            _LOGGER.debug("Build {}".format(package_name))
            execute_simple_command(
                ["python", "./build_package.py", "--dest", str(absolute_output_folder), package_name], cwd=sdk_repo_root
            )
            _LOGGER.debug("Build finished: {}".format(package_name))

    if with_comment:
        files = [f.name for f in absolute_output_folder.iterdir()]
        comment_message = None
        dashboard = DashboardCommentableObject(sdk_pr, "(message created by the CI based on PR content)")
        try:
            installation_message = build_installation_message(sdk_pr)
            download_message = build_download_message(sdk_pr, files)
            comment_message = installation_message + "\n\n" + download_message
            dashboard.create_comment(comment_message)
        except Exception:
            _LOGGER.critical("Unable to do PR comment:\n%s", comment_message)
def build_package_from_pr_number(gh_token, sdk_id, pr_number, output_folder, *, with_comment=False):
    """Will clone the given PR branch and vuild the package with the given name."""

    con = Github(gh_token)
    repo = con.get_repo(sdk_id)
    sdk_pr = repo.get_pull(pr_number)
    # "get_files" of Github only download the first 300 files. Might not be enough.
    package_names = {f.filename.split('/')[0] for f in sdk_pr.get_files() if f.filename.startswith("azure")}
    absolute_output_folder = Path(output_folder).resolve()

    with tempfile.TemporaryDirectory() as temp_dir, \
            manage_git_folder(gh_token, Path(temp_dir) / Path("sdk"), sdk_id, pr_number=pr_number) as sdk_folder:

        for package_name in package_names:
            _LOGGER.debug("Build {}".format(package_name))
            execute_simple_command(
                ["python", "./build_package.py", "--dest", str(absolute_output_folder), package_name],
                cwd=sdk_folder
            )
            _LOGGER.debug("Build finished: {}".format(package_name))

    if with_comment:
        files = [f.name for f in absolute_output_folder.iterdir()]
        comment_message = None
        dashboard = DashboardCommentableObject(sdk_pr, "(message created by the CI based on PR content)")
        try:
            installation_message = build_installation_message(sdk_pr)
            download_message = build_download_message(sdk_pr, files)
            comment_message = installation_message + "\n\n" + download_message
            dashboard.create_comment(comment_message)
        except Exception:
            _LOGGER.critical("Unable to do PR comment:\n%s", comment_message)
    def test_dashboard(self, format_exc):
        format_exc.return_value = "something to do with an exception"

        # Prepare
        repo = self.g.get_repo("lmazuel/TestingRepo")
        issue = repo.get_issue(15)
        initial_size = len(list(issue.get_comments()))
        header = "# MYHEADER"

        dashboard = DashboardCommentableObject(issue, header)

        with exception_to_github(dashboard, "Python bot") as error:
            "Test".fakemethod(12)  # pylint: disable=no-member

        after_size = len(list(issue.get_comments()))
        assert after_size == initial_size + 1

        assert error.comment is not None
        assert "Encountered an unknown error" in error.comment.body

        dashboard.create_comment("New text comment")
        after_size_2 = len(list(issue.get_comments()))
        assert after_size == after_size_2

        # Clean my mess
        error.comment.delete()
Exemple #4
0
def build_package_from_pr_number(gh_token,
                                 sdk_id,
                                 pr_number,
                                 output_folder,
                                 *,
                                 with_comment=False):
    """Will clone the given PR branch and vuild the package with the given name."""

    con = Github(gh_token)
    repo = con.get_repo(sdk_id)
    sdk_pr = repo.get_pull(pr_number)
    files = [
        one_file.filename for one_file in sdk_pr.get_files()
        if one_file.status not in ['removed']
    ]
    # "get_files" of Github only download the first 300 files. Might not be enough.
    package_names = {('.', f.split('/')[0])
                     for f in files if f.startswith("azure")}
    # Handle the SDK folder as well
    matches = {_SDK_FOLDER_RE.search(f) for f in files}
    package_names.update(
        {match.groups()
         for match in matches if match is not None})

    absolute_output_folder = Path(output_folder).resolve()

    with tempfile.TemporaryDirectory() as temp_dir, \
            manage_git_folder(gh_token, Path(temp_dir) / Path("sdk"), sdk_id, pr_number=pr_number) as sdk_repo_root:

        for _, package_name in package_names:
            _LOGGER.debug("Build {}".format(package_name))
            execute_simple_command([
                "python", "./build_package.py", "--dest",
                str(absolute_output_folder), package_name
            ],
                                   cwd=sdk_repo_root)
            _LOGGER.debug("Build finished: {}".format(package_name))

    if with_comment:
        files = [f.name for f in absolute_output_folder.iterdir()]
        comment_message = None
        dashboard = DashboardCommentableObject(
            sdk_pr, "(message created by the CI based on PR content)")
        try:
            installation_message = build_installation_message(sdk_pr)
            download_message = build_download_message(sdk_pr, files)
            comment_message = installation_message + "\n\n" + download_message
            dashboard.create_comment(comment_message)
        except Exception:
            _LOGGER.critical("Unable to do PR comment:\n%s", comment_message)
def rest_pr_management(rest_pr,
                       sdk_repo,
                       sdk_tag,
                       sdk_default_base=_DEFAULT_SDK_BRANCH):
    """What to do when something happen to a PR in the Rest repo.

    :param restpr: a PyGithub pull object
    :type restpr: github.PullRequest.PullRequest
    :param sdk_repo: a PyGithub repository
    :type sdk_repo: github.Repository.Repository
    :param str sdk_tag: repotag to use to filter SwaggerToSDK conf
    :param str sdk_default_base: Default SDK branch.
    """
    # Extract some metadata as variables
    rest_repo = rest_pr.base.repo
    # "repo" can be None if fork has been deleted.
    is_from_a_fork = rest_pr.head.repo is None or rest_pr.head.repo.full_name != rest_repo.full_name

    # THE comment were we put everything
    dashboard = DashboardCommentableObject(
        rest_pr, "# Automation for {}".format(sdk_tag))

    #
    # Work on context, ext if context is not good
    #
    context_tags = list(get_context_tag_from_git_object(rest_pr))
    if not context_tags:
        dashboard.create_comment(
            "Unable to detect any generation context from this PR.")
        return
    if len(context_tags) > _CONTEXT_TAG_LIMITS:
        dashboard.create_comment(
            "This PR contains more than {} context, SDK generation is not enabled. Contexts found:\n{}"
            .format(_CONTEXT_TAG_LIMITS,
                    "\n".join(["- {}".format(ctxt) for ctxt in context_tags])))
        return

    #
    # Decide if this PR will use a context branch
    #
    # A RestPR will have a context branch if:
    # - This is from a fork. Local branch are considered of the context system.
    # - There is one context only. Too much complicated to handle two context branches.
    # - Base is master. If fork to a feature branch, keep that flow.
    is_pushed_to_context_branch = is_from_a_fork and len(
        context_tags) == 1 and rest_pr.base.ref == _DEFAULT_REST_BRANCH

    #
    # Compute the "head" of future SDK PR.
    #
    if is_from_a_fork:
        sdk_pr_head = _SDK_PR_TEMPLATE.format(rest_pr.number)
    else:
        sdk_pr_head = _SDK_PR_TEMPLATE.format(rest_pr.head.ref)

    #
    # Compute the "base" of future SDK PR.
    # "sdk_checkout_bases" is an ordered list of branchs to checkout, since this can be several
    # branches that derives from "master".
    # This are branches that SwaggerToSDK should have "push" permission. Do NOT add "master" or a protected
    # branch to that list.
    #
    sdk_checkout_bases = []
    if rest_pr.base.ref == _DEFAULT_REST_BRANCH:
        sdk_pr_base = sdk_default_base
    else:
        sdk_pr_base = _SDK_PR_TEMPLATE.format(rest_pr.base.ref)
        sdk_checkout_bases.append(sdk_pr_base)

    # In special case where I use context branch
    if is_pushed_to_context_branch:
        sdk_pr_base = _SDK_PR_TEMPLATE.format(context_tags[0])
        sdk_checkout_bases.insert(0, sdk_pr_base)

    #
    # Try to generate on "head", whatever the state of the PR.
    #
    generate_sdk_from_git_object(
        rest_pr,
        sdk_pr_head,
        None,  # We don't need repo id if it's a PR, infer from PR itself.
        sdk_repo.full_name,
        sdk_checkout_bases,
        fallback_base_branch_name=sdk_default_base,
        sdk_tag=sdk_tag)

    #
    # Try to create/get a SDK PR.
    #
    # There is a lot of reasons why a SDK PR could not exist even on a "close" event, so don't assume this exists.
    #
    try:
        sdk_pr = get_or_create_pull(
            sdk_repo,
            title='[AutoPR {}] {}'.format("/".join(context_tags),
                                          rest_pr.title),
            body="Created to sync {}".format(rest_pr.html_url),
            head=sdk_repo.owner.login + ":" + sdk_pr_head,
            base=sdk_pr_base,
        )
    except Exception as err:
        _LOGGER.warning("Unable to create SDK PR: %s", err)
        dashboard.create_comment("Nothing to generate for {}".format(sdk_tag))
        return

    # Replace whatever message it was if we were able to do a PR
    dashboard.create_comment("A PR has been created for you:\n{}".format(
        sdk_pr.html_url))

    #
    # Manage labels/state on this SDK PR.
    #
    sdk_pr_as_issue = sdk_repo.get_issue(sdk_pr.number)
    sdk_pr_merged = False
    if rest_pr.closed_at:  # If there is a date, this is closed
        head_ref = sdk_repo.get_git_ref("heads/{}".format(sdk_pr_head))
        if rest_pr.merged:
            manage_labels(sdk_pr_as_issue,
                          to_add=[SwaggerToSdkLabels.merged],
                          to_remove=[SwaggerToSdkLabels.in_progress])
            if sdk_pr.base.ref.startswith(_SDK_PR_PREFIX):
                try:
                    # Merge "single context PRs" automatically
                    sdk_pr.merge(merge_method="squash")
                    sdk_pr_merged = True
                    # Delete branch from merged PR
                    head_ref.delete()
                except Exception as err:
                    _LOGGER.warning("Was unable to merge: %s", err)
        else:
            manage_labels(sdk_pr_as_issue,
                          to_add=[SwaggerToSdkLabels.refused],
                          to_remove=[SwaggerToSdkLabels.in_progress])
            sdk_pr.edit(state="closed")
            # Delete branch from closed PR
            head_ref.delete()
    else:
        # Try to remove "refused", if it was re-opened
        manage_labels(sdk_pr_as_issue,
                      to_add=[SwaggerToSdkLabels.in_progress],
                      to_remove=[SwaggerToSdkLabels.refused])

    #
    # Extra work: if this was a context branch
    #
    if is_pushed_to_context_branch:
        try:
            context_pr = get_or_create_pull(
                sdk_repo,
                title='[AutoPR] {}'.format("/".join(context_tags)),
                body="Created to accumulate context: {}".format(
                    context_tags[0]),
                head=sdk_repo.owner.login + ":" + sdk_pr_base,
                base=sdk_default_base,
            )
        except Exception as err:
            _LOGGER.warning("Unable to create context PR: %s", err)
            return
        # We got the context PR!
        context_pr_as_issue = sdk_repo.get_issue(context_pr.number)
        manage_labels(context_pr_as_issue, [SwaggerToSdkLabels.service_pr])
        # Put a link into the SDK single PR
        if sdk_pr_merged:
            sdk_pr.create_issue_comment(
                "This PR has been merged into {}".format(context_pr.html_url))
        # Update dashboar to talk about this PR
        if sdk_pr.merged:
            msg = "The initial [PR]({}) has been merged into your service PR:\n{}".format(
                sdk_pr.html_url, context_pr.html_url)
        else:
            msg = "A [PR]({}) has been created for you based on this PR content.\n\n".format(
                sdk_pr.html_url)
            msg += "Once this PR will be merged, content will be added to your service PR:\n{}".format(
                context_pr.html_url)
        dashboard.create_comment(msg)
def rest_pr_management(rest_pr, sdk_repo, sdk_tag, sdk_default_base=_DEFAULT_SDK_BRANCH):
    """What to do when something happen to a PR in the Rest repo.

    :param restpr: a PyGithub pull object
    :type restpr: github.PullRequest.PullRequest
    :param sdk_repo: a PyGithub repository
    :type sdk_repo: github.Repository.Repository
    :param str sdk_tag: repotag to use to filter SwaggerToSDK conf
    :param str sdk_default_base: Default SDK branch.
    """
    # Extract some metadata as variables
    rest_repo = rest_pr.base.repo
    # "repo" can be None if fork has been deleted.
    is_from_a_fork = rest_pr.head.repo is None or rest_pr.head.repo.full_name != rest_repo.full_name

    # THE comment were we put everything
    dashboard = DashboardCommentableObject(rest_pr, "# Automation for {}".format(sdk_tag))

    #
    # Work on context, ext if context is not good
    #
    context_tags = list(get_context_tag_from_git_object(rest_pr))
    if not context_tags:
        dashboard.create_comment("Unable to detect any generation context from this PR.")
        return
    if len(context_tags) > _CONTEXT_TAG_LIMITS:
        dashboard.create_comment(
            "This PR contains more than {} context, SDK generation is not enabled. Contexts found:\n{}".format(
                _CONTEXT_TAG_LIMITS,
                "\n".join(["- {}".format(ctxt) for ctxt in context_tags])
            ))
        return

    #
    # Decide if this PR will use a context branch
    #
    # A RestPR will have a context branch if:
    # - This is from a fork. Local branch are considered of the context system.
    # - There is one context only. Too much complicated to handle two context branches.
    # - Base is master. If fork to a feature branch, keep that flow.
    is_pushed_to_context_branch = is_from_a_fork and len(context_tags) == 1 and rest_pr.base.ref == _DEFAULT_REST_BRANCH

    #
    # Compute the "head" of future SDK PR.
    #
    if is_from_a_fork:
        sdk_pr_head = _SDK_PR_TEMPLATE.format(rest_pr.number)
    else:
        sdk_pr_head = _SDK_PR_TEMPLATE.format(rest_pr.head.ref)

    #
    # Compute the "base" of future SDK PR.
    # "sdk_checkout_bases" is an ordered list of branchs to checkout, since this can be several
    # branches that derives from "master".
    # This are branches that SwaggerToSDK should have "push" permission. Do NOT add "master" or a protected
    # branch to that list.
    #
    sdk_checkout_bases = []
    if rest_pr.base.ref == _DEFAULT_REST_BRANCH:
        sdk_pr_base = sdk_default_base
    else:
        sdk_pr_base = _SDK_PR_TEMPLATE.format(rest_pr.base.ref)
        sdk_checkout_bases.append(sdk_pr_base)

    # In special case where I use context branch
    if is_pushed_to_context_branch:
        sdk_pr_base = _SDK_PR_TEMPLATE.format(context_tags[0])
        sdk_checkout_bases.insert(0, sdk_pr_base)

    #
    # Try to generate on "head", whatever the state of the PR.
    #
    generate_sdk_from_git_object(
        rest_pr,
        sdk_pr_head,
        None,  # We don't need repo id if it's a PR, infer from PR itself.
        sdk_repo.full_name,
        sdk_checkout_bases,
        fallback_base_branch_name=sdk_default_base,
        sdk_tag=sdk_tag
    )

    #
    # Try to create/get a SDK PR.
    #
    # There is a lot of reasons why a SDK PR could not exist even on a "close" event, so don't assume this exists.
    #
    try:
        sdk_pr = get_or_create_pull(
            sdk_repo,
            title='[AutoPR {}] {}'.format("/".join(context_tags), rest_pr.title),
            body="Created to sync {}".format(rest_pr.html_url),
            head=sdk_repo.owner.login+":"+sdk_pr_head,
            base=sdk_pr_base,
        )
    except Exception as err:
        _LOGGER.warning("Unable to create SDK PR: %s", err)
        dashboard.create_comment("Nothing to generate for {}".format(sdk_tag))
        return

    # Replace whatever message it was if we were able to do a PR
    dashboard.create_comment("A PR has been created for you:\n{}".format(sdk_pr.html_url))

    #
    # Manage labels/state on this SDK PR.
    #
    sdk_pr_as_issue = sdk_repo.get_issue(sdk_pr.number)
    sdk_pr_merged = False
    if rest_pr.closed_at:  # If there is a date, this is closed
        head_ref = sdk_repo.get_git_ref("heads/{}".format(sdk_pr_head))
        if rest_pr.merged:
            manage_labels(sdk_pr_as_issue,
                          to_add=[SwaggerToSdkLabels.merged],
                          to_remove=[SwaggerToSdkLabels.in_progress])
            if sdk_pr.base.ref.startswith(_SDK_PR_PREFIX):
                try:
                    # Merge "single context PRs" automatically
                    sdk_pr.merge(merge_method="squash")
                    sdk_pr_merged = True
                    # Delete branch from merged PR
                    head_ref.delete()
                except Exception as err:
                    _LOGGER.warning("Was unable to merge: %s", err)
        else:
            manage_labels(sdk_pr_as_issue,
                          to_add=[SwaggerToSdkLabels.refused],
                          to_remove=[SwaggerToSdkLabels.in_progress])
            sdk_pr.edit(state="closed")
            # Delete branch from closed PR
            head_ref.delete()
    else:
        # Try to remove "refused", if it was re-opened
        manage_labels(sdk_pr_as_issue,
                      to_add=[SwaggerToSdkLabels.in_progress],
                      to_remove=[SwaggerToSdkLabels.refused])

    #
    # Extra work: if this was a context branch
    #
    if is_pushed_to_context_branch:
        try:
            context_pr = get_or_create_pull(
                sdk_repo,
                title='[AutoPR] {}'.format("/".join(context_tags)),
                body="Created to accumulate context: {}".format(context_tags[0]),
                head=sdk_repo.owner.login+":"+sdk_pr_base,
                base=sdk_default_base,
            )
        except Exception as err:
            _LOGGER.warning("Unable to create context PR: %s", err)
            return
        # We got the context PR!
        context_pr_as_issue = sdk_repo.get_issue(context_pr.number)
        manage_labels(context_pr_as_issue, [SwaggerToSdkLabels.service_pr])
        # Put a link into the SDK single PR
        if sdk_pr_merged:
            sdk_pr.create_issue_comment("This PR has been merged into {}".format(context_pr.html_url))
        # Update dashboar to talk about this PR
        if sdk_pr.merged:
            msg = "The initial [PR]({}) has been merged into your service PR:\n{}".format(
                sdk_pr.html_url,
                context_pr.html_url
            )
        else:
            msg = "A [PR]({}) has been created for you based on this PR content.\n\n".format(
                sdk_pr.html_url
            )
            msg += "Once this PR will be merged, content will be added to your service PR:\n{}".format(
                context_pr.html_url
            )
        dashboard.create_comment(msg)