Exemple #1
0
def _get_versions(client, bucket, key):
    """
    Returns all the version IDs for a key ordered by last modified timestamp.
    """
    resp_iterator = client.get_paginator("list_object_versions").paginate(
        Bucket=bucket, Prefix=key)
    try:
        versions = [
            version for page in resp_iterator for version in page["Versions"]
        ]
    except KeyError:
        utils.warning("No versions found\n")
        return ()

    obj_versions = sorted(versions, key=lambda v: v["LastModified"])
    versions = []

    for version in obj_versions:
        if version["Key"] != key:
            continue

        if version["VersionId"] is None or version["VersionId"] == "null":
            continue

        versions.append(version)

    return tuple(versions)
Exemple #2
0
def release_contains(repo: git.Repository, release: Release,
                     commit_oid: git.Oid, name: str):
    if not release.commit:
        utils.warning(f"{name} has a null commit ref\n")
        return "?"

    release_oid = git.Oid(hex=release.commit)
    try:
        in_release = utils.commit_contains(repo, release_oid, commit_oid)
    except utils.CommitNotFound as e:
        utils.warning(f"Error: [{repr(e)}], Project: [{name}]\n")
        in_release = "?"

    return in_release
Exemple #3
0
def get_merged_commits_from_ticket(ticket_id: str) -> Mapping[str, List[str]]:
    # Returns a mapping of repo_name: [commits] for PRs linked to this ticket
    tracker = get_tracker()
    prs = tracker.get_linked_prs(ticket_id)
    if not prs:
        utils.warning("No PRs linked to this ticket\n")

    merged_pr_commits = {}
    for pr in prs:
        if pr.state is PullRequestState.OPEN:
            utils.warning(f"{pr.id} is still open\n")
        elif pr.state is PullRequestState.MERGED:
            merged_pr_commits.setdefault(pr.repo, []).append(pr.merge_commit)

    return merged_pr_commits
Exemple #4
0
    def get_pr_details(self, pr_identifier: str) -> PullRequest:
        parsed_pr_data = PR_URL_RE.fullmatch(pr_identifier)

        req_data = {
            "query": PR_GRAPHQL_QUERY,
            "variables": {
                "owner": parsed_pr_data["owner"],
                "repo_name": parsed_pr_data["repo_name"],
                "pr_number": int(parsed_pr_data["pr_number"]),
            },
        }
        gh_token = os.environ["GITHUB_API_TOKEN"]
        res = requests.post(GH_ENDPOINT,
                            json=req_data,
                            headers={
                                "Authorization": f"bearer {gh_token}"
                            }).json()

        for error in res.get("errors", []):
            err_type = error.get("type")
            err_msg = error.get("message")
            utils.warning(f"{err_type}: {err_msg}\n")
            utils.warning(f"{error}\n")

        pr_details = res["data"]["repository"]["pullRequest"]

        merge_commit = None
        if pr_details["mergeCommit"] is not None:
            merge_commit = pr_details["mergeCommit"]["oid"]

        return PullRequest(
            id=pr_identifier,
            repo=parsed_pr_data["repo_name"],
            title=pr_details["title"],
            state=PullRequestState[pr_details["state"]],
            merge_commit=merge_commit,
        )
Exemple #5
0
def start(
    _,
    name,
    env,
    version=None,
    bucket=None,
    dry=False,
    yes=False,
    rollback=False,
    profile=None,
):
    """
    Deploy a release on an environment.
    """
    client = utils.s3_client(profile)
    repo = utils.git_repo()

    if version is None:
        release = next(get_releases(client, name), None)

    else:
        release = get_release(client, name, int(version))

    if release is None:
        utils.fatal("Release not found")

    if bucket is None:
        bucket = utils.get_config()["deploy"][env]["s3_bucket"]

    last_deploy = next(get_releases(client, name, bucket=bucket), None)

    last_deployed_version = int(last_deploy.version) if last_deploy else 0
    if version is not None:
        since = min(int(version), last_deployed_version)
    else:
        since = last_deployed_version

    releases = list(get_releases(client, name, since=since))

    # the field `commits` is not present in all documents as it was introduced
    # in a later version. if any of the releases doesn't track them, we'll
    # skip the commit filtering to avoid not showing commits in the changelog.
    if any(rel.commits is None for rel in releases):
        commits = None

    else:
        commits = [
            commit for rel in releases if rel.commits for commit in rel.commits
        ]

    if last_deploy is None:
        # first deploy is always None
        changelog = utils.changelog(repo,
                                    release.commit,
                                    None,
                                    keep_only_commits=commits)

        changelog_text = changelog.short_text
        is_rollback = release.rollback

    else:
        # create a changelog from the latest deploy commit
        changelog = utils.changelog(
            repo,
            git.Oid(hex=release.commit),
            git.Oid(hex=last_deploy.commit),
            keep_only_commits=commits,
        )

        changelog_text = changelog.short_text
        is_rollback = changelog.rollback

    action_type = ActionType.automated if config.IS_CONCOURSE else ActionType.manual

    release = dataclasses.replace(
        release,
        changelog=changelog_text,
        timestamp=datetime.now(),
        author=utils.get_author(repo, git.Oid(hex=release.commit)),
        rollback=is_rollback,
        action_type=action_type,
        commits=commits,
    )

    utils.printfmt(release)

    if dry:
        return

    if release.rollback:
        commit_count = len(changelog.logs)
        utils.warning(":warning: This is a rollback! :warning:\n")
        utils.warning(
            f":warning: You are rolling back from {name} v{last_deployed_version} to v{version} :warning:\n"
        )
        utils.warning(
            f":warning: This will remove the above {commit_count} commits from {env} :warning:\n"
        )

        if not rollback:
            utils.error("Missing flag --rollback\n")
            utils.fatal("Aborted!")

    if not yes:

        if release.rollback:
            ok = utils.confirm(
                "Are you sure you want to start a rollback deployment?",
                style=utils.TextStyle.yellow,
            )

            if not ok:
                utils.fatal("Aborted!")

        ok = utils.confirm("Are you sure you want to start this deployment?")
        if not ok:
            utils.fatal("Aborted!")

    put_release(client, bucket, name, release)
    utils.success("Started new deployment :rocket:\n")
Exemple #6
0
def new(
    ctx,
    name,
    commit=None,
    version=None,
    dry=False,
    yes=False,
    image_name=None,
    image_id=None,
    rollback=False,
    filter_files_path=None,
    profile=None,
):
    """
    Create a new release.
    """
    repo = utils.git_repo()

    client = utils.s3_client(profile)
    latest = next(get_releases(client, name), None)
    latest_oid = git.Oid(hex=latest.commit) if latest else None

    if commit is None:
        commit = "HEAD"

    commit_oid = utils.revparse(repo, commit)

    if version is None:
        # create next version
        version = 1 if latest is None else latest.version + 1

    else:
        version = int(version)

    if image_id is None:
        image_id = _get_image_id(ctx,
                                 commit_oid,
                                 name=name,
                                 image_name=image_name)

        if image_id is None:
            utils.fatal("Image not found")

    keep_only_files = None
    if filter_files_path:
        with open(filter_files_path) as fp:
            keep_only_files = [line.strip() for line in fp]

    changelog = utils.changelog(repo,
                                commit_oid,
                                latest_oid,
                                keep_only_files=keep_only_files)

    action_type = ActionType.automated if config.IS_CONCOURSE else ActionType.manual

    release = Release(
        version=version,
        commit=commit_oid.hex,
        changelog=changelog.short_text,
        version_id="",
        image=image_id,
        timestamp=datetime.now(),
        author=utils.get_author(repo, commit_oid),
        rollback=changelog.rollback,
        action_type=action_type,
        commits=[commit.hex for commit in changelog.logs],
    )

    utils.printfmt(release)

    if dry:
        return

    if release.rollback:
        utils.warning("This is a rollback! :warning:\n")

        if not rollback:
            utils.warning("Missing flag --rollback\n")
            utils.fatal("Aborted!")

    if not yes:

        if release.rollback:
            ok = utils.confirm(
                "Are you sure you want to create a rollback release?",
                style=utils.TextStyle.yellow,
            )

            if not ok:
                utils.fatal("Aborted!")

        ok = utils.confirm("Are you sure you want to create this release?")
        if not ok:
            utils.fatal("Aborted!")

    put_release(client, _get_bucket(), name, release)

    utils.success("Created new release :tada:\n")
Exemple #7
0
def new(
    ctx,
    name,
    commit=None,
    version=None,
    dry=False,
    yes=False,
    image_name=None,
    rollback=False,
):
    """
    Create a new release.
    """
    repo = utils.git_repo()

    client = utils.s3_client()
    latest = next(get_releases(client, name), None)

    if commit is None:
        # get last commit
        commit = next(utils.git_log(repo), None)
        commit = commit and commit.hex

    if version is None:
        # crate next version
        version = 1 if latest is None else latest.version + 1

    else:
        version = int(version)

    image_id = _get_image_id(ctx, image_name or name, commit)
    if image_id is None:
        LOG.critical("image ID not found")
        sys.exit(1)

    changelog = utils.changelog(repo, commit, latest and latest.commit)

    release = Release(
        version=version,
        commit=commit,
        changelog=changelog.text,
        version_id="",
        image=image_id,
        timestamp=datetime.now(),
        author=utils.get_author(repo),
        rollback=changelog.rollback,
    )

    utils.printfmt(release)

    if dry:
        return

    if release.rollback:
        utils.warning("this is a rollback! :warning:\n")

        if not rollback:
            utils.warning("missing flag --rollback\n")
            utils.error("aborted!\n")
            sys.exit(1)

    if not yes:

        if release.rollback:
            ok = utils.confirm("sure you want to start a rollback?",
                               style=utils.TextStyle.warning)

            if not ok:
                utils.error("aborted!\n")
                sys.exit(1)

        ok = utils.confirm("sure you want to create this release?")
        if not ok:
            sys.exit(1)

    put_release(client,
                utils.get_config()["release"]["s3_bucket"], name, release)

    utils.success("created new release :tada:\n")
Exemple #8
0
def start(_,
          name,
          env,
          version=None,
          bucket=None,
          dry=False,
          yes=False,
          rollback=False):
    """
    Deploy a release on an environment.
    """
    client = utils.s3_client()
    repo = utils.git_repo()

    if version is None:
        release = next(get_releases(client, name), None)

    else:
        release = get_release(client, name, int(version))

    if release is None:
        LOG.critical("Release not found")
        sys.exit(1)

    if bucket is None:
        bucket = utils.get_config()["deploy"][env]["s3_bucket"]

    last_deploy = next(get_releases(client, name, bucket=bucket), None)
    if last_deploy is None:
        # first deploy is always None
        changelog_text = release.changelog
        is_rollback = release.rollback

    else:
        # create a changelog from the latest deploy commit
        changelog = utils.changelog(repo, release.commit, last_deploy.commit)

        changelog_text = changelog.text
        is_rollback = changelog.rollback

    release = dataclasses.replace(
        release,
        changelog=changelog_text,
        timestamp=datetime.now(),
        author=utils.get_author(repo),
        rollback=is_rollback,
    )

    utils.printfmt(release)

    if dry:
        return

    if release.rollback:
        utils.warning("This is a rollback! :warning:\n")

        if not rollback:
            utils.warning("Missing flag --rollback\n")
            utils.error("Aborted!\n")
            sys.exit(1)

    if not yes:

        if release.rollback:
            ok = utils.confirm(
                "Are you sure you want to start a rollback deployment?",
                style=utils.TextStyle.warning,
            )

            if not ok:
                utils.error("Aborted!\n")
                sys.exit(1)

        ok = utils.confirm("Are you sure you want to start this deployment?")
        if not ok:
            utils.error("Aborted!\n")
            sys.exit(1)

    put_release(client, bucket, name, release)
    utils.success("Started new deployment :rocket:\n")