Exemple #1
0
def add_assets(release, github_token, path):
    """
    Add an asset at `path` to a specific release

    Args:
        release: The release object from github
        github_token: The token to modify the release
        path: The path of the file to upload

    Returns:
        The asset object from github
    """
    filename = os.path.basename(path)
    url = release["upload_url"].replace("{?name,label}", "?name=" + filename)
    headers = get_headers(github_token)
    headers["Content-Type"] = magic.from_file(path, mime=True)
    logging.info("Adding {} at {} to release".format(filename, path))
    with open(path, "rb") as fin:
        response = requests.post(url, headers=headers, data=fin)
        if response.status_code == 422:
            raise ReleaseException(
                "A file by the name of {} is already attached to {}".format(
                    filename, release["html_url"]))
        response.raise_for_status()
        ret = response.json()
    logging.info("Added {} to release at {}".format(filename,
                                                    release["html_url"]))
    return ret
Exemple #2
0
def prompt_for_message(summaries):
    """
    Prompts the user for a release message in an editor, showing them the commits since
    last release, and returns what the user specified

    Args:
        summaries: The commit summaries to display to the user
    """
    summaries_text = "\n".join(("# " + line for line in summaries))
    temp_fd, temp_path = tempfile.mkstemp()
    try:
        with open(temp_path, "w") as fout:
            fout.write(MESSAGE_TEMPLATE.format(commit_messages=summaries_text))
        editor = os.environ.get("EDITOR", "vim")
        run([editor, temp_path])
        with open(temp_path, "r") as fin:
            message = "\n".join(line for line in fin
                                if not line.startswith("#"))
            message = message.strip()
        if not message:
            raise ReleaseException("No valid message was provided")
        return message
    finally:
        if os.path.exists(temp_path):
            os.remove(temp_path)
Exemple #3
0
def prompt_for_message(html_url, summaries):
    """
    Prompts the user for a release message in an editor, showing them the commits since
    last release, and returns what the user specified

    Args:
        html_url:  The url to see the difference between the current commit and the
                   commit for the previous release.
        summaries: The commit summaries to display to the user
    """
    default_message = create_default_message(html_url)
    summaries_text = "\n".join(("# " + line for line in summaries))
    full_message = MESSAGE_PROMPT_TEMPLATE.format(
        default_message=default_message, commit_messages=summaries_text)
    temp_fd, temp_path = tempfile.mkstemp()
    try:
        with open(temp_path, "w") as fout:
            fout.write(full_message)
        editor = os.environ.get("EDITOR", "vim")
        run([editor, temp_path])
        with open(temp_path, "r") as fin:
            message = "\n".join(line for line in fin
                                if not line.startswith("#"))
            message = message.strip()
        if not message:
            raise ReleaseException("No valid message was provided")
        return message
    finally:
        if os.path.exists(temp_path):
            os.remove(temp_path)
Exemple #4
0
def validate_repo_upstream(args):
    """ Make sure we're in the right repository, not a fork """
    output = subprocess.check_output(["git", "remote", "get-url", "origin"],
                                     encoding="utf-8").strip()
    if output not in args.valid_git_upstreams:
        raise ReleaseException(
            "Releases may only be published from the upstream OSS buck repository"
        )
Exemple #5
0
def get_token(token_file):
    """
    Reads the first line from token_file to get a token
    """
    with open(token_file, "r") as fin:
        ret = fin.read().strip()
    if not ret:
        raise ReleaseException("No valid token found in {}".format(token_file))
    return ret
Exemple #6
0
def validate_tap(homebrew_dir, tap_repository, version):
    logging.info("Validating that brew installs with new tap information")
    brew_target = tap_repository + "/buck"
    brew(homebrew_dir, ["uninstall", "--force", brew_target])
    brew(homebrew_dir, ["install", brew_target])
    output = (brew(homebrew_dir, ["info", brew_target],
                   capture_output=True).stdout.decode("utf-8").splitlines()[0])
    if "{}/buck: stable {}".format(tap_repository, version) not in output:
        raise ReleaseException(
            "Expected version {} to be installed, but got this from `brew info {}`: {}"
            .format(version, tap_repository, output))
Exemple #7
0
def publish_chocolatey(chocolatey_file, chocolatey_api_key):
    """ Publish a nupkg to chocolatey """
    url = "https://chocolatey.org/api/v2/package"
    headers = {"X-NuGet-ApiKey": chocolatey_api_key}

    logging.info("Publishing chocolatey package at {}".format(chocolatey_file))
    with open(chocolatey_file, "rb") as fin:
        response = requests.put(url, headers=headers, data=fin)
    if response.status_code == 409:
        raise ReleaseException("Package and version already exists on chocolatey")
    response.raise_for_status()
    logging.info("Published chocolatey package at {}".format(chocolatey_file))
Exemple #8
0
def build_bottle_file(
    homebrew_dir,
    tap_repository,
    tap_path,
    release_version,
    target_macos_version,
    output_dir,
):
    """
    Builds the actual bottle file via brew

    Args:
        tap_repository: The name of the tap repository
        tap_path: The local path to the given tap repository
        release_version: The version that should be built (no "v" prefix)
        target_macos_version: The target macos short nameto use in the resulting path
        output_dir: The directory to move the build artifact to after building

    Returns:
        The path to the bottle.tar.gz
    """
    brew_target = tap_repository + "/buck"

    logging.info("Building bottle")
    # Cool, so install --force will still not rebuild. Uninstall, and just don't
    # care if the uninstall fails
    brew(homebrew_dir, ["uninstall", "--force", brew_target],
         tap_path,
         check=False)
    brew(homebrew_dir, ["install", "--force", "--build-bottle", brew_target],
         tap_path)
    logging.info("Creating bottle file")
    brew(
        homebrew_dir,
        ["bottle", "--no-rebuild", "--skip-relocation", brew_target],
        tap_path,
    )
    logging.info("Created bottle file")

    bottle_filename = "buck-{ver}.{macos_ver}.bottle.tar.gz".format(
        ver=release_version, macos_ver=target_macos_version)
    bottle_path = os.path.join(output_dir, bottle_filename)
    bottles = glob.glob(
        os.path.join(tap_path,
                     "buck--{}*.bottle.tar.gz".format(release_version)))
    if len(bottles) != 1:
        raise ReleaseException(
            "Got an invalid number of bottle files ({} files: {})".format(
                len(bottles), " ".join(bottles)))
    shutil.move(bottles[0], bottle_path)
    return bottle_path
Exemple #9
0
def validate_environment(args):
    """ Make sure we can build """

    validate_repo_upstream(args)
    if args.build_deb:
        ret = docker(
            args.docker_linux_host,
            ["info", "-f", "{{.OSType}}"],
            check=False,
            capture_output=True,
        )
        host = args.docker_linux_host or "localhost"
        if ret.returncode != 0:
            raise ReleaseException(
                "docker info on linux host {} failed. debs cannot be built",
                host)
        host_os = ret.stdout.decode("utf-8").strip()
        if host_os != "linux":
            raise ReleaseException(
                "docker info on host {} returned type '{}' not 'linux'. debs cannot be built",
                host,
                host_os,
            )
    if args.build_chocolatey:
        ret = docker(
            args.docker_windows_host,
            ["info", "-f", "{{.OSType}}"],
            check=False,
            capture_output=True,
        )
        host = args.docker_windows_host or "localhost"
        if ret.returncode != 0:
            raise ReleaseException(
                "docker info on windows host {} failed. chocolatey nupkgs cannot be built",
                host,
            )
        host_os = ret.stdout.decode("utf-8").strip()
        if host_os != "windows":
            raise ReleaseException(
                "docker info on host {} returned type '{}' not 'windows'. chocolatey nupkgs cannot be built",
                host,
                host_os,
            )
    if args.build_homebrew:
        if args.homebrew_dir:
            if not os.path.exists(args.homebrew_dir):
                raise ReleaseException(
                    "Specified homebrew path, {}, does not exist",
                    args.homebrew_dir)
            brew_path = os.path.join(args.homebrew_dir, "bin", "brew")
            try:
                ret = run([brew_path, "--version"])
            except Exception:
                raise ReleaseException(
                    "{} --version failed. bottles cannot be created",
                    brew_path)
Exemple #10
0
def build_bottle_file(
    homebrew_dir,
    tap_repository,
    tap_path,
    release_version,
    target_macos_version,
    output_dir,
):
    """
    Builds the actual bottle file via brew

    Args:
        tap_repository: The name of the tap repository
        tap_path: The local path to the given tap repository
        release_version: The version that should be built (no "v" prefix)
        target_macos_version: The target macos short nameto use in the resulting path
        output_dir: The directory to move the build artifact to after building

    Returns:
        The path to the bottle.tar.gz
    """
    brew_target = tap_repository + "/buck"

    # So, if buck wasn't linked to begin with, we can't unlink it. Ideally the install
    # fails down the road. There is, so far as I could tell, no way to verify if
    # a formula is linked :/
    logging.info("Unlinking buck")
    brew(homebrew_dir, ["unlink", brew_target], tap_path, check=False)

    logging.info("Building bottle")
    # If there is still a buck file that exists, move it out of the way for now
    # This should generally not be an issue outside of FB
    with temp_move_file("/usr/local/bin/buck") as moved:
        # Cool, so install --force will still not rebuild. Uninstall, and just don't
        # care if the uninstall fails
        brew(
            homebrew_dir,
            ["uninstall", "--force", "--build-bottle", brew_target],
            tap_path,
            check=False,
        )
        brew(
            homebrew_dir,
            ["install", "--force", "--build-bottle", brew_target],
            tap_path,
        )
        logging.info("Creating bottle file")
        brew(
            homebrew_dir,
            ["bottle", "--no-rebuild", "--skip-relocation", brew_target],
            tap_path,
        )
        logging.info("Created bottle file")
        if moved:
            # Make sure to unlink again so that we can move the original file back
            logging.info("Unlinking buck again")
            brew(homebrew_dir, ["unlink", brew_target], tap_path)

    bottle_filename = "buck-{ver}.{macos_ver}.bottle.tar.gz".format(
        ver=release_version, macos_ver=target_macos_version)
    bottle_path = os.path.join(output_dir, bottle_filename)
    bottles = glob.glob(
        os.path.join(tap_path,
                     "buck--{}*.bottle.tar.gz".format(release_version)))
    if len(bottles) != 1:
        raise ReleaseException(
            "Got an invalid number of bottle files ({} files: {})".format(
                len(bottles), " ".join(bottles)))
    shutil.move(bottles[0], bottle_path)
    return bottle_path