Esempio n. 1
0
    def _set_pr_labels(self, pr_event, resources) -> bool:
        '''
        @ return True if the required label was set
        '''
        required_labels = {
            resource.source.get('label')
            for resource in resources
            if resource.source.get('label') is not None
        }
        if not required_labels:
            return False
        repo = pr_event.repository()
        repository_path = repo.repository_path()
        pr_number = pr_event.number()

        github_cfg = self.cfg_set.github()
        owner, name = repository_path.split('/')
        github_helper = GitHubRepositoryHelper(
            owner,
            name,
            github_cfg=github_cfg,
        )
        sender_login = pr_event.sender()['login']
        if pr_event.action() is PullRequestAction.OPENED:
            if github_helper.is_pr_created_by_org_member(pr_number):
                logger.info(
                    f"New pull request by member of '{owner}' in '{repository_path}' found. "
                    f"Setting required labels '{required_labels}'.")
                github_helper.add_labels_to_pull_request(
                    pr_number, *required_labels)
                return True
            else:
                logger.debug(
                    f"New pull request by member in '{repository_path}' found, but creator is not "
                    f"member of '{owner}' - will not set required labels.")
                github_helper.add_comment_to_pr(
                    pull_request_number=pr_number,
                    comment=
                    (f"Thank you @{sender_login} for your contribution. Before I can start "
                     "building your PR, a member of the organization must set the required "
                     f"label(s) {required_labels}. Once started, you can check the build "
                     "status in the PR checks section below."))
                return False
        elif pr_event.action() is PullRequestAction.SYNCHRONIZE:
            if github_helper.is_org_member(organization_name=owner,
                                           user_login=sender_login):
                logger.info(
                    f"Update to pull request #{pr_event.number()} by org member '{sender_login}' "
                    f" in '{repository_path}' found. Setting required labels '{required_labels}'."
                )
                github_helper.add_labels_to_pull_request(
                    pr_number, *required_labels)
                return True
            else:
                logger.debug(
                    f"Update to pull request #{pr_event.number()} by '{sender_login}' "
                    f" in '{repository_path}' found. Ignoring, since they are not an org member'."
                )
                return False
        return False
Esempio n. 2
0
    def _set_pr_labels(self, pr_event, resources):
        required_labels = {
            resource.source.get('label')
            for resource in resources if resource.source.get('label') is not None
        }
        repo = pr_event.repository()
        repository_path = repo.repository_path()
        pr_number = pr_event.number()

        github_cfg = self.cfg_set.github()
        owner, name = repository_path.split('/')
        github_helper = GitHubRepositoryHelper(
            owner,
            name,
            github_cfg=github_cfg,
        )
        if github_helper.is_pr_created_by_org_member(pr_number):
            app.logger.info(
                f"New pull request by member of '{owner}' in '{repository_path}' found. "
                f"Setting required labels '{required_labels}'."
            )
            github_helper.add_labels_to_pull_request(pr_number, *required_labels)
        else:
            app.logger.debug(
                f"New pull request by member in '{repository_path}' found, but creator is not "
                f"member of '{owner}' - will not set required labels."
            )
Esempio n. 3
0
def delete_releases(
    github_cfg_name: str,
    github_repository_owner: str,
    github_repository_name: str,
    release_name: [str],
):
    github_cfg = ctx().cfg_factory().github(github_cfg_name)
    github_helper = GitHubRepositoryHelper(
        owner=github_repository_owner,
        name=github_repository_name,
        github_cfg=github_cfg,
    )
    github_helper.delete_releases(release_names=release_name)
Esempio n. 4
0
def fetch_release_notes_from_prs(
        github_helper: GitHubRepositoryHelper,
        pr_numbers_in_range: typing.Set[str],
        cn_current_repo: ComponentName) -> [ReleaseNote]:
    # we should consider adding a release-note label to the PRs
    # to reduce the number of search results
    prs_iter = github_helper.search_issues_in_repo('type:pull is:closed')

    release_notes = list()
    for pr_iter in prs_iter:
        pr_dict = pr_iter.as_dict()

        pr_number = str(pr_dict['number'])
        if pr_number not in pr_numbers_in_range:
            continue

        release_notes_pr = extract_release_notes(
            reference_id=pr_number,
            text=pr_dict['body'],
            user_login=_.get(pr_dict, 'user.login'),
            cn_current_repo=cn_current_repo,
            reference_type=REF_TYPE_PULL_REQUEST)
        if not release_notes_pr:
            continue

        release_notes.extend(release_notes_pr)
    return release_notes
Esempio n. 5
0
def release_tags(github_helper: GitHubRepositoryHelper,
                 repo: git.Repo) -> [str]:
    def is_valid_semver(tag_name):
        try:
            parse_version_info(tag_name)
            return True
        except ValueError:
            warning('{tag} is not a valid SemVer string'.format(tag=tag_name))
            return False

    release_tags = github_helper.release_tags()
    # you can remove the directive to disable the undefined-variable error once pylint is updated
    # with fix https://github.com/PyCQA/pylint/commit/db01112f7e4beadf7cd99c5f9237d580309f0494
    # included
    # pylint: disable=undefined-variable
    tags = _ \
        .chain(repo.tags) \
        .map(lambda tag: {"tag": tag.name, "commit": tag.commit.hexsha}) \
        .filter(lambda item: _.find(release_tags, lambda el: el == item['tag'])) \
        .filter(lambda item: is_valid_semver(item['tag'])) \
        .key_by('commit') \
        .map_values('tag') \
        .value()
    # pylint: enable=undefined-variable
    return tags
Esempio n. 6
0
def github_helper_from_github_access(
    github_access=gci.componentmodel.GithubAccess, ):
    return GitHubRepositoryHelper(
        github_api=ccc.github.github_api_from_gh_access(github_access),
        owner=github_access.org_name(),
        name=github_access.repository_name(),
    )
Esempio n. 7
0
    def _notify_broken_definition_owners(self, failed_descriptor):
        definition_descriptor = failed_descriptor.definition_descriptor
        main_repo = definition_descriptor.main_repo
        github_cfg = github_cfg_for_hostname(self._cfg_set,
                                             main_repo['hostname'])
        github_api = _create_github_api_object(github_cfg)
        repo_owner, repo_name = main_repo['path'].split('/')

        githubrepobranch = GitHubRepoBranch(
            github_config=github_cfg,
            repo_owner=repo_owner,
            repo_name=repo_name,
            branch=main_repo['branch'],
        )

        repo_helper = GitHubRepositoryHelper.from_githubrepobranch(
            githubrepobranch=githubrepobranch, )

        codeowners_enumerator = CodeownersEnumerator()
        codeowners_resolver = CodeOwnerEntryResolver(github_api=github_api)
        recipients = set(
            codeowners_resolver.resolve_email_addresses(
                codeowners_enumerator.enumerate_remote_repo(
                    github_repo_helper=repo_helper)))

        # in case no codeowners are available, resort to using the committer
        if not recipients:
            head_commit = repo_helper.repository.commit(main_repo['branch'])
            user_ids = {
                user_info.get('login')
                for user_info in (head_commit.committer, head_commit.author)
                if user_info.get('login')
            }
            for user_id in user_ids:
                user = github_api.user(user_id)
                if user.email:
                    recipients.add(user.email)

        # if there are still no recipients available print a warning
        if not recipients:
            warning(
                textwrap.dedent(f"""
                Unable to determine recipient for pipeline '{definition_descriptor.pipeline_name}'
                found in branch '{main_repo['branch']}' ({main_repo['path']}). Please make sure that
                CODEOWNERS and committers have exposed a public e-mail address in their profile.
                """))
        else:
            info(
                f'Sending notification e-mail to {recipients} ({main_repo["path"]})'
            )
            email_cfg = self._cfg_set.email()
            _send_mail(
                email_cfg=email_cfg,
                recipients=recipients,
                subject='Your pipeline definition in {repo} is erroneous'.
                format(repo=main_repo['path'], ),
                mail_template=
                (f"The pipeline definition for pipeline '{definition_descriptor.pipeline_name}' "
                 f" on branch '{main_repo['branch']}' contains errors.\n\n"
                 f"Error details:\n{str(failed_descriptor.error_details)}"))
Esempio n. 8
0
def github_api_for_pr_event(
    pr_event: PullRequestEvent,
    cfg_set: model.ConfigurationSet,
):
    repo = pr_event.repository()
    github_host = repo.github_host()
    repository_path = repo.repository_path()

    github_cfg = ccc.github.github_cfg_for_repo_url(
        repo_url=ci.util.urljoin(github_host, repository_path),
        cfg_factory=cfg_set,
    )
    owner, name = repository_path.split('/')

    try:
        github_helper = GitHubRepositoryHelper(
            owner=owner,
            name=name,
            github_cfg=github_cfg,
        )
    except NotFoundError:
        logger.warning(
            f"Unable to access repository '{repository_path}' on github '{github_host}'. "
            "Please make sure the repository exists and the technical user has the necessary "
            "permissions to access it."
        )
        return None

    return github_helper
Esempio n. 9
0
def generate_release_notes_cli(
    repo_dir: str,
    github_cfg_name: str,
    github_repository_owner: str,
    github_repository_name: str,
    repository_branch: str,
    commit_range: str=None
):
    github_cfg = ctx().cfg_factory().github(github_cfg_name)

    githubrepobranch = GitHubRepoBranch(
        github_config=github_cfg,
        repo_owner=github_repository_owner,
        repo_name=github_repository_name,
        branch=repository_branch,
    )

    helper = GitHubRepositoryHelper.from_githubrepobranch(
        githubrepobranch=githubrepobranch,
    )
    git_helper = GitHelper.from_githubrepobranch(
        repo_path=repo_dir,
        githubrepobranch=githubrepobranch,
    )

    ReleaseNotes.create(
        github_helper=helper,
        git_helper=git_helper,
        repository_branch=repository_branch,
        commit_range=commit_range
    ).to_markdown()
Esempio n. 10
0
 def enumerate_remote_repo(self, github_repo_helper: GitHubRepositoryHelper):
     for path in self.CODEOWNERS_PATHS:
         try:
             yield from self._filter_codeowners_entries(
                 github_repo_helper.retrieve_text_file_contents(file_path=path).split('\n')
             )
         except NotFoundError:
             pass # ignore absent files
Esempio n. 11
0
def github_helper_from_github_access(
    github_access=gci.componentmodel.GithubAccess, ):
    logger.info(f'Creating GH Repo-helper for {github_access.repoUrl}')
    return GitHubRepositoryHelper(
        github_api=ccc.github.github_api_from_gh_access(github_access),
        owner=github_access.org_name(),
        name=github_access.repository_name(),
    )
Esempio n. 12
0
def validate_pipeline_definitions(
    cfg_factory,
    cfg_set: model.ConfigurationSet,
    whd_cfg: model.webhook_dispatcher.WebhookDispatcherConfig,
    pr_event: PullRequestEvent,
    github_helper: GitHubRepositoryHelper,
    tagging_label: str,
):
    repo_url = pr_event.repository().repository_url()
    job_mapping_set = cfg_set.job_mapping()
    job_mapping = job_mapping_set.job_mapping_for_repo_url(repo_url, cfg_set)
    pr_number = pr_event.number()

    try:
        validate_repository_pipelines(
            repo_url=pr_event.head_repository().repository_url(),
            cfg_set=cfg_factory.cfg_set(job_mapping.replication_ctx_cfg_set()),
            whd_cfg=whd_cfg,
            branch=pr_event.head_ref(),
            job_mapping=job_mapping,
        )
        # validation succeeded. Remove the label again, if it is currently set.
        if tagging_label in pr_event.label_names():
            logger.info(
                f'Pipeline-definition in PR #{pr_number} of repository {repo_url} passed '
                'validation again. Commenting on PR.'
            )
            github_helper.remove_label_from_pull_request(pr_number, tagging_label)
            github_helper.add_comment_to_pr(
                pull_request_number=pr_number,
                comment='The pipeline-definition has been fixed.',
            )
    except concourse.replicator.PipelineValidationError as e:
        # If validation fails add a comment on the PR iff we haven't already commented, as
        # tracked by label
        if tagging_label not in pr_event.label_names():
            logger.warning(
                f'Pipeline-definition in PR #{pr_number} of repository {repo_url} failed '
                'validation. Commenting on PR.'
            )
            github_helper.add_comment_to_pr(
                pull_request_number=pr_number,
                comment=(
                    'This PR proposes changes that would break the pipeline definition:\n'
                    f'```\n{e}\n```\n'
                ),
            )
            github_helper.add_labels_to_pull_request(pr_number, tagging_label)
Esempio n. 13
0
def greatest_release_version(
    github_repository_url: CliHint(help='e.g.: https://github.com/gardener/cc-utils'),
    anonymous: CliHint(
        typehint=bool,
        help='Use anonymous access. Unauthenticated access is only possible on github.com.',
    ) = False,
    ignore_prereleases: CliHint(
        typehint=bool,
        help='Ignore prerelease-versions (e.g.: 1.2.3-foo)',
    ) = False,
):
    '''Find the release with the greatest name (according to semver) and print its semver-version.

    Note:
    - This will only consider releases whose names are either immediately parseable as semver-
    versions, or prefixed with a single character ('v').
    - The 'v'-prefix (if present) will be not be present in the output.
    - If a release has no name, its tag will be used instead of its name.

    For more details on the ordering of semantic versioning, see 'https://www.semver.org'.
    '''
    parse_result = urllib.parse.urlparse(github_repository_url)

    if not parse_result.netloc:
        raise ValueError(f'Could not determine host for github-url {github_repository_url}')
    host = parse_result.netloc

    try:
        path = parse_result.path.strip('/')
        org, repo = path.split('/')
    except ValueError as e:
        raise ValueError(f"Could not extract org- and repo-name. Error: {e}")

    if anonymous:
        if 'github.com' not in host:
            raise ValueError("Anonymous access is only possible for github.com")
        github_api = github3.GitHub()
        repo_helper = GitHubRepositoryHelper(owner=org, name=repo, github_api=github_api)

    else:
        repo_helper = ccc.github.repo_helper(host=host, org=org, repo=repo)

    print(
        find_greatest_github_release_version(
            releases=repo_helper.repository.releases(),
            warn_for_unparseable_releases=False,
            ignore_prerelease_versions=ignore_prereleases,
        )
    )
Esempio n. 14
0
def list_draft_releases(
    github_cfg_name: str,
    github_repository_owner: str,
    github_repository_name: str,
    only_outdated: bool = False,
):
    '''List all draft releases in a GitHub repository. If the `--only-outdated` flag is set,
    only outdated draft releases are printed. A draft release is considered outdated iff:
        1: its version is smaller than the greatest release version (according to semver) AND
            2a: it is NOT a hotfix draft release AND
            2b: there are no hotfix draft releases with the same major and minor version
            OR
            3a: it is a hotfix draft release AND
            3b: there is a hotfix draft release of greater version (according to semver)
                with the same major and minor version

    Hotfix draft release in this context are draft releases with a semver patch version that is
    not equal to 0.
    '''
    github_cfg = ctx().cfg_factory().github(github_cfg_name)
    github_helper = GitHubRepositoryHelper(
        owner=github_repository_owner,
        name=github_repository_name,
        github_cfg=github_cfg,
    )
    if only_outdated:
        releases = [release for release in github_helper.repository.releases()]
        non_draft_releases = [
            release for release in releases if not release.draft
        ]
        greatest_release_version = find_greatest_github_release_version(
            non_draft_releases)
    else:
        releases = github_helper.repository.releases()

    draft_releases = [release for release in releases if release.draft]

    if only_outdated:
        if greatest_release_version is not None:
            draft_releases = outdated_draft_releases(
                draft_releases=draft_releases,
                greatest_release_version=greatest_release_version,
            )
        else:
            draft_releases = []
    for draft_release in draft_releases:
        print(draft_release.name)
Esempio n. 15
0
    def _set_pr_labels(self, pr_event, resources):
        required_labels = {
            resource.source.get('label')
            for resource in resources
            if resource.source.get('label') is not None
        }
        repo = pr_event.repository()
        repository_path = repo.repository_path()
        pr_number = pr_event.number()

        github_cfg = self.cfg_set.github()
        owner, name = repository_path.split('/')
        github_helper = GitHubRepositoryHelper(
            owner,
            name,
            github_cfg=github_cfg,
        )
        if pr_event.action() is PullRequestAction.OPENED:
            if github_helper.is_pr_created_by_org_member(pr_number):
                app.logger.info(
                    f"New pull request by member of '{owner}' in '{repository_path}' found. "
                    f"Setting required labels '{required_labels}'.")
                github_helper.add_labels_to_pull_request(
                    pr_number, *required_labels)
            else:
                app.logger.debug(
                    f"New pull request by member in '{repository_path}' found, but creator is not "
                    f"member of '{owner}' - will not set required labels.")
        elif pr_event.action() is PullRequestAction.SYNCHRONIZE:
            sender_login = pr_event.sender()['login']
            if github_helper.is_org_member(organization_name=owner,
                                           user_login=sender_login):
                app.logger.info(
                    f"Update to pull request #{pr_event.number()} by org member '{sender_login}' "
                    f" in '{repository_path}' found. Setting required labels '{required_labels}'."
                )
                github_helper.add_labels_to_pull_request(
                    pr_number, *required_labels)
            else:
                app.logger.debug(
                    f"Update to pull request #{pr_event.number()} by '{sender_login}' "
                    f" in '{repository_path}' found. Ignoring, since they are not an org member'."
                )
Esempio n. 16
0
def deploy_and_run_smoketest_pipeline(
    config_dir: str,
    config_name: str,
    concourse_team_name: str,
    cc_pipelines_repo_dir: str,
    cc_utils_repo_dir: str,
    wait_for_job_execution: bool=False,
):
    config_factory = ConfigFactory.from_cfg_dir(cfg_dir=config_dir)
    config_set = config_factory.cfg_set(cfg_name=config_name)
    concourse_cfg = config_set.concourse()

    # as this is an integration test, hard-code assumptions about the layout of
    # our pipelines repository
    template_path = os.path.join(cc_utils_repo_dir, 'concourse', 'templates')
    template_include_dir = os.path.join(cc_utils_repo_dir, 'concourse')
    pipeline_name = 'cc-smoketest'

    # retrieve pipeline-definition from github at hardcoded location
    github_cfg = config_set.github()

    githubrepobranch = GitHubRepoBranch(
        github_config=github_cfg,
        repo_owner='kubernetes',
        repo_name='cc-smoketest',
        branch='master',
    )

    helper = GitHubRepositoryHelper.from_githubrepobranch(
      githubrepobranch=githubrepobranch,
    )
    pipeline_definition = yaml.load(
        helper.retrieve_text_file_contents(
            file_path='.ci/smoketest-pipeline.yaml',
        ),
        Loader=yaml.SafeLoader,
    )

    definition_descriptor = DefinitionDescriptor(
        pipeline_name=pipeline_name,
        pipeline_definition=pipeline_definition[pipeline_name],
        main_repo={'path': 'kubernetes/cc-smoketest', 'branch': 'master'},
        concourse_target_cfg=concourse_cfg,
        concourse_target_team=concourse_team_name,
    )

    preprocessor = DefinitionDescriptorPreprocessor()
    template_retriever = TemplateRetriever(template_path=template_path)
    renderer = Renderer(
        template_retriever=template_retriever,
        template_include_dir=template_include_dir,
        cfg_set=config_set,
    )
    deployer = ConcourseDeployer(
        unpause_pipelines=True,
        expose_pipelines=True
    )

    definition_descriptor = preprocessor.process_definition_descriptor(definition_descriptor)
    rendering_result = renderer.render(definition_descriptor)

    info('deploying pipeline')
    deployment_result = deployer.deploy(rendering_result.definition_descriptor)

    if not deployment_result.deploy_status & DeployStatus.SUCCEEDED:
        fail('deployment failed')
Esempio n. 17
0
def release_and_prepare_next_dev_cycle(
    githubrepobranch: GitHubRepoBranch,
    repository_version_file_path: str,
    release_version: str,
    repo_dir: str,
    release_notes_policy: str,
    release_commit_publishing_policy: str,
    release_commit_callback: str = None,
    next_version_callback: str = None,
    version_operation: str = "bump_minor",
    prerelease_suffix: str = "dev",
    author_name: str = "gardener-ci",
    author_email: str = "*****@*****.**",
    component_descriptor_file_path: str = None,
    slack_cfg_name: str = None,
    slack_channel: str = None,
    rebase_before_release: bool = False,
    commit_message_prefix: str = None,
):
    transaction_ctx = TransactionContext()  # shared between all steps/trxs

    release_notes_policy = ReleaseNotesPolicy(release_notes_policy)
    release_commit_publishing_policy = ReleaseCommitPublishingPolicy(
        release_commit_publishing_policy)
    github_helper = GitHubRepositoryHelper.from_githubrepobranch(
        githubrepobranch)
    git_helper = GitHelper.from_githubrepobranch(
        githubrepobranch=githubrepobranch,
        repo_path=repo_dir,
    )

    step_list = []

    if rebase_before_release:
        rebase_step = RebaseStep(
            git_helper=git_helper,
            repository_branch=githubrepobranch.branch(),
        )
        step_list.append(rebase_step)

    release_commit_step = ReleaseCommitStep(
        git_helper=git_helper,
        repo_dir=repo_dir,
        release_version=release_version,
        repository_version_file_path=repository_version_file_path,
        repository_branch=githubrepobranch.branch(),
        commit_message_prefix=commit_message_prefix,
        release_commit_callback=release_commit_callback,
        publishing_policy=release_commit_publishing_policy,
    )
    step_list.append(release_commit_step)

    if version_operation != version.NOOP:
        next_cycle_commit_step = NextDevCycleCommitStep(
            git_helper=git_helper,
            repo_dir=repo_dir,
            release_version=release_version,
            repository_version_file_path=repository_version_file_path,
            repository_branch=githubrepobranch.branch(),
            version_operation=version_operation,
            prerelease_suffix=prerelease_suffix,
            next_version_callback=next_version_callback,
            publishing_policy=release_commit_publishing_policy,
        )
        step_list.append(next_cycle_commit_step)

    github_release_step = GitHubReleaseStep(
        github_helper=github_helper,
        githubrepobranch=githubrepobranch,
        repo_dir=repo_dir,
        release_version=release_version,
        component_descriptor_file_path=component_descriptor_file_path,
    )
    step_list.append(github_release_step)

    release_transaction = Transaction(ctx=transaction_ctx, steps=step_list)

    release_transaction.validate()
    if not release_transaction.execute():
        raise RuntimeError('An error occurred while creating the Release.')

    publish_release_notes_step = PublishReleaseNotesStep(
        githubrepobranch=githubrepobranch,
        github_helper=github_helper,
        release_version=release_version,
        repo_dir=repo_dir,
    )

    cleanup_draft_releases_step = TryCleanupDraftReleasesStep(
        github_helper=github_helper, )

    cleanup_draft_releases_transaction = Transaction(
        ctx=transaction_ctx,
        steps=(cleanup_draft_releases_step, ),
    )

    if not cleanup_draft_releases_transaction.execute():
        ci.util.warning('An error occured while cleaning up draft releases')

    if release_notes_policy == ReleaseNotesPolicy.DISABLED:
        return info('release notes were disabled - skipping')
    elif release_notes_policy == ReleaseNotesPolicy.DEFAULT:
        pass
    else:
        raise NotImplementedError(release_notes_policy)

    release_notes_transaction = Transaction(
        ctx=transaction_ctx,
        steps=(publish_release_notes_step, ),
    )
    release_notes_transaction.validate()
    if not release_notes_transaction.execute():
        raise RuntimeError(
            'An error occurred while publishing the release notes.')

    if slack_cfg_name and slack_channel:
        release_notes = transaction_ctx.step_output(
            publish_release_notes_step.name()).get('release notes')

        post_to_slack_step = PostSlackReleaseStep(
            slack_cfg_name=slack_cfg_name,
            slack_channel=slack_channel,
            release_version=release_version,
            release_notes=release_notes,
            githubrepobranch=githubrepobranch,
        )
        slack_transaction = Transaction(
            ctx=transaction_ctx,
            steps=(post_to_slack_step, ),
        )
        slack_transaction.validate()
        if not slack_transaction.execute():
            raise RuntimeError(
                'An error occurred while posting the release notes to Slack.')
Esempio n. 18
0
def release_and_prepare_next_dev_cycle(
    githubrepobranch: GitHubRepoBranch,
    repository_version_file_path: str,
    release_version: str,
    repo_dir: str,
    release_notes_policy: str,
    release_commit_callback: str = None,
    next_version_callback: str = None,
    version_operation: str = "bump_minor",
    prerelease_suffix: str = "dev",
    author_name: str = "gardener-ci",
    author_email: str = "*****@*****.**",
    component_descriptor_file_path: str = None,
    slack_cfg_name: str = None,
    slack_channel: str = None,
    rebase_before_release: bool = False,
):
    release_notes_policy = ReleaseNotesPolicy(release_notes_policy)
    github_helper = GitHubRepositoryHelper.from_githubrepobranch(
        githubrepobranch)
    git_helper = GitHelper.from_githubrepobranch(
        githubrepobranch=githubrepobranch,
        repo_path=repo_dir,
    )

    release_commits_step = ReleaseCommitsStep(
        git_helper=git_helper,
        repo_dir=repo_dir,
        release_version=release_version,
        repository_version_file_path=repository_version_file_path,
        repository_branch=githubrepobranch.branch(),
        version_operation=version_operation,
        prerelease_suffix=prerelease_suffix,
        release_commit_callback=release_commit_callback,
        next_version_callback=next_version_callback,
        rebase_before_release=rebase_before_release,
    )

    github_release_step = GitHubReleaseStep(
        github_helper=github_helper,
        githubrepobranch=githubrepobranch,
        repo_dir=repo_dir,
        release_version=release_version,
        component_descriptor_file_path=component_descriptor_file_path,
    )

    release_transaction = Transaction(
        release_commits_step,
        github_release_step,
    )

    release_transaction.validate()
    if not release_transaction.execute():
        raise RuntimeError('An error occurred while creating the Release.')

    publish_release_notes_step = PublishReleaseNotesStep(
        githubrepobranch=githubrepobranch,
        github_helper=github_helper,
        release_version=release_version,
        repo_dir=repo_dir,
    )

    cleanup_draft_releases_step = CleanupDraftReleaseStep(
        github_helper=github_helper,
        release_version=release_version,
    )

    if release_notes_policy == ReleaseNotesPolicy.DISABLED:
        return info('release notes were disabled - skipping')
    elif release_notes_policy == ReleaseNotesPolicy.DEFAULT:
        pass
    else:
        raise NotImplementedError(release_notes_policy)

    release_notes_steps = [
        publish_release_notes_step,
        cleanup_draft_releases_step,
    ]

    release_notes_transaction = Transaction(*release_notes_steps)
    release_notes_transaction.validate()
    if not release_notes_transaction.execute():
        raise RuntimeError(
            'An error occurred while publishing the release notes.')

    if slack_cfg_name and slack_channel:
        context = release_notes_transaction.context()
        release_notes = context.step_output(
            publish_release_notes_step.name()).get('release notes')

        post_to_slack_step = PostSlackReleaseStep(
            slack_cfg_name=slack_cfg_name,
            slack_channel=slack_channel,
            release_version=release_version,
            release_notes=release_notes,
            githubrepobranch=githubrepobranch,
        )
        slack_transaction = Transaction(post_to_slack_step)
        slack_transaction.validate()
        if not slack_transaction.execute():
            raise RuntimeError(
                'An error occurred while posting the release notes to Slack.')
Esempio n. 19
0
output_dir = util.check_env('BINARY_PATH')

repo_owner, repo_name = repo_owner_and_name.split('/')

repo_path = pathlib.Path(repo_dir).resolve()
output_path = pathlib.Path(output_dir).resolve()
version_file_path = repo_path / VERSION_FILE_NAME

version_file_contents = version_file_path.read_text()

cfg_factory = util.ctx().cfg_factory()
github_cfg = cfg_factory.github('github_com')

github_repo_helper = GitHubRepositoryHelper(
    owner=repo_owner,
    name=repo_name,
    github_cfg=github_cfg,
)

gh_release = github_repo_helper.repository.release_from_tag(
    version_file_contents)

for dir, dirs, files in os.walk(os.path.join(output_path, "bin", "rel")):
    for binName in files:
        dir_path = pathlib.Path(dir).resolve()
        binFilePath = dir_path / binName
        gh_release.upload_asset(
            content_type='application/octet-stream',
            name=f'{binName}',
            asset=binFilePath.open(mode='rb'),
        )
Esempio n. 20
0
def release_and_prepare_next_dev_cycle(
    component_name: str,
    githubrepobranch: GitHubRepoBranch,
    release_commit_publishing_policy: str,
    release_notes_policy: str,
    release_version: str,
    repo_hostname: str,
    repo_path: str,
    repo_dir: str,
    repository_version_file_path: str,
    git_tags: list,
    github_release_tag: dict,
    release_commit_callback_image_reference: str,
    component_descriptor_v2_path: str = None,
    ctf_path: str = None,
    next_cycle_commit_message_prefix: str = None,
    next_version_callback: str = None,
    prerelease_suffix: str = "dev",
    rebase_before_release: bool = False,
    release_on_github: bool = True,
    release_commit_callback: str = None,
    release_commit_message_prefix: str = None,
    slack_channel_configs: list = [],
    version_operation: str = "bump_minor",
):
    transaction_ctx = TransactionContext()  # shared between all steps/trxs

    release_notes_policy = ReleaseNotesPolicy(release_notes_policy)
    release_commit_publishing_policy = ReleaseCommitPublishingPolicy(
        release_commit_publishing_policy)
    github_helper = GitHubRepositoryHelper.from_githubrepobranch(
        githubrepobranch)
    git_helper = GitHelper.from_githubrepobranch(
        githubrepobranch=githubrepobranch,
        repo_path=repo_dir,
    )

    step_list = []

    if rebase_before_release:
        rebase_step = RebaseStep(
            git_helper=git_helper,
            repository_branch=githubrepobranch.branch(),
        )
        step_list.append(rebase_step)

    release_commit_step = ReleaseCommitStep(
        git_helper=git_helper,
        repo_dir=repo_dir,
        release_version=release_version,
        repository_version_file_path=repository_version_file_path,
        repository_branch=githubrepobranch.branch(),
        release_commit_message_prefix=release_commit_message_prefix,
        release_commit_callback=release_commit_callback,
        release_commit_callback_image_reference=
        release_commit_callback_image_reference,
        publishing_policy=release_commit_publishing_policy,
    )
    step_list.append(release_commit_step)

    create_tag_step = CreateTagsStep(
        git_tags=git_tags,
        github_release_tag=github_release_tag,
        git_helper=git_helper,
        github_helper=github_helper,
        release_version=release_version,
        publishing_policy=release_commit_publishing_policy,
    )
    step_list.append(create_tag_step)

    if version_operation != version.NOOP:
        next_cycle_commit_step = NextDevCycleCommitStep(
            git_helper=git_helper,
            repo_dir=repo_dir,
            release_version=release_version,
            repository_version_file_path=repository_version_file_path,
            repository_branch=githubrepobranch.branch(),
            version_operation=version_operation,
            prerelease_suffix=prerelease_suffix,
            next_version_callback=next_version_callback,
            publishing_policy=release_commit_publishing_policy,
            next_cycle_commit_message_prefix=next_cycle_commit_message_prefix,
        )
        step_list.append(next_cycle_commit_step)

    if release_on_github:
        github_release_step = GitHubReleaseStep(
            github_helper=github_helper,
            githubrepobranch=githubrepobranch,
            repo_dir=repo_dir,
            component_name=component_name,
            release_version=release_version,
        )
        step_list.append(github_release_step)

    upload_component_descriptor_step = UploadComponentDescriptorStep(
        github_helper=github_helper,
        component_descriptor_v2_path=component_descriptor_v2_path,
        ctf_path=ctf_path,
        release_on_github=release_on_github,
    )
    step_list.append(upload_component_descriptor_step)

    release_transaction = Transaction(
        ctx=transaction_ctx,
        steps=step_list,
    )

    release_transaction.validate()
    if not release_transaction.execute():
        raise RuntimeError('An error occurred while creating the Release.')

    if release_on_github:
        publish_release_notes_step = PublishReleaseNotesStep(
            githubrepobranch=githubrepobranch,
            github_helper=github_helper,
            repository_hostname=repo_hostname,
            repository_path=repo_path,
            release_version=release_version,
            component_descriptor_v2_path=component_descriptor_v2_path,
            ctf_path=ctf_path,
            repo_dir=repo_dir,
        )

    cleanup_draft_releases_step = TryCleanupDraftReleasesStep(
        github_helper=github_helper, )

    cleanup_draft_releases_transaction = Transaction(
        ctx=transaction_ctx,
        steps=(cleanup_draft_releases_step, ),
    )

    if not cleanup_draft_releases_transaction.execute():
        logger.warning('An error occured while cleaning up draft releases')

    if release_notes_policy == ReleaseNotesPolicy.DISABLED:
        return logger.info('release notes were disabled - skipping')
    elif release_notes_policy == ReleaseNotesPolicy.DEFAULT:
        pass
    else:
        raise NotImplementedError(release_notes_policy)

    if release_on_github:
        release_notes_transaction = Transaction(
            ctx=transaction_ctx,
            steps=(publish_release_notes_step, ),
        )
        release_notes_transaction.validate()
        if not release_notes_transaction.execute():
            raise RuntimeError(
                'An error occurred while publishing the release notes.')

    if slack_channel_configs:
        if not release_on_github:
            raise RuntimeError('Cannot post to slack without a github release')
        release_notes = transaction_ctx.step_output(
            publish_release_notes_step.name()).get('release notes')
        all_slack_releases_successful = True
        for slack_cfg in slack_channel_configs:
            slack_cfg_name = slack_cfg['slack_cfg_name']
            slack_channel = slack_cfg['channel_name']
            post_to_slack_step = PostSlackReleaseStep(
                slack_cfg_name=slack_cfg_name,
                slack_channel=slack_channel,
                release_version=release_version,
                release_notes=release_notes,
                githubrepobranch=githubrepobranch,
            )
            slack_transaction = Transaction(
                ctx=transaction_ctx,
                steps=(post_to_slack_step, ),
            )
            slack_transaction.validate()
            all_slack_releases_successful = (all_slack_releases_successful
                                             and slack_transaction.execute())
        if not all_slack_releases_successful:
            raise RuntimeError(
                'An error occurred while posting the release notes to Slack.')
def create_upgrade_pr(
        from_ref,
        to_ref,
        pull_request_util,
        upgrade_script_path,
        githubrepobranch: GitHubRepoBranch,
        repo_dir,
        github_cfg_name,
        cfg_factory,
        merge_policy,
        after_merge_callback=None,
    ):
    ls_repo = pull_request_util.repository

    # have component create upgradation diff
    cmd_env = os.environ.copy()
    cmd_env['DEPENDENCY_TYPE'] = to_ref.type_name()
    cmd_env['DEPENDENCY_NAME'] = to_ref.name()
    cmd_env['DEPENDENCY_VERSION'] = to_ref.version()
    cmd_env['REPO_DIR'] = repo_dir
    cmd_env['GITHUB_CFG_NAME'] = github_cfg_name

    # pass type-specific attributes
    if to_ref.type_name() == 'container_image':
      cmd_env['DEPENDENCY_IMAGE_REFERENCE'] = to_ref.image_reference()

    subprocess.run(
        [str(upgrade_script_path)],
        check=True,
        env=cmd_env
    )
    commit_msg = 'Upgrade {cn}\n\nfrom {ov} to {nv}'.format(
        cn=to_ref.name(),
        ov=from_ref.version(),
        nv=to_ref.version(),
    )

    # mv diff into commit and push it
    helper = gitutil.GitHelper.from_githubrepobranch(
        githubrepobranch=githubrepobranch,
        repo_path=repo_dir,
    )
    commit = helper.index_to_commit(message=commit_msg)
    ci.util.info(f'commit for upgrade-PR: {commit.hexsha}')

    new_branch_name = ci.util.random_str(prefix='ci-', length=12)
    repo_branch = githubrepobranch.branch()
    head_sha = ls_repo.ref(f'heads/{repo_branch}').object.sha
    ls_repo.create_ref(f'refs/heads/{new_branch_name}', head_sha)

    def rm_pr_branch():
      ls_repo.ref(f'heads/{new_branch_name}').delete()

    try:
      helper.push(from_ref=commit.hexsha, to_ref=f'refs/heads/{new_branch_name}')
    except:
      ci.util.warning('an error occurred - removing now useless pr-branch')
      rm_pr_branch()
      raise

    helper.repo.git.checkout('.')

    try:
      with tempfile.TemporaryDirectory() as temp_dir:
          from_github_cfg = cfg_factory.github(from_ref.config_name())
          from_github_helper = GitHubRepositoryHelper(
              github_cfg=from_github_cfg,
              owner=from_ref.github_organisation(),
              name=from_ref.github_repo(),
          )
          from_git_helper = gitutil.GitHelper.clone_into(
              target_directory=temp_dir,
              github_cfg=from_github_cfg,
              github_repo_path=from_ref.github_repo_path()
          )
          commit_range = '{from_version}..{to_version}'.format(
              from_version=from_ref.version(),
              to_version=to_ref.version()
          )
          release_note_blocks = ReleaseNotes.create(
              github_helper=from_github_helper,
              git_helper=from_git_helper,
              commit_range=commit_range
          ).release_note_blocks()
          if release_note_blocks:
              text = '*Release Notes*:\n{blocks}'.format(
                  blocks=release_note_blocks
              )
          else:
              text = pull_request_util.retrieve_pr_template_text()
    except:
      ci.util.warning('an error occurred during release notes processing (ignoring)')
      text = None
      import traceback
      ci.util.warning(traceback.format_exc())

    pull_request = ls_repo.create_pull(
            title=github.util.PullRequestUtil.calculate_pr_title(
                reference=to_ref,
                from_version=from_ref.version(),
                to_version=to_ref.version()
            ),
            base=repo_branch,
            head=new_branch_name,
            body=text,
    )

    if merge_policy is MergePolicy.MANUAL:
        return

    # auto-merge - todo: make configurable (e.g. merge method)
    pull_request.merge()
    rm_pr_branch()

    if after_merge_callback:
        subprocess.run(
            [os.path.join(repo_dir, after_merge_callback)],
            check=True,
            env=cmd_env
        )