Add logo to installer where applicable (Windows and macOS graphical)

This commit is contained in:
Ryan Volz 2024-05-01 17:19:33 -04:00
parent 03bba5ae50
commit b69eeae4b4

View File

@ -1,14 +1,35 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import contextlib import contextlib
import math
import pathlib import pathlib
import shutil import shutil
from typing import Any, Dict, Optional from typing import Any, Dict, Optional
import conda_lock import conda_lock
from conda_package_streaming.package_streaming import stream_conda_component
from conda_package_streaming.url import conda_reader_for_url
import diff_match_patch import diff_match_patch
import yaml import yaml
from conda_package_streaming.package_streaming import stream_conda_component
from conda_package_streaming.url import conda_reader_for_url
from PIL import Image
def resize_contain(image, size, resample=Image.LANCZOS, bg_color=(255, 255, 255, 0)):
"""
Resize image according to size.
image: a Pillow image instance
size: a list of two integers [width, height]
"""
img_format = image.format
img = image.copy()
img.thumbnail((size[0], size[1]), resample)
background = Image.new("RGBA", (size[0], size[1]), bg_color)
img_position = (
int(math.ceil((size[0] - img.size[0]) / 2)),
int(math.ceil((size[1] - img.size[1]) / 2)),
)
background.paste(img, img_position)
background.format = img_format
return background.convert("RGBA")
def name_from_pkg_spec(spec: str): def name_from_pkg_spec(spec: str):
@ -98,6 +119,7 @@ def render_constructors(
license_file: pathlib.Path, license_file: pathlib.Path,
output_dir: pathlib.Path, output_dir: pathlib.Path,
builder_lockfile_path: pathlib.Path, builder_lockfile_path: pathlib.Path,
logo_path: Optional[pathlib.Path] = None,
) -> None: ) -> None:
lock_content = conda_lock.conda_lock.parse_conda_lock_file(lockfile_path) lock_content = conda_lock.conda_lock.parse_conda_lock_file(lockfile_path)
lock_work_dir = lockfile_path.parent lock_work_dir = lockfile_path.parent
@ -119,10 +141,18 @@ def render_constructors(
extras=("installer",), extras=("installer",),
) )
if logo_path is not None:
logo = Image.open(logo_path)
for platform_env_yaml_path in lock_work_dir.glob("*.constructor.yml"): for platform_env_yaml_path in lock_work_dir.glob("*.constructor.yml"):
constructor_name = platform_env_yaml_path.name.partition(".")[0] constructor_name = platform_env_yaml_path.name.partition(".")[0]
platform = constructor_name.split(sep="-", maxsplit=1)[1] platform = constructor_name.split(sep="-", maxsplit=1)[1]
constructor_dir = output_dir / constructor_name
if constructor_dir.exists():
shutil.rmtree(constructor_dir)
constructor_dir.mkdir(parents=True)
with platform_env_yaml_path.open("r") as f: with platform_env_yaml_path.open("r") as f:
platform_env_dict = yaml.safe_load(f) platform_env_dict = yaml.safe_load(f)
@ -152,6 +182,29 @@ def render_constructors(
channel_priority="strict", channel_priority="strict",
), ),
) )
if logo_path is not None:
if platform.startswith("win"):
# convert to RGB (no transparency) and set white background
# because constructor eventually converts to bmp without transparency
welcome_image = resize_contain(
logo.convert("RGB"), (164, 314), bg_color=(255, 255, 255, 255)
)
header_image = resize_contain(
logo.convert("RGB"), (150, 57), bg_color=(255, 255, 255, 255)
)
icon_image = resize_contain(logo, (256, 256))
welcome_image.save(constructor_dir / "welcome.png")
header_image.save(constructor_dir / "header.png")
icon_image.save(constructor_dir / "icon.png")
construct_dict["welcome_image"] = "welcome.png"
construct_dict["header_image"] = "header.png"
construct_dict["icon_image"] = "icon.png"
elif platform.startswith("osx"):
welcome_image = resize_contain(logo, (1227, 600))
welcome_image.save(constructor_dir / "welcome.png")
construct_dict["welcome_image"] = "welcome.png"
if platform.startswith("win"): if platform.startswith("win"):
construct_dict["post_install"] = "post_install.bat" construct_dict["post_install"] = "post_install.bat"
# point to template that we generate at build time with a patch over default # point to template that we generate at build time with a patch over default
@ -159,11 +212,6 @@ def render_constructors(
else: else:
construct_dict["post_install"] = "post_install.sh" construct_dict["post_install"] = "post_install.sh"
constructor_dir = output_dir / constructor_name
if constructor_dir.exists():
shutil.rmtree(constructor_dir)
constructor_dir.mkdir(parents=True)
# copy license to the constructor directory # copy license to the constructor directory
shutil.copy(license_file, constructor_dir / "LICENSE") shutil.copy(license_file, constructor_dir / "LICENSE")
@ -263,6 +311,7 @@ def render(
license_file: pathlib.Path, license_file: pathlib.Path,
output_dir: pathlib.Path, output_dir: pathlib.Path,
conda_exe: pathlib.Path, conda_exe: pathlib.Path,
logo_path: Optional[pathlib.Path] = None,
dirty: Optional[bool] = False, dirty: Optional[bool] = False,
keep_workdir: Optional[bool] = False, keep_workdir: Optional[bool] = False,
) -> None: ) -> None:
@ -337,6 +386,7 @@ def render(
license_file=license_file, license_file=license_file,
output_dir=output_dir, output_dir=output_dir,
builder_lockfile_path=builder_lockfile_path, builder_lockfile_path=builder_lockfile_path,
logo_path=logo_path,
) )
# clean up conda-lock work dir # clean up conda-lock work dir
@ -431,6 +481,16 @@ if __name__ == "__main__":
" (default: %(default)s)" " (default: %(default)s)"
), ),
) )
parser.add_argument(
"--logo",
dest="logo_path",
type=pathlib.Path,
default=here / "static" / f"{distname}_logo.png",
help=(
"File containing the logo image to include in the installer."
" (default: %(default)s)"
),
)
parser.add_argument( parser.add_argument(
"-o", "-o",
"--output_dir", "--output_dir",
@ -477,6 +537,7 @@ if __name__ == "__main__":
license_file=args.license_file, license_file=args.license_file,
output_dir=args.output_dir, output_dir=args.output_dir,
conda_exe=args.conda_exe, conda_exe=args.conda_exe,
logo_path=args.logo_path,
dirty=args.dirty, dirty=args.dirty,
keep_workdir=args.keep_workdir, keep_workdir=args.keep_workdir,
) )