Example #1
0
def check_for_merge(project: CSharpProject, require_branch: str) -> None:
    """Checks project before merging of the development branch into stable branch"""
    with project.context(project.BLOCK_GIT):
        if project.repo:
            if project.repo.active_branch.name != require_branch:
                project.error(f"Not on the '{require_branch}' branch")
        if project.latest_tag and not project.git_tag_version:
            click.echo(
                f"WARNING: Unable to parse git tag: {project.latest_tag.name}")
    with project.context(project.BLOCK_VERSIONS):
        if (not project.assembly_version
                or project.assembly_version != project.change_log_version):
            project.error(
                f"Assembly version should match the latest ChangeLog entry"
                f"\nAssembly:  {project.assembly_version!r}"
                f"\nChangeLog: {project.change_log_version!r}")
        elif (project.assembly_version and project.git_tag_version
              and project.git_tag_version >= project.assembly_version):
            project.error(
                f"Git tag version should be be older than Assembly version"
                f"\nAssembly:  {project.assembly_version!r}"
                f"\nGit tag:   {project.git_tag_version!r}")
    if not project.context.failed:
        click.echo(
            f"You can merge the '{require_branch}' into your stable branch\n"
            f"in {project.path}\n"
            f"Upcoming release:\n"
            f"{project.assembly_info!r}")
        if project.git_tag_version is not None:
            click.echo(f"Previous release:\n{project.git_tag_version!r}")
    sys_exit(project)
Example #2
0
def remove_tag_by_version(project: CSharpProject) -> None:
    """
    Removes git tag named after AssemblyVersion from AssemblyInfo.cs

    Some conditions are checked before the tag is removed:

    \b
        - the git tag with this version should be the latest tag
    """
    if not project.repo:
        return
    with project.context(project.BLOCK_GIT):
        if not project.latest_tag:
            project.error("No tag found")
            return
        if not project.git_tag_version:
            project.error(
                f"Unable to parse latest git tag: {project.latest_tag.name}", )
            return
    with project.context(project.BLOCK_VERSIONS):
        if project.assembly_version != project.git_tag_version:
            project.error(
                "Assembly version does not match the latest git tag:\n"
                f"Assembly: {project.assembly_version!r}\n"
                f"Git tag:  {project.git_tag_version!r}")
            return
    if not project.context.failed:
        with project.context(project.BLOCK_GIT):
            click.echo(f"Removing the tag: {project.git_tag_version!r}")
            project.repo.delete_tag(project.latest_tag)
            project.update_latest_tag()
            click.echo(f"Latest tag now: {project.git_tag_version!r}")
    sys_exit(project)
Example #3
0
def create_archive(project: CSharpProject) -> None:
    """Creates a KSP mod distribution archive"""
    if (
        not project.assembly_title
        or not project.game_data_path
        or not project.archives_path
    ):
        sys.exit(0)
    project.context.reset()
    exclude = exclude_backups + project.mod_config.exclude_patterns
    with project.context(project.BLOCK_VERSIONS):
        if not project.versions_match(archive=False) or not project.dll_version:
            project.error(f"Versions do not match\n{project.versions_info()}")
            sys_exit(project)
    with project.context(project.BLOCK_ARCHIVE):
        archive_filename = f"{project.assembly_title}-{project.dll_version}.zip"
        archive_path = project.archives_path / archive_filename
        click.echo(f"Creating: {archive_path.relative_to(project.path)}")
        with ZipFile(archive_path, "w", ZIP_DEFLATED) as zip_file:
            zip_dir(zip_file, project.game_data_path, exclude)
            for include_folder in project.mod_config.additional_data_paths:
                include_path = Path(include_folder)
                if not include_path.is_absolute():
                    include_path = project.path / include_path
                zip_dir(zip_file, include_path, exclude)
    sys_exit(project)
Example #4
0
def check_for_release(project: CSharpProject, require_branch: str) -> None:
    """Checks project before building the release archive"""
    with project.context(project.BLOCK_GIT):
        if project.repo:
            if project.repo.active_branch.name != require_branch:
                project.error(f"Not on the '{require_branch}' branch")
        if project.latest_tag is None:
            project.error(f"No tags in the repo at {project.path}")
        elif project.repo:
            if project.latest_tag.commit != project.repo.head.commit:
                project.error(
                    f"Latest tag {project.latest_tag.name} is not on the HEAD commit"
                )
            if project.git_tag_version is None:
                project.error(
                    f"Unable to parse git tag: {project.latest_tag.name}")
    with project.context(project.BLOCK_VERSIONS):
        if not project.versions_match(archive=False):
            project.error(
                f"Versions do not match\n{project.versions_info(archive=False)}"
            )
        else:
            click.echo(
                f"All versions match\n{project.versions_info(archive=False)}")
    sys_exit(project)
Example #5
0
def create_changelog(project: CSharpProject, reformat: bool,
                     dry_run: bool) -> None:
    """
    Creates new changelog file if it does not exist.

    If the file exists and the --reformat flag is provided,
    the change log from the file is reformatted and saved back.
    """
    project.context.reset(project.BLOCK_GIT)
    if project.context.failed:
        sys_exit(project)
    rel_path = project.change_log.filepath.relative_to(project.path)
    with project.context(project.BLOCK_CHANE_LOG):
        if project.change_log.filepath.exists():
            if not reformat:
                click.echo(f"Change log already exists: {rel_path}")
                sys_exit(project)
            if not project.change_log.has_changed:
                click.echo(f"Change log has not changed: {rel_path}")
                sys_exit(project)
            click.echo(f"Reformatting existing change log from: {rel_path}")
        else:
            click.echo(f"Creating new change log from: {rel_path}")
        if dry_run:
            click.echo(project.change_log)
        else:
            project.change_log.save()
    sys_exit(project)
Example #6
0
def create_changelog_entry(project: CSharpProject, update: bool,
                           dry_run: bool) -> None:
    """
    Creates new change log entry for the current AssemblyVersion.

    If the entry already exists and the --update flag is provided,
    the entry is replaced with the newly generated one.
    """
    if not project.assembly_version or not project.repo:
        sys_exit(project)
    with project.context(project.BLOCK_GIT):
        if project.assembly_version <= project.git_tag_version:
            project.error(
                "Assembly version is not greater than the latest git tag.\n"
                "Did you forget to update the AssemblyVersion?")
            sys_exit(project)
        if project.latest_tag and project.latest_tag.commit == project.repo.head.commit:
            project.error(
                "Latest git tag is on HEAD. Nothing to write into the change log."
            )
            sys_exit(project)
    project.context.reset()
    with project.context(project.BLOCK_CHANE_LOG):
        entry = project.change_log[project.assembly_version]
        if entry and not update:
            project.error(
                f"Change log entry already exists for: {project.assembly_version}"
            )
            sys_exit(project)
        new_entry = "\n".join(
            format_commit_message(commit) for commit in iter_commits_until(
                project.repo, project.latest_tag)).strip()
        if new_entry != entry:
            changelog_version = ChangeLogVersion.clone(
                project.assembly_version,
                title=f"/ {project.assembly_version.date:%Y-%m-%d}",
            )
            project.change_log[changelog_version] = new_entry
        if project.change_log.is_dirty:
            click.echo(
                f"Adding new change log entry for: {project.assembly_version}\n"
                f"------------------------\n{new_entry}\n------------------------"
            )
            if not dry_run:
                project.change_log.save()
    sys_exit(project)
Example #7
0
def upload_to_spacedock(
    project: CSharpProject,
) -> None:
    if not project.mod_config.spacedock_mod_id or not project.mod_config.archive_path:
        sys.exit(0)
    with project.context(project.BLOCK_VERSIONS):
        # see if locally everything matches
        if not project.versions_match():
            project.error(f"Versions do not match\n{project.versions_info()}")
            sys_exit(project)
    with project.context(project.BLOCK_SPACEDOCK, SpacedockError):
        if (
            not project.assembly_info
            or not project.assembly_version
            or not project.archive_version
        ):
            sys_exit(project)
        if not project.assembly_info.max_ksp_version:
            project.error("No MAX KSP Version found in AssemblyInfo")
            sys_exit(project)
        # get mod info from spacedock
        user = spacedock.login(".")
        mod_id = project.mod_config.spacedock_mod_id
        mod = user.get_mod(mod_id, reload=True)
        if not mod:
            project.error(f"Mod {mod_id} does not belong to {user.username}")
            sys_exit(project)
        # check if we already have the release for this version
        published_version = mod.get_version(project.assembly_version)
        if published_version:
            project.error(
                f"Release already exists: {published_version.friendly_version} "
                f"at {published_version.download_url}"
            )
            sys_exit(project)
        # get the change log entry for the release body
        change_log = project.change_log[project.assembly_version]
        if not change_log:
            project.error(
                f"Unable to get change log entry for: {project.assembly_version}"
            )
            sys_exit(project)
        # update the mod and reload its info
        mod.update(
            project.assembly_version.as_str_without_prefix,
            change_log,
            project.assembly_info.max_ksp_version.as_str_without_prefix,
            project.archive_version.filepath,
        )
        click.echo(f"Successfully published release {project.assembly_version}")
        try:
            mod = mod.reload()
            new_version = mod.get_version(project.assembly_version)
            if new_version:
                click.echo(f"Download URL: {new_version.download_url}")
            else:
                click.echo("WARNING: new version not found!", err=True)
        except SpacedockError as e:
            click.echo(f"But could not reload mod info: {e}", err=True)
    sys_exit(project)
Example #8
0
def set_auth(project: CSharpProject, username, password) -> None:
    with project.context(project.BLOCK_SPACEDOCK, SpacedockError):
        spacedock.set_auth(username, password, ".")
    sys_exit(project)
Example #9
0
def create_tag_by_version(project: CSharpProject, require_branch: str) -> None:
    """
    Creates lightweight git tag named after AssemblyVersion from AssemblyInfo.cs
    The version is prefixed with "v", e.g. "v1.2.3".

    Some conditions are checked before the tag is created:

    \b
        - repository should be on the --required-branch
        - a changelog file should exist, containing the same version as AssemblyInfo.cs
          E.g. if [assembly: AssemblyVersion("3.8.0")] is found in AssemblyInfo.cs,
          The sub-string "v3.8.0" should be present in the changelog file before other
          version-like sub-strings.
        - a git tag with this version should not exist
        - the latest existing git tag with a version should not be on HEAD commit
    """
    with project.context(project.BLOCK_GIT):
        if not project.repo:
            sys_exit(project)
        if project.repo.active_branch.name != require_branch:
            project.error(f"Not on the '{require_branch}' branch")
            sys_exit(project)
        if project.latest_tag:
            if project.latest_tag.commit == project.repo.head.commit:
                project.error(
                    f"The latest git tag {project.latest_tag.name} is on the HEAD"
                )
                sys_exit(project)
            if not project.git_tag_version:
                click.echo(
                    f"WARNING: Unable to parse latest git tag: {project.latest_tag.name}",
                    err=True,
                )
            else:
                if project.git_tag_version > project.assembly_version:
                    project.error(
                        f"Assembly version {project.assembly_version} is less "
                        f"than the git tag version {project.git_tag_version}.\n"
                        f"Did you forget to update AssemblyInfo and ChangeLog?",
                    )
                    sys_exit(project)
                if project.git_tag_version == project.assembly_version:
                    project.error(
                        f"Assembly version {project.assembly_version} "
                        "is equal to the latest git tag version.\n"
                        "You have to investigate and remove the tag manually.",
                    )
                    sys_exit(project)
    with project.context(project.BLOCK_CHANE_LOG):
        if (project.assembly_version
                and project.assembly_version > project.change_log_version):
            project.error(
                f"Assembly version {project.assembly_version} is greater "
                f"than the ChangeLog version {project.change_log_version}\n"
                f"Fill in the changelog entry for {project.assembly_version}",
            )
            sys_exit(project)
    if not project.context.failed:
        click.echo(
            f"Creating new lightweight tag at the HEAD of the '{require_branch}' branch:\n"
            f"{project.assembly_version!r} on {project.repo.head.commit.hexsha[:7]} <- HEAD"
        )
        if project.git_tag_version:
            click.echo(f"{project.git_tag_version!r}")
        with project.context(project.BLOCK_GIT):
            new_tag = project.repo.create_tag(f"{project.assembly_version}")
            new_tag_version = get_git_tag_version(new_tag)
            click.echo(f"Created new tag: {new_tag_version!r}")
    sys_exit(project)
Example #10
0
def upload_to_github(project: CSharpProject, update) -> None:
    if not project.mod_config.github_url or not project.mod_config.archive_path:
        sys.exit(0)
    project.update_github()
    if not project.github:
        sys.exit(0)
    with project.context(project.BLOCK_VERSIONS):
        # see if locally everything matches
        if not project.versions_match() or not project.git_tag_version:
            project.error(f"Versions do not match\n{project.versions_info()}")
            sys_exit(project)
    with project.context(project.BLOCK_GITHUB, github.GithubException):
        repo = project.github.get_repo(project.mod_config.github_url)
        # check if the tag for the release exists on the remote
        published_tag: Optional[Tag] = None
        git_tag_name = f"{project.git_tag_version}"
        for tag in repo.get_tags():
            if tag.name == git_tag_name:
                if tag.commit.sha != project.git_tag_version.commit_sha:
                    project.error(
                        "Remote tag is on the wrong commit:\n"
                        f"Tag:    {git_tag_name}"
                        f"Local:  {project.git_tag_version.commit_sha}\n"
                        f"Remote: {tag.commit.sha}")
                    sys_exit(project)
                published_tag = tag
                break
        if not published_tag:
            project.error(
                f"Git tag is not published: {project.git_tag_version!r}")
            sys_exit(project)
        # check if we already have the release for this tag
        try:
            release = repo.get_release(published_tag.name)
            if release and not update:
                project.error(
                    f"Release already exists: {release.title} at {release.html_url}"
                )
                sys_exit(project)
        except github.UnknownObjectException:
            release = None
        # get the change log entry for the release body
        change_log = project.change_log[project.assembly_version]
        if not change_log:
            project.error(
                f"Unable to get change log entry for: {project.assembly_version}"
            )
            sys_exit(project)
        # create or update the release
        if not release:
            click.echo(f"Creating new release: {published_tag.name}")
            release = repo.create_git_release(published_tag.name,
                                              published_tag.name, change_log)
        else:
            click.echo(f"Updating the release: {release.title}")
            if release.body != change_log:
                click.echo(
                    f"Updating the change log for: {project.assembly_version}")
                release.update_release(release.title, change_log)
        # check if the asset already exists
        existing_assert: Optional[GitReleaseAsset] = None
        if not project.archive_version:
            sys_exit(project)
        for asset in release.get_assets():
            if asset.name == project.archive_version.filename:
                existing_assert = asset
        if existing_assert:
            if not update:
                project.error(
                    f"Asset already exists: {existing_assert.browser_download_url}"
                )
                sys_exit(project)
            click.echo(f"Removing existing asset: {existing_assert.name}")
            existing_assert.delete_asset()
        click.echo(f"Uploading asset: {project.archive_version.filepath}")
        release.upload_asset(f"{project.archive_version.filepath}")
        click.echo(
            f"Successfully published release {release.title}: {release.html_url}"
        )
    sys_exit(project)
Example #11
0
def set_token(project: CSharpProject, token) -> None:
    with project.context(project.BLOCK_GITHUB, GithubError):
        set_github_token(token, ".")
        project.update_github()
    sys_exit(project)