260 lines
9.3 KiB
Python
260 lines
9.3 KiB
Python
# SPDX-License-Identifier: GPL-2.0-or-later
|
|
# SPDX-FileCopyrightText: 2011-2024 Blender Authors
|
|
# <pep8 compliant>
|
|
|
|
import json
|
|
import os
|
|
import pathlib
|
|
import time
|
|
|
|
import worker.blender.version
|
|
import worker.deploy
|
|
import worker.utils
|
|
|
|
|
|
def extract_file(
|
|
builder: worker.deploy.CodeStoreBuilder, source_file_path: pathlib.Path, platform: str
|
|
) -> None:
|
|
worker.utils.info(f"Extracting artifact [{source_file_path}] for Steam")
|
|
if not source_file_path.exists():
|
|
raise Exception("File not found, aborting")
|
|
|
|
dest_extract_path = builder.store_steam_dir / platform
|
|
dest_content_path = dest_extract_path / "content"
|
|
worker.utils.remove_dir(dest_extract_path)
|
|
worker.utils.remove_dir(dest_content_path)
|
|
os.makedirs(dest_extract_path, exist_ok=True)
|
|
|
|
if platform == "linux":
|
|
worker.utils.info(f"Extract [{source_file_path}] to [{dest_extract_path}]")
|
|
cmd = ["tar", "-xf", source_file_path, "--directory", dest_extract_path]
|
|
worker.utils.call(cmd)
|
|
|
|
# Move any folder there as ./content
|
|
for source_content_path in dest_extract_path.iterdir():
|
|
if source_content_path.is_dir():
|
|
worker.utils.info(f"Move [{source_content_path.name}] -> [{dest_content_path}]")
|
|
worker.utils.move(source_content_path, dest_content_path)
|
|
break
|
|
|
|
elif platform == "darwin":
|
|
source_content_path = dest_extract_path / "Blender"
|
|
if source_content_path.exists():
|
|
worker.utils.info(f"Removing [{source_content_path}]")
|
|
worker.utils.remove_dir(source_content_path)
|
|
|
|
image_file_path = source_file_path.with_suffix(".img")
|
|
|
|
cmd = ["dmg2img", "-v", "-i", source_file_path, "-o", image_file_path]
|
|
worker.utils.call(cmd)
|
|
|
|
cmd = ["7z", "x", f"-o{dest_extract_path}", image_file_path]
|
|
worker.utils.call(cmd)
|
|
|
|
os.makedirs(dest_content_path, exist_ok=True)
|
|
|
|
worker.utils.remove_file(image_file_path)
|
|
|
|
worker.utils.info(f"Move Blender app from [{source_content_path}] -> [{dest_content_path}]")
|
|
worker.utils.move(source_content_path / "Blender.app", dest_content_path / "Blender.app")
|
|
worker.utils.remove_dir(source_content_path)
|
|
elif platform == "windows":
|
|
worker.utils.info(f"Extracting zip file [{source_file_path}]")
|
|
cmd = ["7z", "x", f"-o{dest_extract_path}", source_file_path]
|
|
worker.utils.call(cmd)
|
|
|
|
# Move any folder there as ./content
|
|
for source_content_path in dest_extract_path.iterdir():
|
|
if source_content_path.is_dir():
|
|
worker.utils.info(f"Move [{source_content_path.name}] -> [{dest_content_path}]")
|
|
worker.utils.move(source_content_path, dest_content_path)
|
|
break
|
|
else:
|
|
raise Exception(f"Don't know how to extract for platform [{platform}]")
|
|
|
|
|
|
def extract(builder: worker.deploy.CodeStoreBuilder) -> None:
|
|
package_manifest = builder.package_dir / "manifest.json"
|
|
builds = json.loads(package_manifest.read_text())
|
|
|
|
for build in builds:
|
|
if build["file_extension"] not in ["zip", "tar.xz", "dmg"]:
|
|
continue
|
|
if build["architecture"] == "arm64":
|
|
continue
|
|
|
|
file_path = builder.package_dir / build["file_name"]
|
|
platform = build["platform"]
|
|
extract_file(builder, file_path, platform)
|
|
|
|
|
|
def build(builder: worker.deploy.CodeStoreBuilder, is_preview: bool) -> None:
|
|
dry_run = False
|
|
if builder.service_env_id == "LOCAL":
|
|
worker.utils.warning("Performing dry run on LOCAL service environment")
|
|
dry_run = True
|
|
|
|
version_info = worker.blender.version.VersionInfo(builder)
|
|
branches_config = builder.get_branches_config()
|
|
is_lts = builder.track_id in branches_config.all_lts_tracks
|
|
is_latest = branches_config.track_major_minor_versions["vdev"] == version_info.short_version
|
|
|
|
track_path = builder.track_path
|
|
log_path = builder.track_path / "log"
|
|
worker.utils.remove_dir(log_path)
|
|
os.makedirs(log_path, exist_ok=True)
|
|
|
|
worker_config = builder.get_worker_config()
|
|
steam_credentials = worker_config.steam_credentials(builder.service_env_id)
|
|
steam_user_id, steam_user_password = steam_credentials
|
|
if not steam_user_id or not steam_user_password:
|
|
if not dry_run:
|
|
raise Exception("Steam user id or password not available, aborting")
|
|
|
|
env = os.environ.copy()
|
|
env["PATH"] = env["PATH"] + os.pathsep + "/usr/games"
|
|
|
|
cmd: worker.utils.CmdSequence = [
|
|
"steamcmd",
|
|
"+login",
|
|
worker.utils.HiddenArgument(steam_user_id),
|
|
worker.utils.HiddenArgument(steam_user_password),
|
|
"+quit",
|
|
]
|
|
worker.utils.call(cmd, dry_run=dry_run, env=env)
|
|
|
|
worker.utils.info("Waiting 5 seconds for next steam command")
|
|
time.sleep(5.0)
|
|
|
|
steam_app_id = worker_config.steam_app_id
|
|
steam_platform_depot_ids = worker_config.steam_platform_depot_ids
|
|
|
|
for platform_id in ["linux", "darwin", "windows"]:
|
|
worker.utils.info(f"Platform {platform_id}")
|
|
|
|
platform_depot_id = steam_platform_depot_ids[platform_id]
|
|
|
|
track_build_root_path = builder.store_steam_dir / platform_id
|
|
if not track_build_root_path.exists():
|
|
raise Exception(f"Folder {track_build_root_path} does not exist")
|
|
|
|
platform_build_file_path = track_build_root_path / "depot_build.vdf"
|
|
|
|
source_root_path = track_build_root_path / "content"
|
|
if not source_root_path.exists():
|
|
raise Exception(f"Folder {source_root_path} does not exist")
|
|
|
|
dest_root_path = track_build_root_path / "output"
|
|
|
|
# Steam branches cannot be uper case and no spaces allowed
|
|
# Branches are named "daily" and "devtest" on Steam, so rename those.
|
|
steam_branch_id = builder.service_env_id.lower()
|
|
steam_branch_id = steam_branch_id.replace("prod", "daily")
|
|
steam_branch_id = steam_branch_id.replace("uatest", "devtest")
|
|
|
|
if is_lts:
|
|
# daily-X.X and devtest-X.X branches for LTS.
|
|
steam_branch_id = f"{steam_branch_id}-{version_info.short_version}"
|
|
elif is_latest:
|
|
# daily and devtest branches for main without suffix.
|
|
pass
|
|
else:
|
|
# Not setting this live.
|
|
steam_branch_id = ""
|
|
|
|
preview = "1" if is_preview else "0"
|
|
|
|
app_build_script = f"""
|
|
"appbuild"
|
|
{{
|
|
"appid" "{steam_app_id}"
|
|
"desc" "Blender {version_info.version}" // description for this build
|
|
"buildoutput" "{dest_root_path}" // build output folder for .log, .csm & .csd files, relative to location of this file
|
|
"contentroot" "{source_root_path}" // root content folder, relative to location of this file
|
|
"setlive" "{steam_branch_id}" // branch to set live after successful build, non if empty
|
|
"preview" "{preview}" // 1 to enable preview builds, 0 to commit build to steampipe
|
|
"local" "" // set to file path of local content server
|
|
|
|
"depots"
|
|
{{
|
|
"{platform_depot_id}" "{platform_build_file_path}"
|
|
}}
|
|
}}
|
|
"""
|
|
|
|
platform_build_script = f"""
|
|
"DepotBuildConfig"
|
|
{{
|
|
// Set your assigned depot ID here
|
|
"DepotID" "{platform_depot_id}"
|
|
|
|
// Set a root for all content.
|
|
// All relative paths specified below (LocalPath in FileMapping entries, and FileExclusion paths)
|
|
// will be resolved relative to this root.
|
|
// If you don't define ContentRoot, then it will be assumed to be
|
|
// the location of this script file, which probably isn't what you want
|
|
"ContentRoot" "{source_root_path}"
|
|
|
|
// include all files recursivley
|
|
"FileMapping"
|
|
{{
|
|
// This can be a full path, or a path relative to ContentRoot
|
|
"LocalPath" "*"
|
|
|
|
// This is a path relative to the install folder of your game
|
|
"DepotPath" "."
|
|
|
|
// If LocalPath contains wildcards, setting this means that all
|
|
// matching files within subdirectories of LocalPath will also
|
|
// be included.
|
|
"recursive" "1"
|
|
}}
|
|
|
|
// but exclude all symbol files
|
|
// This can be a full path, or a path relative to ContentRoot
|
|
//"FileExclusion" "*.pdb"
|
|
}}
|
|
"""
|
|
|
|
(track_build_root_path / "app_build.vdf").write_text(app_build_script)
|
|
platform_build_file_path.write_text(platform_build_script)
|
|
|
|
worker.utils.info(
|
|
f"Version [{version_info.version}] for [{platform_id}] in preview [{is_preview}] for steam branch [{steam_branch_id}], building"
|
|
)
|
|
|
|
cmd = [
|
|
"steamcmd",
|
|
"+login",
|
|
worker.utils.HiddenArgument(steam_user_id),
|
|
worker.utils.HiddenArgument(steam_user_password),
|
|
"+run_app_build",
|
|
track_build_root_path / "app_build.vdf",
|
|
"+quit",
|
|
]
|
|
retry_count = 0 if preview else 3
|
|
|
|
worker.utils.call(
|
|
cmd, retry_count=retry_count, retry_wait_time=120, dry_run=dry_run, env=env
|
|
)
|
|
|
|
worker.utils.info("Waiting 5 seconds for next steam command")
|
|
time.sleep(5.0)
|
|
|
|
worker.utils.info(
|
|
f"Version [{version_info.version}] for [{platform_id}] in preview [{is_preview}] is done, success"
|
|
)
|
|
|
|
|
|
def package(builder: worker.deploy.CodeStoreBuilder) -> None:
|
|
worker.utils.remove_dir(builder.store_steam_dir)
|
|
os.makedirs(builder.store_steam_dir, exist_ok=True)
|
|
|
|
# Extract and prepare content
|
|
extract(builder)
|
|
build(builder, is_preview=True)
|
|
|
|
|
|
def deliver(builder: worker.deploy.CodeStoreBuilder) -> None:
|
|
# This will push to the store
|
|
build(builder, is_preview=False)
|