builder.braak.pro/config/worker/blender/sign.py
2024-11-19 21:41:39 +01:00

195 lines
6.7 KiB
Python

# SPDX-License-Identifier: GPL-2.0-or-later
# SPDX-FileCopyrightText: 2011-2024 Blender Authors
# <pep8 compliant>
import pathlib
import sys
from typing import Optional, Sequence
import worker.blender
import worker.utils
def sign_windows_files(
service_env_id: str,
file_paths: Sequence[pathlib.Path],
description: Optional[str] = None,
certificate_id: str = "",
) -> None:
import conf.worker
worker_config = conf.worker.get_config(service_env_id)
# TODO: Rotate them if first 1 fails
timeserver = worker_config.sign_code_windows_time_servers[0]
server_url = worker_config.sign_code_windows_server_url
if not certificate_id:
certificate_id = worker_config.sign_code_windows_certificate
dry_run = False
if service_env_id == "LOCAL" and not certificate_id:
worker.utils.warning("Performing dry run on LOCAL service environment")
dry_run = True
cmd_args = [
sys.executable,
"C:\\tools\\codesign.py",
"--server-url",
worker.utils.HiddenArgument(server_url),
]
if description:
cmd_args += ["--description", description]
cmd: worker.utils.CmdSequence = cmd_args
# Signing one file at a time causes a stampede on servers, resulting in blocking.
# Instead sign in chunks of multiple files.
chunk_size = 25 # Sign how many files at a time
retry_count = 3
for i in range(0, len(file_paths), chunk_size):
file_chunks = file_paths[i : i + chunk_size]
worker.utils.call(list(cmd) + list(file_chunks), retry_count=retry_count, dry_run=dry_run)
def sign_windows(service_env_id: str, install_path: pathlib.Path) -> None:
# TODO: Why use a junction? Is there some failure with long file paths?
# worker.utils.info("Creating building link")
# temp_build_root_path = pathlib.Path("C:/BlenderTemp")
# os.makedirs(temp_build_root_path, exist_ok=True)
# orig_install_path = install_path
# install_path = temp_build_root_path / install_path.name
try:
# TODO
# New-Item -type Junction -path install_path -value orig_install_path
worker.utils.info("Collecting files to process")
file_paths = list(install_path.glob("*.exe"))
file_paths += list(install_path.glob("*.dll"))
file_paths += list(install_path.glob("*.pyd"))
file_paths = [f for f in file_paths if str(f).find("blender.crt") == -1]
for f in file_paths:
print(f)
sign_windows_files(service_env_id, file_paths)
finally:
# worker.utils.info(f"Removing temporary folder {temp_build_root_path}")
# worker.utils.remove_dir(temp_build_root_path, retry_count=5, retry_wait_time=5.0)
# TODO: is this really necessary?
# worker.utils.info("Flushing volume cache...")
# Write-VolumeCache -DriveLetter C
# core_shell_retry_command -retry_count 5 -delay_in_milliseconds 1000 -script_block `
# worker.utils.info("Junction information...")
# junction = Get-Item -Path install_path
# worker.utils.info(junction | Format-Table)
# worker.utils.info("Attempting to remove...")
# junction.Delete()
# worker.utils.info("Junction deleted!")
pass
worker.utils.info("End of codesign steps")
def sign_darwin_files(
builder: worker.blender.CodeBuilder,
file_paths: Sequence[pathlib.Path],
entitlements_file_name: str
) -> None:
entitlements_path = builder.code_path / "release" / "darwin" / entitlements_file_name
if not entitlements_path.exists():
raise Exception(f"File {entitlements_path} not found, aborting")
worker_config = builder.get_worker_config()
certificate_id = worker_config.sign_code_darwin_certificate
dry_run = False
if builder.service_env_id == "LOCAL" and not certificate_id:
worker.utils.warning("Performing dry run on LOCAL service environment")
dry_run = True
keychain_password = worker_config.darwin_keychain_password(builder.service_env_id)
cmd: worker.utils.CmdSequence = [
"security",
"unlock-keychain",
"-p",
worker.utils.HiddenArgument(keychain_password),
]
worker.utils.call(cmd, dry_run=dry_run)
for file_path in file_paths:
if file_path.is_dir() and file_path.suffix != ".app":
continue
# Remove signature
if file_path.suffix != ".dmg":
worker.utils.call(
["codesign", "--remove-signature", file_path], exit_on_error=False, dry_run=dry_run
)
# Add signature
worker.utils.call(
[
"codesign",
"--force",
"--timestamp",
"--options",
"runtime",
f"--entitlements={entitlements_path}",
"--sign",
certificate_id,
file_path,
],
retry_count=3,
dry_run=dry_run,
)
if file_path.suffix == ".app":
worker.utils.info(f"Vaildating app bundle {file_path}")
worker.utils.call(
["codesign", "-vvv", "--deep", "--strict", file_path], dry_run=dry_run
)
def sign_darwin(builder: worker.blender.CodeBuilder) -> None:
bundle_path = builder.install_dir / "Blender.app"
# Executables
sign_path = bundle_path / "Contents" / "MacOS"
worker.utils.info(f"Collecting files to process in {sign_path}")
sign_darwin_files(builder, list(sign_path.rglob("*")), "entitlements.plist")
# Thumbnailer app extension.
thumbnailer_appex_path = bundle_path / "Contents" / "PlugIns" / "blender-thumbnailer.appex"
if thumbnailer_appex_path.exists():
sign_path = thumbnailer_appex_path / "Contents" / "MacOS"
worker.utils.info(f"Collecting files to process in {sign_path}")
sign_darwin_files(builder, list(sign_path.rglob("*")), "thumbnailer_entitlements.plist")
# Shared librarys and Python
sign_path = bundle_path / "Contents" / "Resources"
worker.utils.info(f"Collecting files to process in {sign_path}")
file_paths = list(
set(sign_path.rglob("*.dylib"))
| set(sign_path.rglob("*.so"))
| set(sign_path.rglob("python3.*"))
)
sign_darwin_files(builder, file_paths, "entitlements.plist")
# Bundle
worker.utils.info(f"Signing app bundle {bundle_path}")
sign_darwin_files(builder, [bundle_path], "entitlements.plist")
def sign(builder: worker.blender.CodeBuilder) -> None:
builder.setup_build_environment()
if builder.platform == "windows":
sign_windows(builder.service_env_id, builder.install_dir)
elif builder.platform == "darwin":
sign_darwin(builder)
else:
worker.utils.info("No code signing to be done on this platform")