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)
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)
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)
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)
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)
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)
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)
def set_auth(project: CSharpProject, username, password) -> None: with project.context(project.BLOCK_SPACEDOCK, SpacedockError): spacedock.set_auth(username, password, ".") sys_exit(project)
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)
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)
def set_token(project: CSharpProject, token) -> None: with project.context(project.BLOCK_GITHUB, GithubError): set_github_token(token, ".") project.update_github() sys_exit(project)