Example #1
0
 def from_githubrepobranch(
     githubrepobranch: GitHubRepoBranch,
     repo_path: str,
 ):
     return GitHelper(
         repo=repo_path,
         github_cfg=githubrepobranch.github_config(),
         github_repo_path=githubrepobranch.github_repo_path(),
     )
Example #2
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()
Example #3
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)}"))
Example #4
0
 def _examinee(
         github_helper=MagicMock(),
         githubrepobranch=GitHubRepoBranch(
             github_config='test_config',
             repo_owner='test_owner',
             repo_name='test_name',
             branch='master',
         ),
         repo_dir=str(tmp_path),
         release_version='1.0.0',
 ):
     return concourse.steps.release.PublishReleaseNotesStep(
         github_helper=github_helper,
         githubrepobranch=githubrepobranch,
         repo_dir=repo_dir,
         release_version=release_version,
     )
Example #5
0
 def _examinee(
     slack_cfg_name='test_config',
     slack_channel='test_channel',
     githubrepobranch=GitHubRepoBranch(
         github_config='test_config',
         repo_owner='test_owner',
         repo_name='test_name',
         branch='master',
     ),
     release_version='1.0.0',
 ):
     return concourse.steps.release.PostSlackReleaseStep(
         slack_cfg_name=slack_cfg_name,
         slack_channel=slack_channel,
         githubrepobranch=githubrepobranch,
         release_version=release_version,
     )
Example #6
0
 def _examinee(
     githubrepobranch=GitHubRepoBranch(
         github_config='test_config',
         repo_owner='test_owner',
         repo_name='test_name',
         branch='master',
     ),
     repo_dir=str(tmp_path),
     release_version='1.0.0',
     tag_helper_return_value=False,
 ):
     # Create a github_helper mock that always reports a tag as existing/not existing,
     # depending on the passed value
     github_helper_mock = MagicMock(spec=GitHubRepositoryHelper)
     return concourse.steps.release.GitHubReleaseStep(
         github_helper=github_helper_mock,
         githubrepobranch=githubrepobranch,
         repo_dir=repo_dir,
         release_version=release_version,
         component_descriptor_v2_path=component_descriptor_v2,
         ctf_path=ctf_path,
     )
Example #7
0
 def _examinee(
         github_helper=MagicMock(),
         githubrepobranch=GitHubRepoBranch(
             github_config='test_config',
             repo_owner='test_owner',
             repo_name='test_name',
             branch='master',
         ),
         repository_hostname="example.com",
         repository_path="a_name",
         repo_dir=str(tmp_path),
         release_version='1.0.0',
 ):
     return concourse.steps.release.PublishReleaseNotesStep(
         github_helper=github_helper,
         githubrepobranch=githubrepobranch,
         repository_hostname=repository_hostname,
         repository_path=repository_path,
         repo_dir=repo_dir,
         release_version=release_version,
         component_descriptor_v2_path=component_descriptor_v2,
         ctf_path=ctf_path,
     )
def create_upgrade_pr(
    component: gci.componentmodel.Component,
    from_ref: gci.componentmodel.ComponentReference,
    to_ref: gci.componentmodel.ComponentReference,
    to_version: str,
    pull_request_util,
    upgrade_script_path,
    upgrade_script_relpath,
    githubrepobranch: GitHubRepoBranch,
    repo_dir,
    github_cfg_name,
    merge_policy: MergePolicy,
    merge_method: MergeMethod,
    after_merge_callback=None,
    container_image: str = None,
):
    if container_image:
        dockerutil.launch_dockerd_if_not_running()

    ls_repo = pull_request_util.repository

    from_component_descriptor = cnudie.retrieve.component_descriptor(
        name=from_ref.componentName,
        version=from_ref.version,
        ctx_repo=component.current_repository_ctx(),
    )
    from_component = from_component_descriptor.component

    # prepare env for upgrade script and after-merge-callback
    cmd_env = os.environ.copy()
    # TODO: Handle upgrades for types other than 'component'
    cmd_env['DEPENDENCY_TYPE'] = product.v2.COMPONENT_TYPE_NAME
    cmd_env['DEPENDENCY_NAME'] = to_ref.componentName
    cmd_env['LOCAL_DEPENDENCY_NAME'] = to_ref.name
    cmd_env['DEPENDENCY_VERSION'] = to_version
    if container_image:
        cmd_env['REPO_DIR'] = (repo_dir_in_container := '/mnt/main_repo')
    else:
        cmd_env['REPO_DIR'] = repo_dir
    cmd_env['GITHUB_CFG_NAME'] = github_cfg_name
    cmd_env['CTX_REPO_URL'] = component.current_repository_ctx().baseUrl

    if not container_image:
        # create upgrade diff
        subprocess.run([str(upgrade_script_path)], check=True, env=cmd_env)
    else:
        # run check-script in container
        oci_registry_cfg = cr.find_config(image_reference=container_image)
        if oci_registry_cfg:
            docker_cfg_dir = tempfile.TemporaryDirectory()
            dockerutil.mk_docker_cfg_dir(
                cfg={'auths': oci_registry_cfg.as_docker_auths()},
                cfg_dir=docker_cfg_dir.name,
                exist_ok=True,
            )
        else:
            docker_cfg_dir = None

        upgrade_script_path_in_container = os.path.join(
            repo_dir_in_container,
            upgrade_script_relpath,
        )

        docker_argv = dockerutil.docker_run_argv(
            image_reference=container_image,
            argv=(upgrade_script_path_in_container, ),
            env=cmd_env,
            mounts={
                repo_dir: repo_dir_in_container,
            },
            cfg_dir=docker_cfg_dir.name,
        )

        logger.info(f'will run: ${docker_argv=}')

        try:
            subprocess.run(
                docker_argv,
                check=True,
            )
        finally:
            if docker_cfg_dir:
                docker_cfg_dir.cleanup()

    from_version = from_ref.version
    commit_message = f'Upgrade {to_ref.name}\n\nfrom {from_version} to {to_version}'

    upgrade_branch_name = push_upgrade_commit(
        ls_repo=ls_repo,
        commit_message=commit_message,
        githubrepobranch=githubrepobranch,
        repo_dir=repo_dir,
    )
    # branch was created. Cleanup if something fails
    try:
        release_notes = _import_release_notes(
            component=from_component,
            to_version=to_version,
            pull_request_util=pull_request_util,
        )
    except Exception:
        logger.warning('failed to retrieve release-notes')
        traceback.print_exc()
        release_notes = 'failed to retrieve release-notes'

    pull_request = ls_repo.create_pull(
        title=github.util.PullRequestUtil.calculate_pr_title(
            reference=to_ref, from_version=from_version,
            to_version=to_version),
        base=githubrepobranch.branch(),
        head=upgrade_branch_name,
        body=release_notes or 'failed to retrieve release-notes',
    )

    if merge_policy is MergePolicy.MANUAL:
        return

    if merge_method is MergeMethod.MERGE:
        pull_request.merge(merge_method='merge')
    elif merge_method is MergeMethod.REBASE:
        pull_request.merge(merge_method='rebase')
    elif merge_method is MergeMethod.SQUASH:
        pull_request.merge(merge_method='squash')
    else:
        raise NotImplementedError(f'{merge_method=}')

    try:
        ls_repo.ref(f'heads/{upgrade_branch_name}').delete()
    except github3.exceptions.NotFoundError:
        pass

    if after_merge_callback:
        subprocess.run([os.path.join(repo_dir, after_merge_callback)],
                       check=True,
                       env=cmd_env)
Example #9
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')
Example #10
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.')
Example #11
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.')
Example #12
0
def create_upgrade_pr(
    component: gci.componentmodel.Component,
    from_ref: gci.componentmodel.ComponentReference,
    to_ref: gci.componentmodel.ComponentReference,
    to_version: str,
    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
    from_version = from_ref.version

    # prepare env for upgrade script and after-merge-callback
    cmd_env = os.environ.copy()
    # TODO: Handle upgrades for types other than 'component'
    cmd_env['DEPENDENCY_TYPE'] = product.v2.COMPONENT_TYPE_NAME
    cmd_env['DEPENDENCY_NAME'] = to_ref.componentName
    cmd_env['LOCAL_DEPENDENCY_NAME'] = to_ref.name
    cmd_env['DEPENDENCY_VERSION'] = to_version
    cmd_env['REPO_DIR'] = repo_dir
    cmd_env['GITHUB_CFG_NAME'] = github_cfg_name
    cmd_env['CTX_REPO_URL'] = component.current_repository_ctx().baseUrl

    # create upgrade diff
    subprocess.run(
        [str(upgrade_script_path)],
        check=True,
        env=cmd_env
    )

    commit_message = f'Upgrade {to_ref.name}\n\nfrom {from_version} to {to_version}'

    upgrade_branch_name = push_upgrade_commit(
        ls_repo=ls_repo,
        commit_message=commit_message,
        githubrepobranch=githubrepobranch,
        repo_dir=repo_dir,
    )

    github_cfg, repo_owner, repo_name = get_source_repo_config_for_component_reference(
        component=component,
        component_reference=from_ref,
        component_version=from_version,
    )

    release_notes = create_release_notes(
        from_component_ref=from_ref,
        ctx_repo_base_url=component.current_repository_ctx().baseUrl,
        from_github_cfg=github_cfg,
        from_repo_owner=repo_owner,
        from_repo_name=repo_name,
        from_version=from_version,
        to_version=to_version,
    )

    if not release_notes:
        release_notes = pull_request_util.retrieve_pr_template_text()

    pull_request = ls_repo.create_pull(
        title=github.util.PullRequestUtil.calculate_pr_title(
            reference=to_ref,
            from_version=from_version,
            to_version=to_version
        ),
        base=githubrepobranch.branch(),
        head=upgrade_branch_name,
        body=release_notes,
    )

    if merge_policy is MergePolicy.MANUAL:
        return
    # auto-merge - todo: make configurable (e.g. merge method)
    pull_request.merge()
    try:
        ls_repo.ref(f'heads/{upgrade_branch_name}').delete()
    except github3.exceptions.NotFoundError:
        pass

    if after_merge_callback:
        subprocess.run(
            [os.path.join(repo_dir, after_merge_callback)],
            check=True,
            env=cmd_env
        )
Example #13
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
        )