Exemple #1
0
def update_version(to: str,
                   _version: version,
                   *,
                   develop: bool = False) -> Tuple[bool, str]:
    """Use pontos-version to update the version.

    Arguments:
        to: The version (str) that will be set
        _version: Version module
        develop: Wether to set version to develop or not (bool)

    Returns:
       executed: True if successfully executed, False else
       filename: The filename of the project definition
    """
    args = ['--quiet']
    args.append('update')
    args.append(to)
    if develop:
        args.append('--develop')
    executed, filename = _version.main(leave=False, args=args)

    if not executed:
        if filename == "":
            error("No project definition found.")
        else:
            error(f"Unable to update version {to} in {filename}")

    return executed, filename
Exemple #2
0
def calculate_calendar_version() -> str:
    """find the correct next calendar version by checking latest version and
    the today's date"""

    current_version_str: str = get_current_version()
    current_version = Version(current_version_str)

    today = datetime.date.today()

    if (current_version.major < today.year % 100
            or current_version.minor < today.month):
        release_version = Version(
            f'{str(today.year  % 100)}.{str(today.month)}.0')
        return str(release_version)
    elif (current_version.major == today.year % 100
          and current_version.minor == today.month):
        if current_version.dev is None:
            release_version = Version(
                f'{str(today.year  % 100)}.{str(today.month)}.'
                f'{str(current_version.micro + 1)}')
        else:
            release_version = Version(
                f'{str(today.year  % 100)}.{str(today.month)}.'
                f'{str(current_version.micro)}')
        return str(release_version)
    else:
        error(f"'{str(current_version)}' is higher than "
              f"'{str(today.year  % 100)}.{str(today.month)}'.")
        sys.exit(1)
Exemple #3
0
def download_assets(
    assets_url: str,
    path: Path,
    requests_module: requests,
) -> List[str]:
    """Download all .tar.gz and zip assets of a github release"""

    file_paths = []
    if not assets_url:
        return file_paths

    if assets_url:
        assets_response = requests_module.get(assets_url)
        if assets_response.status_code != 200:
            error(
                f"Wrong response status code {assets_response.status_code} for "
                f" request {assets_url}")
            out(json.dumps(assets_response.text, indent=4, sort_keys=True))
        else:
            assets_json = assets_response.json()
            for asset_json in assets_json:
                asset_url: str = asset_json.get('browser_download_url', '')
                name: str = asset_json.get('name', '')
                if name.endswith('.zip') or name.endswith('.tar.gz'):
                    asset_path = download(
                        asset_url,
                        name,
                        path=path,
                        requests_module=requests_module,
                    )
                    file_paths.append(asset_path)

    return file_paths
Exemple #4
0
def upload_assets(
    username: str,
    token: str,
    file_paths: List[Path],
    github_json: Dict,
    path: Path,
    requests_module: requests,
) -> bool:
    """Function to upload assets

    Arguments:
        username: The GitHub username to use for the upload
        token: That username's GitHub token
        file_paths: List of paths to asset files
        github_json: The github dictionary, containing relevant information
            for the upload
        path: the python pathlib.Path module
        requests_module: the python request module

    Returns:
        True on success, false else
    """
    info(f"Uploading assets: {[str(p) for p in file_paths]}")

    asset_url = github_json['upload_url'].replace('{?name,label}', '')
    paths = [path(f'{str(p)}.asc') for p in file_paths]

    headers = {
        'Accept': 'application/vnd.github.v3+json',
        'content-type': 'application/octet-stream',
    }
    auth = (username, token)

    for file_path in paths:
        to_upload = file_path.read_bytes()
        resp = requests_module.post(
            f"{asset_url}?name={file_path.name}",
            headers=headers,
            auth=auth,
            data=to_upload,
        )

        if resp.status_code != 201:
            error(f"Wrong response status {resp.status_code}"
                  f" while uploading {file_path.name}")
            out(json.dumps(resp.text, indent=4, sort_keys=True))
            return False
        else:
            ok(f"Uploaded: {file_path.name}")

    return True
Exemple #5
0
def get_current_version() -> str:
    """Get the current Version from a pyproject.toml or
    a CMakeLists.txt file"""

    available_cmds = [
        ('CMakeLists.txt', CMakeVersionCommand),
        ('pyproject.toml', PontosVersionCommand),
    ]
    for file_name, cmd in available_cmds:
        project_definition_path = Path.cwd() / file_name
        if project_definition_path.exists():
            ok(f"Found {file_name} project definition file.")
            current_version: str = cmd().get_current_version()
            return current_version

    error("No project settings file found")
    sys.exit(1)
Exemple #6
0
def main(
    shell_cmd_runner=lambda x: subprocess.run(
        x,
        shell=True,
        check=True,
        errors="utf-8",  # use utf-8 encoding for error output
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
    ),
    _path: Path = Path,
    _requests: requests = requests,
    _version: version = version,
    _changelog: changelog = changelog,
    leave: bool = True,
    args=None,
):
    username, token, parsed_args = parse(args)
    term = Terminal()
    _set_terminal(term)

    term.bold_info(f'pontos-release => {parsed_args.func.__name__}')

    with term.indent():
        try:
            if not parsed_args.func(
                    shell_cmd_runner,
                    parsed_args,
                    path=_path,
                    username=username,
                    token=token,
                    changelog_module=_changelog,
                    requests_module=_requests,
                    version_module=_version,
            ):
                return sys.exit(1) if leave else False
        except subprocess.CalledProcessError as e:
            error(f'Could not run command "{e.cmd}". Error was:\n\n{e.stderr}')
            sys.exit(1)

    return sys.exit(0) if leave else True
Exemple #7
0
def sign(
    shell_cmd_runner: Callable,
    args: argparse.Namespace,
    *,
    path: Path,
    username: str,
    token: str,
    requests_module: requests,
    **_kwargs,
) -> bool:

    project: str = (args.project if args.project is not None else
                    get_project_name(shell_cmd_runner))
    space: str = args.space
    git_tag_prefix: str = args.git_tag_prefix
    release_version: str = (args.release_version if args.release_version
                            is not None else get_current_version())
    signing_key: str = args.signing_key

    headers = {'Accept': 'application/vnd.github.v3+json'}

    git_version: str = f'{git_tag_prefix}{release_version}'

    base_url = (f"https://api.github.com/repos/{space}/{project}"
                f"/releases/tags/{git_version}")

    response = requests_module.get(
        base_url,
        headers=headers,
    )
    if response.status_code != 200:
        error(f"Wrong response status code {response.status_code} for request "
              f"{base_url}")
        out(json.dumps(response.text, indent=4, sort_keys=True))
        return False

    github_json = json.loads(response.text)
    zip_path = download(
        github_json['zipball_url'],
        f"{project}-{release_version}.zip",
        path=path,
        requests_module=requests_module,
    )
    tar_path = download(
        github_json['tarball_url'],
        f"{project}-{release_version}.tar.gz",
        path=path,
        requests_module=requests_module,
    )

    file_paths = [zip_path, tar_path]

    asset_paths = download_assets(
        github_json.get('assets_url'),
        path=path,
        requests_module=requests_module,
    )

    file_paths.extend(asset_paths)

    for file_path in file_paths:
        info(f"Signing {file_path}")

        shell_cmd_runner(
            f"gpg --default-key {signing_key} --yes --detach-sign --armor "
            f"{file_path}")

    return upload_assets(
        username,
        token,
        file_paths,
        github_json,
        path=path,
        requests_module=requests_module,
    )
Exemple #8
0
def release(
    shell_cmd_runner: Callable,
    args: argparse.Namespace,
    *,
    path: Path,
    version_module: version,
    username: str,
    token: str,
    requests_module: requests,
    changelog_module: changelog,
    **_kwargs,
) -> bool:
    project: str = (args.project if args.project is not None else
                    get_project_name(shell_cmd_runner))
    space: str = args.space
    git_signing_key: str = (args.git_signing_key
                            if args.git_signing_key is not None else
                            find_signing_key(shell_cmd_runner))
    git_remote_name: str = (args.git_remote_name
                            if args.git_remote_name is not None else '')
    git_tag_prefix: str = args.git_tag_prefix
    release_version: str = (args.release_version if args.release_version
                            is not None else get_current_version())
    next_version: str = (args.next_version if args.next_version is not None
                         else get_next_dev_version(release_version))

    info("Pushing changes")

    shell_cmd_runner(f"git push --follow-tags {git_remote_name}")

    info("Creating release")
    changelog_text: str = path(RELEASE_TEXT_FILE).read_text()

    headers = {'Accept': 'application/vnd.github.v3+json'}

    base_url = f"https://api.github.com/repos/{space}/{project}/releases"
    git_version = f'{git_tag_prefix}{release_version}'
    response = requests_module.post(
        base_url,
        headers=headers,
        auth=(username, token),
        json=build_release_dict(
            git_version,
            changelog_text,
            name=f"{project} {release_version}",
        ),
    )
    if response.status_code != 201:
        error(f"Wrong response status code: {response.status_code}")
        error(json.dumps(response.text, indent=4, sort_keys=True))
        return False

    path(RELEASE_TEXT_FILE).unlink()

    # set to new version add skeleton
    change_log_path = path.cwd() / 'CHANGELOG.md'

    executed, filename = update_version(next_version,
                                        version_module,
                                        develop=True)
    if not executed:
        return False

    updated = changelog_module.add_skeleton(
        markdown=change_log_path.read_text(),
        new_version=release_version,
        project_name=project,
        git_tag_prefix=git_tag_prefix,
        git_space=space,
    )
    change_log_path.write_text(updated)

    commit_msg = (f'Automatic adjustments after release\n\n'
                  f'* Update to version {next_version}\n'
                  f'* Add empty changelog after {release_version}')
    commit_files(
        filename,
        commit_msg,
        shell_cmd_runner,
        git_signing_key=git_signing_key,
    )

    shell_cmd_runner(f"git push --follow-tags {git_remote_name}")

    return True
Exemple #9
0
 def test_error(self):
     error('foo bar')
     self.term.error.assert_called_with('foo bar')