Pipeline Release: Use Add-On Bundle to Update Add-Ons #269

Merged
3 changed files with 262 additions and 544 deletions

View File

@ -2,7 +2,7 @@ Pipeline release is a script to package all addons into a single zip on the pipe
## Prerequisite ## Prerequisite
In order to use this tool you need: In order to use this tool you need:
- GIT - GIT & GIT LFS installed
- Python 3.11+ - Python 3.11+
- [Requests Module](https://requests.readthedocs.io/en/latest/) - [Requests Module](https://requests.readthedocs.io/en/latest/)

734
scripts/pipeline-release/pipeline_release.py Normal file → Executable file
View File

@ -1,582 +1,252 @@
# ***** BEGIN GPL LICENSE BLOCK ***** #!/usr/bin/env python3
#
# 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 os
import subprocess
from pathlib import Path from pathlib import Path
from typing import List
import shutil import shutil
import argparse import hashlib
import re import subprocess
from typing import Pattern import tempfile
import datetime import sys
import requests
import json
from requests import Response
REPO_ROOT_DIR = Path(__file__).parent.parent.parent BASE_PATH = "https://projects.blender.org/api/v1"
# BORROWED FROM https://github.com/pawamoy/git-changelog/blob/master/src/git_changelog/commit.py REPO_PATH = '/studio/blender-studio-pipeline'
TYPES: dict[str, str] = { RELEASE_PATH = BASE_PATH + f'/repos{REPO_PATH}/releases'
"add": "Added", TAG_PATH = BASE_PATH + f'/repos{REPO_PATH}/tags'
"fix": "Fixed", API_TOKEN = None
"change": "Changed",
"remove": "Removed",
"merge": "Merged",
"doc": "Documented",
"breaking": "Breaking",
}
# GITEA LOGIN SETTINGS RELEASE_TITLE = "Blender Studio Add-Ons Latest"
api_token_file = Path(__file__).parent.joinpath("api_token.env") RELEASE_VERSION = "latest"
if not api_token_file.exists(): RELEASE_DESCRIPTION = "Latest Release of Blender Studio Pipeline Add-Ons"
print("API Token File not Found")
api_token = open(api_token_file, 'r').read() ZIP_NAME = "blender_studio_add-ons_latest"
base_url = 'https://projects.blender.org'
api_path = f"{base_url}/api/v1"
repo_path = '/studio/blender-studio-pipeline'
release_path = f'/repos{repo_path}/releases'
tag_path = f'/repos{repo_path}/tags'
def parse_commit(commit_message: str) -> dict[str, str]: def main():
""" get_api_token()
Parse the type of the commit given its subject. latest_release = get_release()
Arguments: temp_dir = Path(tempfile.mkdtemp(prefix=ZIP_NAME + "_"))
commit_subject: The commit message subject. release_files = create_latest_addons_zip(ZIP_NAME, temp_dir)
remove_existing_release_assets(latest_release["id"])
for file in release_files:
upload_asset_to_release(latest_release["id"], file)
shutil.rmtree(temp_dir)
print("Blender Studio Add-Ons Successfully Released")
def remove_existing_release_assets(release_id: int) -> None:
"""Removes all existing release assets for the given release ID.
Args:
release_id (int): The ID of the release to remove assets from.
Returns: Returns:
Dict containing commit message and type None
""" """
type = ""
# Split at first colon to remove prefix from commit all_assets = send_get_request(RELEASE_PATH + f"/{release_id}/assets").json()
if ": " in commit_message: for asset in all_assets:
message_body = commit_message.split(': ')[1] if asset["name"] == ZIP_NAME + ".zip" or asset["name"] == ZIP_NAME + ".zip.sha256":
else: send_delete_request(RELEASE_PATH + f"/{release_id}/assets/{asset['id']}")
message_body = commit_message print(f"Deleted {asset['name']} created on: {asset['created_at']}")
type_regex: Pattern = re.compile(r"^(?P<type>(%s))" % "|".join(TYPES.keys()), re.I)
breaking_regex: Pattern = re.compile(
r"^break(s|ing changes?)?[ :].+$", def upload_asset_to_release(release_id: int, file: str) -> None:
re.I | re.MULTILINE, """Uploads an asset to the specified release.
Args:
release_id (int): The id of the release to upload to.
file (str): The path to the file to upload.
Returns:
None
"""
file_name = Path(file.name).name
payload = open(file, 'rb')
file_content = [
('attachment', (file_name, payload, 'application/zip')),
]
print(f"Uploading '{file_name}'......", end="")
response = requests.post(
url=f"{RELEASE_PATH}/{release_id}/assets?name={file_name}&token={API_TOKEN}",
files=file_content,
) )
type_match = type_regex.match(message_body) response.raise_for_status()
if type_match:
type = TYPES.get(type_match.groupdict()["type"].lower(), "") if not response.status_code == 201:
if bool(breaking_regex.search(message_body)): print(f"Failed to upload.")
type = "Breaking" else:
return { print(f"Completed")
"message": message_body,
"type": type,
def get_release() -> dict:
"""Gets the latest release matching the configured title and version.
Removes any existing release with the same title and version first before
returning the latest release to ensure it represents the current commit.
Returns:
dict: The release object for the latest matching release.
"""
# Remove Previous Release so Release is always based on Current Commit
for release in send_get_request(RELEASE_PATH).json():
if release["name"] == RELEASE_TITLE and release["tag_name"] == RELEASE_VERSION:
send_delete_request(RELEASE_PATH + f"/{release['id']}")
send_delete_request(TAG_PATH + f"/{release['tag_name']}")
return create_new_release()
def create_new_release() -> dict:
"""Create a new release on Gitea with the given title, version and description.
Makes a POST request to the Gitea API to create a new release with the specified
parameters. Checks if a tag with the same version already exists first. If not,
creates the tag before creating the release.
Returns:
dict: The release object for the latest matching release.
"""
# Create New Tag
existing_tag = send_get_request(TAG_PATH + f'/{RELEASE_VERSION}')
if existing_tag.status_code == 404:
tag_content = {
"message": RELEASE_DESCRIPTION,
"tag_name": RELEASE_VERSION,
"target": f"main",
}
send_post_request(TAG_PATH, tag_content)
# Create New Release
release_content = {
"body": RELEASE_DESCRIPTION,
"draft": False,
"name": RELEASE_TITLE,
"prerelease": False,
"tag_name": RELEASE_VERSION,
"target_commitish": "string", # will default to latest
} }
return send_post_request(RELEASE_PATH, release_content).json()
parser = argparse.ArgumentParser()
parser.add_argument(
"-c",
"--commit",
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(
"-o",
"--output",
help="Provide a string for the output path of generated zips",
type=str,
)
parser.add_argument(
"-m",
"--major",
help="Bump the major version number, otherwise bump minor version number",
action="store_true",
)
parser.add_argument(
"-t",
"--test",
help="Test release system by only running locally and skip committing/uploading to release",
action="store_true",
)
parser.add_argument(
"-r",
"--reuse_lastest_release",
help="Add new packages to the lastest avaliable release",
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: def get_api_token() -> None:
"""Run command in CLI and capture it's output """Get API token from environment file.
Arguments:
command: String of command to run in CLI. Reads the API token from the api_token.env file and assigns it to the global
API_TOKEN variable. Exits with error if file not found. Exists if API token is invalid.
""" """
output = subprocess.run(command.split(' '), capture_output=True, encoding="utf-8") global API_TOKEN
return output api_token_file = Path(__file__).parent.joinpath("api_token.env")
if not api_token_file.exists():
print("API Token File not Found")
sys.exit(1)
API_TOKEN = open(api_token_file, 'r').read()
# Don't use send_get_request() so we can print custom error message to user
response = requests.get(url=f"{BASE_PATH}/settings/api?token={API_TOKEN}")
if response.status_code != 200:
print("API Token is invalid")
print(f"Error: {response.status_code}: '{response.reason}'")
sys.exit(1)
def exit_program(message: str): def create_latest_addons_zip(name: str, temp_dir: Path):
print(message) """Generate a pipeline release.
sys.exit(0)
Args:
name (str): The name of the release.
Returns:
list: A list containing the path to the zipped release and checksum file.
"""
output_dir = Path(temp_dir).joinpath(name)
output_dir.mkdir()
addons_dir = Path(__file__).parents[2].joinpath("scripts-blender/addons")
zipped_release = shutil.make_archive(
temp_dir.joinpath(name),
'zip',
addons_dir,
)
checksum = generate_checksum(zipped_release)
chechsum_name = name + ".zip.sha256"
checksum_path = temp_dir / chechsum_name
write_file(
checksum_path,
f"{checksum} {name}.zip",
)
return [Path(zipped_release), Path(checksum_path)]
def write_file(file_path: Path, content): def write_file(file_path: Path, content: str) -> None:
"""Write content to file at given file path.
Args:
file_path (Path): Path to file to write to.
content (str): Content to write to file.
Returns:
None
"""
file = open(file_path, 'w') file = open(file_path, 'w')
file.writelines(content) file.writelines(content)
file.close() file.close()
def replace_line(file_path: Path, new_line: str, line_number: int): def generate_checksum(archive_path: Path) -> str:
file = open( """Generate checksum for archive file.
file_path,
)
lines = file.readlines()
lines[line_number] = new_line
out = open(file_path, 'w')
out.writelines(lines)
out.close()
Args:
archive_path (Path): Path to archive file to generate checksum for.
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: Returns:
sha256 checksum for the provided archive as string str: Hex digest string of checksum.
""" """
sha256 = hashlib.sha256()
with open(archive_path, 'rb') as file: with open(archive_path, 'rb') as file:
# Read the file in chunks to handle large files efficiently digest = hashlib.file_digest(file, "sha256")
chunk = file.read(4096) return digest.hexdigest()
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): def send_delete_request(url) -> Response:
""" response = requests.delete(url=f"{url}?token={API_TOKEN}")
Generate changelog messages for a specific category. if response.status_code != 204:
Types are defined in global variable 'TYPES' print(f"Error: {response.status_code}: '{response.reason}'")
Arguments: sys.exit(1)
changelog_messages: dict contaning commit message & type return response
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: def send_get_request(url: str) -> Response:
""" response = requests.get(url=f"{url}?token={API_TOKEN}")
Generate Changelog Entries from a list of commits hashes if not (response.status_code == 200 or response.status_code == 404):
Arguments: print(f"Error: {response.status_code}: '{response.reason}'")
commit_hashes: A list of commit hashes to include in Changelog sys.exit(1)
version: Latest addon version number return response
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]: def send_post_request(url: str, data: dict) -> Response:
"""
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 update_release_table(addon_dir: Path, version: str, release_version: str):
directory = Path(__file__).parent
template_file = directory.joinpath("overview.md.template")
table_file = directory.joinpath("overview.md")
with open(template_file, 'r') as readme_template:
for num, line in enumerate(readme_template):
if addon_dir.name in line:
line_to_replace = num
break # Use first line found
line = line.replace("<VERSION>", f"{version}")
line = line.replace(
"<ZIP_URL>",
f"{base_url}{repo_path}/releases/download/{release_version}/{addon_dir.name}-{version}.zip",
)
new_line = line.replace(
"<CHECKSUM_URL>",
f"{base_url}{repo_path}/releases/download/{release_version}/{addon_dir.name}-{version}.sha256",
)
replace_line(table_file, new_line, line_to_replace)
return table_file
def addon_package(
directory: Path,
commit_prefix: str,
is_major=False,
force=False,
test=False,
output_path=None,
to_upload=[],
release_version="",
):
"""
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)
table_file = update_release_table(directory, version, release_version)
change_log_file = changelog_file_write(
directory.joinpath("CHANGELOG.md"), change_log
)
if not test:
cli_command(f'git reset')
cli_command(f'git stage {change_log_file}')
cli_command(f'git stage {init_file}')
cli_command(f'git stage {table_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
if output_path is None:
addon_output_dir = get_directory(dist_dir, directory.name)
else:
addon_output_dir = get_directory(Path(output_path), directory.name)
zipped_addon = shutil.make_archive(
addon_output_dir.joinpath(f"{name}-{version}"),
'zip',
directory.parent,
directory.name,
)
checksum = generate_checksum(zipped_addon)
checksum_path = addon_output_dir.joinpath(f"{name}-{version}.sha256")
checksum_file = write_file(
checksum_path,
f"{checksum} {name}-{version}.zip",
)
to_upload.append(zipped_addon)
to_upload.append(checksum_path._str)
else:
print(f"No New Version: {directory.name}")
def addon_version_set(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_set(lines[version_line], is_major)
repl_str = f' "version": ({version}),\n'
replace_line(init_file, repl_str, version_line)
return init_file, version.replace(', ', '.').replace(',', '.')
### GITEA UPLOAD RELEASE
import requests # TODO ADD PRINT STATEMENT IF UNABLE TO IMPORT
import json
"""
API token must be created under user>settings>application
- Use browser to 'INSPECT' the Generate Token button
- Find the property 'GTHidden Display' and remove the element of 'None' to nothing
- Then Set the correct scope for the key using the new dropdown menu before creating tag
"""
def upload_file_to_release(url, api_token, release_id, file):
file_name = Path(file.name).name
file_content = [
('attachment', (file_name, file, 'application/zip')),
]
response = requests.post(
url=f"{url}/{release_id}/assets?name={file_name}&token={api_token}",
files=file_content,
)
if not response.status_code == 201:
print(f"{file_name} failed to upload")
else:
print(f"Uploaded {file_name}")
def send_post_request(url, api_token, data):
header_cont = { header_cont = {
'Content-type': 'application/json', 'Content-type': 'application/json',
} }
response = requests.post( response = requests.post(
url=f"{url}?token={api_token}", url=f"{url}?token={API_TOKEN}",
headers=header_cont, headers=header_cont,
data=json.dumps(data), data=json.dumps(data),
) )
response_json = response.json() response_json = response.json()
if response.status_code != 201: if response.status_code != 201:
print(response_json["message"]) print(response_json["message"])
return response_json sys.exit(1)
return response
def create_new_release(tag_url, base_release_url, release_version, api_token):
release_description = "Latest Release of Blender Studio Pipeline"
# Create New Tag
tag_content = {
"message": f"{release_description}",
"tag_name": f"{release_version}",
"target": f"main",
}
send_post_request(tag_url, api_token, tag_content)
# Create New Release
release_content = {
"body": f"{release_description}",
"draft": False,
"name": f"Pipeline Release {release_version}",
"prerelease": False,
"tag_name": f"{release_version}",
"target_commitish": "string", # will default to latest
}
return send_post_request(base_release_url, api_token, release_content)
def main() -> int:
args = parser.parse_args()
commit = args.commit
major = args.major
test = args.test
user_names = args.name
output_path = args.output
force = args.force
reuse_latest_relase = args.reuse_lastest_release
addon_folder = REPO_ROOT_DIR.joinpath(REPO_ROOT_DIR, "scripts-blender/addons")
addon_to_upload = []
base_release_url = f"{api_path}{release_path}"
base_tag_url = f"{api_path}{tag_path}"
latest_release = requests.get(url=f"{base_release_url}/latest?token={api_token}")
# Exception for intial release
if latest_release.status_code == 404:
release_version = '0.0.1'
else:
latest_tag = latest_release.json()["tag_name"]
release_version = latest_tag.replace(
latest_tag[-1], str(int(latest_tag[-1]) + 1)
)
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,
commit,
major,
force,
test,
output_path,
addon_to_upload,
release_version,
)
if not test:
# Release Script
if reuse_latest_relase:
release_id = latest_release.json()["id"]
else:
response = create_new_release(
base_tag_url, base_release_url, release_version, api_token
)
release_id = response["id"]
for file in addon_to_upload:
payload = open(file, 'rb')
upload_file_to_release(
base_release_url,
api_token,
release_id,
payload,
)
return 0
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -1,10 +1,61 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import glob
import hashlib import hashlib
import os from pathlib import Path
import pathlib from urllib.request import urlretrieve
import sys
import requests import requests
import glob
import os
def update_blender_studio_addons(download_folder_path: Path):
if not download_folder_path.exists():
print(
f"Ensure script is run out of Project Tools directory {str(download_folder_path)} does not exist"
)
sys.exit(1)
sha_file = download_folder_path.joinpath("blender_studio_add-ons_latest.zip.sha256")
zip_file = download_folder_path.joinpath("blender_studio_add-ons_latest.zip")
url_sha = "https://projects.blender.org/studio/blender-studio-pipeline/releases/download/latest/blender_studio_add-ons_latest.zip.sha256"
url_zip = "https://projects.blender.org/studio/blender-studio-pipeline/releases/download/latest/blender_studio_add-ons_latest.zip"
# Check current sha and early return if match
web_sha = requests.get(url_sha).text.strip().lower()
if sha_file.exists() & zip_file.exists():
if shasum_matches(zip_file, web_sha):
print(f"{zip_file.name} already up to date, canceling update")
return
else:
# Remove current files
if sha_file.exists():
sha_file.unlink()
if zip_file.exists():
zip_file.unlink()
print(f"Downloading {zip_file.name}......", end="")
urlretrieve(url_zip, str(zip_file))
print("Complete")
print(f"Downloading {sha_file.name}......", end="")
urlretrieve(url_sha, str(sha_file))
print("Complete")
if not shasum_matches(zip_file, web_sha):
print(f"Downloaded file {zip_file.name} does not match its shasum, exiting!")
exit(1)
print("Blender Studio Add-Ons Successfully Updated for Current Project")
print(
"Blender Studio Add-Ons will be copied to your local directory next time you launch Blender via Projet Tools"
)
def shasum_matches(file, sha_sum):
with open(file, "rb") as f:
digest = hashlib.file_digest(f, "sha256")
return sha_sum.startswith(digest.hexdigest())
def download_file(url, out_folder, filename): def download_file(url, out_folder, filename):
@ -33,16 +84,13 @@ def download_file(url, out_folder, filename):
return local_filename return local_filename
current_file_folder_path = pathlib.Path(__file__).parent current_file_folder_path = Path(__file__).parent
download_folder_path = (current_file_folder_path / "../../shared/artifacts/addons/").resolve() download_folder_path = (current_file_folder_path / "../../shared/artifacts/addons/").resolve()
update_blender_studio_addons(download_folder_path)
# Ensure that the download directory exists # Customize this script to download add-ons from other sources
os.makedirs(download_folder_path, exist_ok=True) # download_file(
print("This script currently does nothing. If you want to update the 'studio-pipeline' addons, run the 'package_local.py' script in the studio-pipline repo.")
#download_file(
# "https://projects.blender.org/studio/blender-studio-pipeline/archive/main.zip", # "https://projects.blender.org/studio/blender-studio-pipeline/archive/main.zip",
# download_folder_path, # download_folder_path,
# "blender-studio-pipeline-main.zip", # "blender-studio-pipeline-main.zip",
#) # )