diff --git a/docs/addons/anim_cupboard.md b/docs/addons/anim_cupboard.md index 1a916592..740c17de 100644 --- a/docs/addons/anim_cupboard.md +++ b/docs/addons/anim_cupboard.md @@ -1,2 +1,4 @@ +# CHANGELOG + diff --git a/docs/addons/asset_pipeline.md b/docs/addons/asset_pipeline.md index 9a403817..f2b4a984 100644 --- a/docs/addons/asset_pipeline.md +++ b/docs/addons/asset_pipeline.md @@ -1,2 +1,4 @@ +# CHANGELOG + diff --git a/docs/addons/blender_kitsu.md b/docs/addons/blender_kitsu.md index 209000d5..b9d969f9 100644 --- a/docs/addons/blender_kitsu.md +++ b/docs/addons/blender_kitsu.md @@ -1,2 +1,4 @@ +# CHANGELOG + diff --git a/docs/addons/blender_svn.md b/docs/addons/blender_svn.md index 798472bf..89a44089 100644 --- a/docs/addons/blender_svn.md +++ b/docs/addons/blender_svn.md @@ -1,2 +1,4 @@ +# CHANGELOG + diff --git a/docs/addons/bone_gizmos.md b/docs/addons/bone_gizmos.md index 42db980e..056a0541 100644 --- a/docs/addons/bone_gizmos.md +++ b/docs/addons/bone_gizmos.md @@ -1,2 +1,4 @@ +# CHANGELOG + diff --git a/docs/addons/cache_manager.md b/docs/addons/cache_manager.md index 74abdbc1..d8e2bd44 100644 --- a/docs/addons/cache_manager.md +++ b/docs/addons/cache_manager.md @@ -1,2 +1,4 @@ +# CHANGELOG + diff --git a/docs/addons/contactsheet.md b/docs/addons/contactsheet.md index d170c28d..68e36fce 100644 --- a/docs/addons/contactsheet.md +++ b/docs/addons/contactsheet.md @@ -1,2 +1,4 @@ +# CHANGELOG + diff --git a/docs/addons/easy_weights.md b/docs/addons/easy_weights.md index c9af828d..1857ec95 100644 --- a/docs/addons/easy_weights.md +++ b/docs/addons/easy_weights.md @@ -1,2 +1,4 @@ +# CHANGELOG + diff --git a/docs/addons/geonode_shapekeys.md b/docs/addons/geonode_shapekeys.md index 41217225..dcf7cdb4 100644 --- a/docs/addons/geonode_shapekeys.md +++ b/docs/addons/geonode_shapekeys.md @@ -1,2 +1,4 @@ +# CHANGELOG + diff --git a/docs/addons/grease_converter.md b/docs/addons/grease_converter.md index 63947dc1..bcfd423a 100644 --- a/docs/addons/grease_converter.md +++ b/docs/addons/grease_converter.md @@ -1,2 +1,4 @@ +# CHANGELOG + diff --git a/docs/addons/lattice_magic.md b/docs/addons/lattice_magic.md index 503172a5..15f46b6d 100644 --- a/docs/addons/lattice_magic.md +++ b/docs/addons/lattice_magic.md @@ -1 +1,3 @@ +# CHANGELOG + diff --git a/docs/addons/lighting_overrider.md b/docs/addons/lighting_overrider.md index 8212fca6..29d10472 100644 --- a/docs/addons/lighting_overrider.md +++ b/docs/addons/lighting_overrider.md @@ -1 +1,3 @@ +# CHANGELOG + diff --git a/docs/addons/pose_shape_keys.md b/docs/addons/pose_shape_keys.md index 0f2b92e4..1c9a091c 100644 --- a/docs/addons/pose_shape_keys.md +++ b/docs/addons/pose_shape_keys.md @@ -1 +1,3 @@ +# CHANGELOG + diff --git a/docs/addons/render_review.md b/docs/addons/render_review.md index 64de3a16..157c7433 100644 --- a/docs/addons/render_review.md +++ b/docs/addons/render_review.md @@ -1 +1,3 @@ - \ No newline at end of file + +# CHANGELOG + diff --git a/docs/package.json b/docs/package.json index 21555acd..cc083234 100644 --- a/docs/package.json +++ b/docs/package.json @@ -10,7 +10,7 @@ "docs:dev": "vitepress dev", "docs:build": "vitepress build", "docs:preview": "vitepress preview", - "docs:publish": "vitepress build && source .env && rsync -ravz -e \"ssh\" .vitepress/dist/ $DESTINATION" + "docs:publish": "vitepress build && source .env && rsync -ravz -e \"ssh\" .vitepress/dist/ $DESTINATION && rsync -ravz -e \"ssh\" ../dist/ $DESTINATION/download" }, "dependencies": { "@fontsource/heebo": "^4.5.15", diff --git a/scripts-blender/README.md b/scripts-blender/README.md index 023b3abf..fda1851d 100644 --- a/scripts-blender/README.md +++ b/scripts-blender/README.md @@ -1,102 +1,21 @@ # Blender Add-ons -Most add-ons used in the Blender Studio pipeline. +Add-ons used by the Blender Studio pipeline. Download the latest addons releases from the table below. To review or report issues visit the [Blender-Studio-Pipeline](https://projects.blender.org/studio/blender-studio-pipeline/issues) issues board. -## Anim Cupboard - -Add-on with miscellaneous tools for animators. - -Author & Maintainer: Demeter Dzadik - - -## Asset Pipeline - -Add-on that manages the Asset Pipeline, used by Modeling, Shading and Rigging departments to be able to work on different aspects of the same asset, primarily characters. Each department works in their own files (char.modeling, char.shading, char.rigging), and push and pull changes from a publish file (char.v001.blend), where all the different data is combined into the final character file. - -Most of the actual data transferring code is in a file that is NOT part of the add-on. This file is in the production SVN, under `pro/config/asset_pipeline_config/task_layers.py`. - -Author: Paul Golter -Maintainers: Demeter Dzadik, Simon Thommes - - -## Blender Kitsu - -Add-on used by animation, layout, and editorial department to push video files of shot versions to Kitsu. It also has features that are not directly related to Kitsu but support certain aspects of the Blender Studio Pipeline. - -**Shot Builder** : tools used by animators or TDs to build .blend files by pulling in shot data from a production's Kitsu database, by linking characters, sets, and props that are used by a given shot. Author: Jeroen Bakker - -**Anim Setup** : Sub-module that automates the setup of animation within shot_builder. Author: Paul Golter - -Author: Paul Golter -Maintainers: Nick Alberelli, Francesco Siddi, Demeter Dzadik - -## Blender Svn - -Add-on that is intended as a UI for the SVN (Subversion) file versioning system which we use at the studio. Currently doesn't support check-out, but once a check-out is done, it supports all common SVN operations, including resolving conflicts. The currently opened .blend file must be in an SVN repository for the UI to appear. - -Author & Maintainer: Demeter Dzadik - - -## Bone Gizmos - -Add-on that attempts to prototype a system for using meshes for the manipulation of armatures. Design task: https://projects.blender.org/blender/blender/issues/92218 - -Author & Maintainer: Demeter Dzadik - -## Cache Manager - -Add-on to streamline the alembic cache workflow of assets. - -Author: Paul Golter - -## Contact Sheet - -Add-on to create a contactsheet from sequence editor strips. - -Author: Paul Golter - -## Easy Weights - -Easy Weight is an addon focused on quality of life improvements for weight painting in Blender. - -Author & Maintainer: Demeter Dzadik - -## Geonode Shapekeys - -Add-on used by animators to sculpt on linked and overridden meshes. - -Author & Maintainer: Demeter Dzadik - -## Grease Converter - -Add-on that can convert annotations to grease pencil objects and vise versa. - -Author: Paul Golter - -## Lattice Magic - -This addon adds some Lattice-based utilities to Blender. - -Author & Maintainer: Demeter Dzadik - -## Lighting Overrider - -Add-on to create, manage and apply python overrides in a flexible and reliable way as they are used in the lighting process of the Blender Studio pipeline on a shot and sequence level. - -Author & Maintainer: Simon Thommes - - -## Pose Shape Keys - -Add-on used by the rigging department to manage and maintain shapekeys. - -Author & Maintainer: Demeter Dzadik - - -## Render Review - -Add-on to review renders from flamenco with the sequence editor, and push and approve them on Kitsu. - -Author: Paul Golter -Maintainer: Demeter Dzadik \ No newline at end of file +| Add-on | Description | Version | Checksum | +|---|---|---|---| +|Anim Cupboard |Add-on with miscellaneous tools for animators. | [v0.0.2](download/anim_cupboard/anim_cupboard-0.0.2.zip) |[SHA256](download/anim_cupboard/anim_cupboard-0.0.2.sha256)| +|Asset Pipeline |Add-on that manages the Asset Pipeline, used by Modeling, Shading and Rigging departments. |[v0.1.1](download/asset_pipeline/asset_pipeline-0.1.1.zip) |[SHA256](download/asset_pipeline/asset_pipeline-0.1.1.sha256)| +|Blender Kitsu|Enforce conventions, build shots, manage production files and update data on kitsu server. |[v0.1.1](download/blender_kitsu/blender_kitsu-0.1.1.zip) |[SHA256](download/blender_kitsu/blender_kitsu-0.1.1.sha256)| +|Blender SVN |Add-on that is intended as a UI for the SVN (Subversion) file versioning system which we use at the studio. | [v0.2.1](download/blender_svn/blender_svn-0.2.1.zip) |[SHA256](download/blender_svn/blender_svn-0.2.1.sha256)| +|Blender Gizmos|Add-on that attempts to prototype a system for using meshes for the manipulation of armatures. |[v0.0.2](download/bone_gizmos/blender_gizmos-0.0.2.zip) |[SHA256](download/bone_gizmos/blender_gizmos-0.0.2.sha256)| +|Cache Manager |Streamline the alembic cache workflow of assets. |[v0.2.1](download/cache_manager/cache_manager-0.2.1.zip) |[SHA256](download/cache_manager/cache_manager-0.2.1.sha256)| +|Contact Sheet |Create a contactsheet from sequence editor strips. | [v0.1.1](download/contactsheet/contact_sheet-0.1.1.zip) |[SHA256](download/contactsheet/contact_sheet-0.1.1.sha256)| +|Easy Weights |Easy Weight is an addon focused on quality of life improvements for weight painting in Blender. |[v0.0.2](download/easy_wights/easy_wights-0.0.2.zip) |[SHA256](download/easy_wights/easy_wights-0.0.2.sha256)| +|Geonode Shapekeys |Add-on used by animators to sculpt on linked and overridden meshes. | [v0.0.2](download/geonode_shapekeys/geonode_shapekeys-0.0.2.zip) |[SHA256](download/geonode_shapekeys/geonode_shapekeys-0.0.2.sha256)| +|Grease Converter |Add-on that can convert annotations to grease pencil objects and vise versa. |[v0.1.1](download/grease_converter/grease_converter-0.1.1.zip) |[SHA256](download/grease_converter/grease_converter-0.1.1.sha256)| +|Lattice Magic |This addon adds some Lattice-based utilities to Blender. |[v0.1.1](download/lattice_magic/lattice_magic-0.1.1.zip) |[SHA256](download/lattice_magic/lattice_magic-0.1.1.sha256)| +|Lighting Overrider |Ccreate, manage and apply python overrides in a flexible and reliable way. | [v0.1.1](download/lighting_overrider/lighting_overrider-0.1.1.zip) |[SHA256](downloadlighting_overrider/lighting_overrider-0.1.1.sha256)| +|Pose Shape Keys|Add-on used by the rigging department to manage and maintain shapekeys. |[v0.0.2](download/pose_shape_keys/pose_shape_keys-0.0.2.zip) |[SHA256](download/pose_shape_keys/pose_shape_keys-0.1.1.sha256)| +|Render Review |Review renders from flamenco with the sequence editor. |[v0.1.1](download/render_review/render_review-0.1.1.zip) |[SHA256](download/render_review/render_review-0.1.1.sha256)| \ No newline at end of file diff --git a/scripts-blender/addons/anim_cupboard/__init__.py b/scripts-blender/addons/anim_cupboard/__init__.py index 8a2b3be2..a8ea90a1 100644 --- a/scripts-blender/addons/anim_cupboard/__init__.py +++ b/scripts-blender/addons/anim_cupboard/__init__.py @@ -10,14 +10,14 @@ from . import easy_constraints from . import warn_about_broken_libraries bl_info = { - 'name' : "Animation Cupboard" - ,'author': "Demeter Dzadik" - ,'version' : (0, 0, 1) - ,'blender' : (3, 2, 0) - ,'description' : "Tools to improve animation workflows" - ,'location': "Various" - ,'category': 'Animation' - # ,'doc_url' : "https://gitlab.com/blender/CloudRig/" + 'name' : "Animation Cupboard", + 'author': "Demeter Dzadik", + 'version' : (0, 0, 1), + 'blender' : (3, 2, 0), + 'description' : "Tools to improve animation workflows", + 'location': "Various", + 'category': 'Animation', + # 'doc_url' : "https://gitlab.com/blender/CloudRig/", } modules = ( diff --git a/scripts-blender/addons/bone_gizmos/__init__.py b/scripts-blender/addons/bone_gizmos/__init__.py index aabdd138..db35a056 100644 --- a/scripts-blender/addons/bone_gizmos/__init__.py +++ b/scripts-blender/addons/bone_gizmos/__init__.py @@ -10,14 +10,14 @@ from bpy.props import FloatProperty, IntProperty from bpy.types import AddonPreferences bl_info = { - 'name' : "Bone Gizmos" - ,'author': "Demeter Dzadik" - ,'version' : (0, 0, 1) - ,'blender' : (3, 0, 0) - ,'description' : "Bone Gizmos for better armature interaction" - ,'location': "Properties->Bone->Viewport Display->Custom Gizmo" - ,'category': 'Rigging' - # ,'doc_url' : "https://gitlab.com/blender/CloudRig/" + 'name' : "Bone Gizmos", + 'author': "Demeter Dzadik", + 'version' : (0, 0, 1), + 'blender' : (3, 0, 0), + 'description' : "Bone Gizmos for better armature interaction", + 'location': "Properties->Bone->Viewport Display->Custom Gizmo", + 'category': 'Rigging', + # 'doc_url' : "https://gitlab.com/blender/CloudRig/", } modules = ( diff --git a/scripts-blender/addons/easy_weights/__init__.py b/scripts-blender/addons/easy_weights/__init__.py index 18c29258..8d8c4e75 100644 --- a/scripts-blender/addons/easy_weights/__init__.py +++ b/scripts-blender/addons/easy_weights/__init__.py @@ -27,7 +27,7 @@ import importlib bl_info = { "name": "Easy Weight", "author": "Demeter Dzadik", - "version": (1, 0), + "version": (0, 1, 0), "blender": (2, 90, 0), "location": "Weight Paint > Weights > Easy Weight", "description": "Operators to make weight painting easier.", diff --git a/scripts-blender/addons/lattice_magic/__init__.py b/scripts-blender/addons/lattice_magic/__init__.py index e199de76..da7369b4 100644 --- a/scripts-blender/addons/lattice_magic/__init__.py +++ b/scripts-blender/addons/lattice_magic/__init__.py @@ -16,7 +16,7 @@ bl_info = { "name": "Lattice Magic", "author": "Demeter Dzadik", - "version": (1,0), + "version": (0, 1, 0), "blender": (2, 90, 0), "location": "View3D > Sidebar > Lattice Magic", "description": "Various Lattice-based tools to smear or adjust geometry.", diff --git a/scripts-blender/addons/lighting_overrider/__init__.py b/scripts-blender/addons/lighting_overrider/__init__.py index 1ca6d880..daec86fb 100644 --- a/scripts-blender/addons/lighting_overrider/__init__.py +++ b/scripts-blender/addons/lighting_overrider/__init__.py @@ -6,7 +6,7 @@ from . import templates, json_io, execution, ui, override_picker bl_info = { "name": "Lighting Overrider", "author": "Simon Thommes", - "version": (1,0), + "version": (0, 1, 0), "blender": (3, 0, 0), "location": "3D Viewport > Sidebar > Overrides", "description": "Tool for the Blender Studio to create, manage and store local python overrides of linked data on a shot and sequence level.", diff --git a/scripts/pipeline-release/README.md b/scripts/pipeline-release/README.md new file mode 100644 index 00000000..8e17b05a --- /dev/null +++ b/scripts/pipeline-release/README.md @@ -0,0 +1,63 @@ +Pipeline release is a script to package addons in the pipeline repo. + +# Features + - Automatically Find Commits since last version for each addon in `scripts-blender/addons/` + - Appends changelog to existing `CHANGELOG.md` per addon + - Bump Version on `__init__.py` file + - Commits `__init__.py` and `CHANGELOG.md` to current branch (user must manually push changes) + - Creates Archive with Checksum in `dist` folder + +## Prerequisite +In order to use this tool you need: +- Python 3.5+ +- GIT + +## Run +This folder contains a command line tool that doesn't require installation to use properly. To run `pipeline_release` without installation follow the steps below. +1. Clone this repository with `git clone https://projects.blender.org/studio/blender-studio-pipeline.git` +2. Run `cd blender-studio-pipeline/scripts/pipeline_release` to enter directory +3. Run program with `python -m pipeline_release` + +## How to get started + +| Command | Description | +| ----------- | ----------- | +| -b, --bump|Bump the major version number, otherwise bump minor version| +| -n --name| Name of addon(s) folder to update. All addons will be checked if flag is not provided| +| -m --msg| Title of commit to consider basis of latest release, otherwise the last commit called 'Version Bump:' will be used| +| -f --force|Bump version even if no commits are found| +| -h, --help| show the above help message and exit| + + +## Changelog Conventions +|Changelog Title| Commit Prefix| +| ----------- | ----------- | +|ADD |add| +|BUG FIX |fix| +|CHANGED |change| +|REMOVED |remove| +|MERGED |merge| +|DOCUMENTED|doc| +|BREAKING|breaking| + + +This tool will automatically generate changelog messages based on the "changelog categories" below. Commit's subject line convention is `{Name of Addon}: {category} commit content` for example: +### Commit Subject Line: +``` +Blender Kitsu: Fix naming conventions +```` + +### Changelog Output: +``` +### Fixes +- Fix naming conventions +``` + +## Example Usage +| Action | Command | +| ----------- | ----------- | +|Create a new minor version if available of all addons|`python -m pipeline_release`| +|Create a new major version if available of all addons|`python -m pipeline_release -b`| +|Create a new version even no new commits are found|`python -m pipeline_release` -f| +|Only check if addon has this name(s) |`python -m pipeline_release -n "blender_kitsu, blender_svn"`| +|Find a commit that matches this message and uses as version basis otherwise the last commit called 'Version Bump:' will be used |`python -m pipeline_release -m "Commit MSG"`| \ No newline at end of file diff --git a/scripts/pipeline-release/pipeline_release.py b/scripts/pipeline-release/pipeline_release.py new file mode 100644 index 00000000..b824748b --- /dev/null +++ b/scripts/pipeline-release/pipeline_release.py @@ -0,0 +1,387 @@ +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# ***** END GPL LICENCE BLOCK ***** +# +# (c) 2021, Blender Foundation + +import zipfile +import hashlib +import sys +import os +import subprocess +from pathlib import Path +from typing import List +import shutil +import argparse +import re +from typing import Pattern +import datetime + +REPO_ROOT_DIR = Path(__file__).parent.parent.parent +# BORROWED FROM https://github.com/pawamoy/git-changelog/blob/master/src/git_changelog/commit.py +TYPES: dict[str, str] = { + "add": "Added", + "fix": "Fixed", + "change": "Changed", + "remove": "Removed", + "merge": "Merged", + "doc": "Documented", + "breaking": "Breaking", +} + + +def parse_commit(commit_message: str) -> dict[str, str]: + """ + Parse the type of the commit given its subject. + Arguments: + commit_subject: The commit message subject. + Returns: + Dict containing commit message and type + """ + type = "" + # Split at first colon to remove prefix from commit + if ": " in commit_message: + message_body = commit_message.split(': ')[1] + else: + message_body = commit_message + type_regex: Pattern = re.compile(r"^(?P(%s))" % "|".join(TYPES.keys()), re.I) + breaking_regex: Pattern = re.compile( + r"^break(s|ing changes?)?[ :].+$", + re.I | re.MULTILINE, + ) + + type_match = type_regex.match(message_body) + if type_match: + type = TYPES.get(type_match.groupdict()["type"].lower(), "") + if bool(breaking_regex.search(message_body)): + type = "Breaking" + return { + "message": message_body, + "type": type, + } + + +parser = argparse.ArgumentParser() +parser.add_argument( + "-m", + "--msg", + help="Find commit with this message and use it as the last version.", + type=str, +) +parser.add_argument( + "-n", + "--name", + help="Only update the addon corrisponding to this name(s).", + type=str, +) + +parser.add_argument( + "-b", + "--bump", + help="Bump the major version number, otherwise bump minor version number", + action="store_true", +) + +parser.add_argument( + "-f", + "--force", + help="Bump version even if no commits are found", + action="store_true", +) + + +def cli_command(command: str) -> subprocess: + """Run command in CLI and capture it's output + Arguments: + command: String of command to run in CLI. + """ + output = subprocess.run(command.split(' '), capture_output=True, encoding="utf-8") + return output + + +def exit_program(message: str): + print(message) + sys.exit(0) + + +def write_file(file_path: Path, content): + file = open(file_path, 'w') + file.writelines(content) + file.close() + + +def get_directory(repo_root: Path, folder_name: str) -> Path: + """Returns directory PATH, creates one if none exists""" + path = repo_root.joinpath(folder_name) + if not os.path.exists(path): + os.makedirs(path) + return path + + +def clean_str(string: str) -> str: + """Returns string with qoutes and line breaks removed""" + return string.replace('\n', '').replace("'", "").replace('"', '') + + +def generate_checksum(archive_path: str) -> str: + """ + Generate a checksum for a zip file + Arguments: + archive_path: String of the archive's file path + Returns: + sha256 checksum for the provided archive as string + """ + sha256 = hashlib.sha256() + with open(archive_path, 'rb') as file: + # Read the file in chunks to handle large files efficiently + chunk = file.read(4096) + while len(chunk) > 0: + sha256.update(chunk) + chunk = file.read(4096) + return sha256.hexdigest() + + +def changelog_category_get(changelog_messages: dict[str, str], title: str, key: str): + """ + Generate changelog messages for a specific category. + Types are defined in global variable 'TYPES' + Arguments: + changelog_messages: dict contaning commit message & type + title: Title of the changelog category + key: Key for category/type as defined in global variable TYPES + Returns: + changelog entry for the given category/type as a string + """ + entry = '' + if not any(commit for commit in changelog_messages if commit["type"] == key): + return entry + entry += f"### {title} \n" + for commit in changelog_messages: + if commit["type"] == key: + entry += f'- {commit["message"]}' + entry += "\n" + return entry + + +def changelog_generate(commit_hashes: list[str], version: str) -> str: + """ + Generate Changelog Entries from a list of commits hashes + Arguments: + commit_hashes: A list of commit hashes to include in Changelog + version: Latest addon version number + Returns: + complete changelog for latest version as string + """ + + log_entry = f'## {version} - {datetime.date.today()} \n \n' + changelog_messages = [] + if commit_hashes is not None: + for commit in commit_hashes: + message = ( + f"{cli_command(f'git log --pretty=format:%s -n 1 {commit}').stdout}\n" + ) + changelog_messages.append(parse_commit(message)) + + for type in TYPES: + log_entry += changelog_category_get( + changelog_messages, TYPES.get(type).upper(), TYPES.get(type) + ) + + log_entry += "### UN-CATEGORIZED \n" + for commit in changelog_messages: + if commit["message"] not in log_entry: + log_entry += f"- {commit['message']}" + log_entry += "\n" + return log_entry + + +def changelog_commits_get(directory: Path, commit_message: str) -> list[str]: + """ + Get list of commit hashes, that affect a given directory + Arguments: + directory: Name of directory/folder to filter commits + commit_message: Prefix of commit to use as base for latest release + Returns: + list of commit hashes + """ + last_version_commit = None + commits_in_folder = cli_command( + f'git log --format=format:"%H" {directory}/*' + ).stdout.split('\n') + # Find Last Version + for commit in commits_in_folder: + commit = clean_str(commit) + commit_msg = cli_command(f'git log --format=%B -n 1 {commit}') + if commit_message in commit_msg.stdout: + last_version_commit = commit + if last_version_commit is None: + return + + commits_since_release = cli_command( + f'git rev-list {clean_str(last_version_commit)[0:9]}..HEAD' + ).stdout.split('\n') + commit_hashes = [] + + for commit in commits_in_folder: + if any(clean_str(commit) in x for x in commits_since_release): + commit_hashes.append(clean_str(commit)) + return commit_hashes + + +def changelog_file_write(file_path: Path, content: str): + """ + Append changelog to existing changelog file or create a new + changelog file if none exists + Arguments: + file_path: PATH to changelog + content: changelog for latest version as string + """ + if file_path.exists(): + dummy_file = str(file_path._str) + '.bak' + with open(file_path, 'r') as read_obj, open(dummy_file, 'w') as write_obj: + write_obj.write(content) + for line in read_obj: + write_obj.write(line) + os.remove(file_path) + os.rename(dummy_file, file_path) + else: + write_file(file_path, content) + return file_path + + +def addon_package(directory: Path, commit_prefix: str, is_major=False, force=False): + """ + For a give directory, if new commits are found after the commit matching 'commit_prefix', + bump addon version, generate a changelog, commit changes and package addon into an archive. + Print statements indicate if addon was version bumped, or if new version was found. + Arguments: + directory: Name of directory/folder to filter commits + commit_prefix: Prefix of commit to use as base for latest release + is_major: if major 2nd digit in version is updated, else 3rd digit + """ + commit_msg = 'Version Bump:' if commit_prefix is None else commit_prefix + commits_in_folder = changelog_commits_get(directory, commit_msg) + dist_dir = get_directory(REPO_ROOT_DIR, "dist") + if commits_in_folder or force: + init_file, version = addon_version_bump(directory, is_major) + change_log = changelog_generate(commits_in_folder, version) + change_log_file = changelog_file_write( + directory.joinpath("CHANGELOG.MD"), change_log + ) + cli_command(f'git reset') + cli_command(f'git stage {change_log_file}') + cli_command(f'git stage {init_file}') + subprocess.run( + ['git', 'commit', '-m', f"Version Bump: {directory.name} {version}"], + capture_output=True, + encoding="utf-8", + ) + print(f"Version Bump: {directory.name} {version}") + name = directory.name + addon_output_dir = get_directory(dist_dir, directory.name) + + zipped_addon = shutil.make_archive( + addon_output_dir.joinpath(f"{name}-{version}"), 'zip', directory + ) + checksum = generate_checksum(zipped_addon) + checksum_file = write_file( + addon_output_dir.joinpath(f"{name}-{version}.sha256"), + f"{checksum} {name}-{version}.zip", + ) + else: + print(f"No New Version: {directory.name}") + + +def addon_version_get(version_line: str, is_major: bool) -> str: + """ + Read bl_info within addon's __init__.py file to get new version number + Arguments: + version_line: Line of bl_info containing version number + is_major: if major 2nd digit in version is updated, else 3rd digit + Returns + Latest addon version number + """ + version = version_line.split('(')[1].split(')')[0] + # Bump either last digit for minor versions and second last digit for major + if is_major: + new_version = version[:-4] + str(int(version[3]) + 1) + version[-3:] + else: + new_version = version[:-1] + str(int(version[-1]) + 1) + return new_version + + +def addon_version_bump(directory: Path, is_major: bool): + """ + Update bl_info within addon's __init__.py file to indicate + version bump. Expects line to read as '"version": (n, n, n),\n' + Arguments: + directory: Name of directory/folder containing addon + is_major: if major 2nd digit in version is updated, else 3rd digit + + Returns: + init_file: PATH to init file that has been updated with new version + version: Latest addon version number + """ + + version_line = None + str_find = "version" + init_file = directory.joinpath("__init__.py") + with open(init_file, 'r') as myFile: + for num, line in enumerate(myFile): + if str_find in line and "(" in line and line[0] != "#": + version_line = num + break # Use first line found + + file = open( + init_file, + ) + lines = file.readlines() + version = addon_version_get(lines[version_line], is_major) + repl_str = f' "version": ({version}),\n' + lines[version_line] = repl_str + out = open(init_file, 'w') + out.writelines(lines) + out.close() + return init_file, version.replace(', ', '.').replace(',', '.') + + +def main() -> int: + args = parser.parse_args() + msg = args.msg + bump = args.bump + user_names = args.name + force = args.force + addon_folder = REPO_ROOT_DIR.joinpath(REPO_ROOT_DIR, "scripts-blender/addons") + addon_dirs = [ + name + for name in os.listdir(addon_folder) + if os.path.isdir(addon_folder.joinpath(name)) + ] + if user_names: + addon_dirs = [ + name + for name in os.listdir(addon_folder) + if os.path.isdir(addon_folder.joinpath(name)) and name in user_names + ] + for dir in addon_dirs: + addon_to_package = addon_folder.joinpath(addon_folder, dir) + addon_package(addon_to_package, msg, bump, force) + return 0 + + +if __name__ == "__main__": + main()