unity-devkit 0.1.25.dev37268005125__tar.gz → 0.3.0.dev223__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {unity_devkit-0.1.25.dev37268005125 → unity_devkit-0.3.0.dev223}/PKG-INFO +2 -2
- {unity_devkit-0.1.25.dev37268005125 → unity_devkit-0.3.0.dev223}/pyproject.toml +2 -3
- unity_devkit-0.3.0.dev223/src/unity_devkit/build_unity.py +149 -0
- {unity_devkit-0.1.25.dev37268005125 → unity_devkit-0.3.0.dev223}/src/unity_devkit/compile_check_unity.py +9 -20
- {unity_devkit-0.1.25.dev37268005125 → unity_devkit-0.3.0.dev223}/src/unity_devkit/install.py +10 -10
- {unity_devkit-0.1.25.dev37268005125 → unity_devkit-0.3.0.dev223}/src/unity_devkit/license.py +1 -10
- {unity_devkit-0.1.25.dev37268005125 → unity_devkit-0.3.0.dev223}/src/unity_devkit/license_restore.py +3 -3
- {unity_devkit-0.1.25.dev37268005125 → unity_devkit-0.3.0.dev223}/src/unity_devkit/matrix.py +4 -8
- {unity_devkit-0.1.25.dev37268005125 → unity_devkit-0.3.0.dev223}/src/unity_devkit/player_build.py +4 -3
- {unity_devkit-0.1.25.dev37268005125 → unity_devkit-0.3.0.dev223}/src/unity_devkit/upm_cache.py +1 -1
- {unity_devkit-0.1.25.dev37268005125 → unity_devkit-0.3.0.dev223}/tests/test_build_player.py +7 -5
- {unity_devkit-0.1.25.dev37268005125 → unity_devkit-0.3.0.dev223}/tests/test_matrix.py +9 -16
- unity_devkit-0.1.25.dev37268005125/src/unity_devkit/build_unity.py +0 -41
- unity_devkit-0.1.25.dev37268005125/src/unity_devkit/ci_build_unity.py +0 -139
- {unity_devkit-0.1.25.dev37268005125 → unity_devkit-0.3.0.dev223}/.gitignore +0 -0
- {unity_devkit-0.1.25.dev37268005125 → unity_devkit-0.3.0.dev223}/src/unity_devkit/__init__.py +0 -0
- {unity_devkit-0.1.25.dev37268005125 → unity_devkit-0.3.0.dev223}/src/unity_devkit/identity.py +0 -0
- {unity_devkit-0.1.25.dev37268005125 → unity_devkit-0.3.0.dev223}/src/unity_devkit/lock_unity.py +0 -0
- {unity_devkit-0.1.25.dev37268005125 → unity_devkit-0.3.0.dev223}/src/unity_devkit/projects.py +0 -0
- {unity_devkit-0.1.25.dev37268005125 → unity_devkit-0.3.0.dev223}/src/unity_devkit/py.typed +0 -0
- {unity_devkit-0.1.25.dev37268005125 → unity_devkit-0.3.0.dev223}/src/unity_devkit/test_unity.py +0 -0
- {unity_devkit-0.1.25.dev37268005125 → unity_devkit-0.3.0.dev223}/tests/fixtures/environment-dump.json +0 -0
- {unity_devkit-0.1.25.dev37268005125 → unity_devkit-0.3.0.dev223}/tests/test_identity.py +0 -0
- {unity_devkit-0.1.25.dev37268005125 → unity_devkit-0.3.0.dev223}/tests/test_prepare.py +0 -0
- {unity_devkit-0.1.25.dev37268005125 → unity_devkit-0.3.0.dev223}/tests/test_projects.py +0 -0
- {unity_devkit-0.1.25.dev37268005125 → unity_devkit-0.3.0.dev223}/tests/test_run_unity_batchmode.py +0 -0
- {unity_devkit-0.1.25.dev37268005125 → unity_devkit-0.3.0.dev223}/tests/test_upm_cache.py +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: unity-devkit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0.dev223
|
|
4
4
|
Summary: Unity build, license, and CI tooling
|
|
5
5
|
Requires-Python: >=3.13
|
|
6
6
|
Requires-Dist: bashrun>=0.1.0
|
|
7
|
-
Requires-Dist:
|
|
7
|
+
Requires-Dist: build-artifact-registry>=0.2.0.dev0
|
|
8
8
|
Requires-Dist: pydantic-settings>=2.9.1
|
|
9
9
|
Requires-Dist: pydantic>=2.11.7
|
|
10
10
|
Requires-Dist: typer>=0.17.4
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "unity-devkit"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.3.0.dev223"
|
|
4
4
|
description = "Unity build, license, and CI tooling"
|
|
5
5
|
requires-python = ">=3.13"
|
|
6
6
|
dependencies = [
|
|
7
7
|
"bashrun>=0.1.0",
|
|
8
|
-
"
|
|
8
|
+
"build-artifact-registry>=0.2.0.dev0",
|
|
9
9
|
"typer>=0.17.4",
|
|
10
10
|
"pydantic>=2.11.7",
|
|
11
11
|
"pydantic-settings>=2.9.1",
|
|
@@ -15,7 +15,6 @@ dependencies = [
|
|
|
15
15
|
activate-unity-license = "unity_devkit.license:activate_app"
|
|
16
16
|
build-unity = "unity_devkit.build_unity:app"
|
|
17
17
|
build-unity-matrix = "unity_devkit.matrix:build_app"
|
|
18
|
-
ci-build-unity = "unity_devkit.ci_build_unity:app"
|
|
19
18
|
compile-check-unity = "unity_devkit.compile_check_unity:app"
|
|
20
19
|
compile-check-unity-matrix = "unity_devkit.matrix:compile_check_app"
|
|
21
20
|
install = "unity_devkit.install:app"
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import shutil
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Annotated
|
|
6
|
+
|
|
7
|
+
import typer
|
|
8
|
+
from bashrun.bash import bash, bash_output
|
|
9
|
+
|
|
10
|
+
from build_artifact_registry.builds import build_repository, push_build
|
|
11
|
+
from build_artifact_registry.cache import restore, save
|
|
12
|
+
from build_artifact_registry.setup_oras import install_oras
|
|
13
|
+
from .identity import builds_registry, cache_key, cache_registry
|
|
14
|
+
from .license_restore import restore_or_activate_license
|
|
15
|
+
from .player_build import build_player, parse_environment_fields, resolve_unity_project
|
|
16
|
+
from .upm_cache import restore_upm_cache, save_upm_cache
|
|
17
|
+
|
|
18
|
+
app = typer.Typer(add_completion=False, pretty_exceptions_show_locals=False)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@app.command()
|
|
22
|
+
def build_unity(
|
|
23
|
+
project: Annotated[str, typer.Option(help="Project name (the 'name' field of its unity-devkit.json)")],
|
|
24
|
+
platform: Annotated[str, typer.Option(help="Target platform (a key of the project's 'platforms' map)")],
|
|
25
|
+
version: Annotated[
|
|
26
|
+
str,
|
|
27
|
+
typer.Option(
|
|
28
|
+
help="Bare version base; with --commit-count renders bundleVersion={version}+{commit-count}; empty builds unversioned"
|
|
29
|
+
),
|
|
30
|
+
] = "",
|
|
31
|
+
pr_number: Annotated[
|
|
32
|
+
str, typer.Option(help="Pull-request number; empty on non-PR runs selects the dev cache scope")
|
|
33
|
+
] = "",
|
|
34
|
+
commit_count: Annotated[
|
|
35
|
+
int,
|
|
36
|
+
typer.Option(help="Commit count rendering AndroidBundleVersionCode and the +{count} bundleVersion suffix"),
|
|
37
|
+
] = 0,
|
|
38
|
+
license_cache_key: Annotated[
|
|
39
|
+
str,
|
|
40
|
+
typer.Option("--license-cache-key", help="ORAS tag the license cache restores under; empty uses today's tag"),
|
|
41
|
+
] = "",
|
|
42
|
+
registry: Annotated[
|
|
43
|
+
str,
|
|
44
|
+
typer.Option(
|
|
45
|
+
help="Build-artifact registry root; supplied runs the CI shape (license/UPM/library caches, "
|
|
46
|
+
"build-output push), empty is the local build"
|
|
47
|
+
),
|
|
48
|
+
] = "",
|
|
49
|
+
development: Annotated[bool, typer.Option("--development", help="Development build column")] = False,
|
|
50
|
+
environment_preset: Annotated[
|
|
51
|
+
str, typer.Option(help="Environment preset name; empty leaves the workspace untouched")
|
|
52
|
+
] = "",
|
|
53
|
+
environment_field: Annotated[
|
|
54
|
+
list[str] | None,
|
|
55
|
+
typer.Option("--environment-field", help="Environment field override as path=value (repeatable)"),
|
|
56
|
+
] = None,
|
|
57
|
+
) -> None:
|
|
58
|
+
fields = parse_environment_fields(environment_field or [])
|
|
59
|
+
parsed_pr_number = int(pr_number) if pr_number else None
|
|
60
|
+
derived_cache_key = cache_key(project, platform, parsed_pr_number)
|
|
61
|
+
library_cache_registry = cache_registry(registry)
|
|
62
|
+
builds_namespace = builds_registry(registry)
|
|
63
|
+
project_path = resolve_unity_project(project).path
|
|
64
|
+
|
|
65
|
+
if registry:
|
|
66
|
+
install_oras()
|
|
67
|
+
restore_or_activate_license(library_cache_registry, license_cache_key or None)
|
|
68
|
+
restore_upm_cache(project_path, library_cache_registry)
|
|
69
|
+
restore(library_cache_registry, "unity-library", derived_cache_key, Path("."))
|
|
70
|
+
|
|
71
|
+
artifacts = build_player(
|
|
72
|
+
project,
|
|
73
|
+
platform,
|
|
74
|
+
version=version,
|
|
75
|
+
commit_count=commit_count,
|
|
76
|
+
development=development,
|
|
77
|
+
environment_preset=environment_preset,
|
|
78
|
+
environment_fields=fields,
|
|
79
|
+
)
|
|
80
|
+
for artifact in artifacts:
|
|
81
|
+
print(f"Built: {artifact}")
|
|
82
|
+
|
|
83
|
+
if not registry:
|
|
84
|
+
return
|
|
85
|
+
|
|
86
|
+
# PackageCache (~1.6 GiB) is redundant with the shared UPM cache at ~/.cache/Unity/upm/
|
|
87
|
+
package_cache = project_path / "Library" / "PackageCache"
|
|
88
|
+
if package_cache.exists():
|
|
89
|
+
shutil.rmtree(package_cache)
|
|
90
|
+
|
|
91
|
+
relative_project_path = project_path.relative_to(Path.cwd())
|
|
92
|
+
save(library_cache_registry, "unity-library", derived_cache_key, Path("."), [f"{relative_project_path}/Library/"])
|
|
93
|
+
save_upm_cache(project_path, library_cache_registry)
|
|
94
|
+
|
|
95
|
+
build_directory = project_path / "Build"
|
|
96
|
+
if build_directory.is_dir():
|
|
97
|
+
artifact_directory = Path("/tmp/unity-builds")
|
|
98
|
+
shutil.rmtree(artifact_directory, ignore_errors=True)
|
|
99
|
+
artifact_directory.mkdir(parents=True)
|
|
100
|
+
report_directory = Path("/tmp/unity-build-reports")
|
|
101
|
+
shutil.rmtree(report_directory, ignore_errors=True)
|
|
102
|
+
if platform == "Linux":
|
|
103
|
+
shutil.copytree(build_directory, artifact_directory, dirs_exist_ok=True)
|
|
104
|
+
else:
|
|
105
|
+
binaries = [file for file in build_directory.rglob("*") if file.suffix in {".apk", ".exe"}]
|
|
106
|
+
if len(binaries) != 1:
|
|
107
|
+
raise SystemExit(
|
|
108
|
+
f"build for ({project}, {platform}) must produce exactly one binary, "
|
|
109
|
+
f"found {len(binaries)} ({', '.join(file.name for file in binaries)})"
|
|
110
|
+
)
|
|
111
|
+
shutil.copy2(binaries[0], artifact_directory / binaries[0].name)
|
|
112
|
+
for report in build_directory.rglob("BuildReport.json"):
|
|
113
|
+
destination = report_directory / report.relative_to(build_directory)
|
|
114
|
+
destination.parent.mkdir(parents=True, exist_ok=True)
|
|
115
|
+
shutil.copy2(report, destination)
|
|
116
|
+
|
|
117
|
+
artifact_directory = Path("/tmp/unity-builds")
|
|
118
|
+
if not artifact_directory.is_dir() or not any(artifact_directory.iterdir()):
|
|
119
|
+
print("No build artifacts to push")
|
|
120
|
+
return
|
|
121
|
+
|
|
122
|
+
head_sha = bash_output("git rev-parse HEAD").strip()
|
|
123
|
+
tags = (derived_cache_key, f"sha-{head_sha}")
|
|
124
|
+
if platform == "Linux":
|
|
125
|
+
staging = Path("/tmp/unity-builds-push")
|
|
126
|
+
staging.mkdir(parents=True, exist_ok=True)
|
|
127
|
+
tar_path = staging / "build.tar"
|
|
128
|
+
bash(f"tar -cf {tar_path} -C {artifact_directory} .")
|
|
129
|
+
try:
|
|
130
|
+
for build_tag in tags:
|
|
131
|
+
push_build(builds_namespace, project, platform, build_tag, staging, ["build.tar"])
|
|
132
|
+
finally:
|
|
133
|
+
tar_path.unlink(missing_ok=True)
|
|
134
|
+
else:
|
|
135
|
+
paths = sorted(
|
|
136
|
+
str(path.relative_to(artifact_directory)) for path in artifact_directory.rglob("*") if path.is_file()
|
|
137
|
+
)
|
|
138
|
+
for build_tag in tags:
|
|
139
|
+
push_build(builds_namespace, project, platform, build_tag, artifact_directory, paths)
|
|
140
|
+
|
|
141
|
+
report_directory = Path("/tmp/unity-build-reports")
|
|
142
|
+
if report_directory.is_dir():
|
|
143
|
+
report_paths = sorted(
|
|
144
|
+
str(path.relative_to(report_directory)) for path in report_directory.rglob("BuildReport.json")
|
|
145
|
+
)
|
|
146
|
+
for build_tag in tags:
|
|
147
|
+
push_build(builds_namespace, project, f"{platform}-report", build_tag, report_directory, report_paths)
|
|
148
|
+
repository = build_repository(builds_namespace, project, platform)
|
|
149
|
+
print(f"Pushed build artifacts: {repository} ({', '.join(tags)})")
|
|
@@ -3,21 +3,13 @@ from __future__ import annotations
|
|
|
3
3
|
from typing import Annotated
|
|
4
4
|
|
|
5
5
|
import typer
|
|
6
|
-
from pydantic_settings import BaseSettings
|
|
7
6
|
|
|
8
|
-
from
|
|
9
|
-
from ci_devkit.setup import configure_git
|
|
10
|
-
from ci_devkit.setup_oras import install_oras
|
|
7
|
+
from build_artifact_registry.setup_oras import install_oras
|
|
11
8
|
from .identity import cache_registry
|
|
12
9
|
from .license_restore import restore_or_activate_license
|
|
13
10
|
from .player_build import prepare_unity_project, resolve_unity_project, run_unity_batchmode
|
|
14
11
|
from .upm_cache import restore_upm_cache, save_upm_cache
|
|
15
12
|
|
|
16
|
-
|
|
17
|
-
class Settings(BaseSettings):
|
|
18
|
-
github_workspace: str
|
|
19
|
-
|
|
20
|
-
|
|
21
13
|
app = typer.Typer(add_completion=False, pretty_exceptions_show_locals=False)
|
|
22
14
|
|
|
23
15
|
COMPILE_ERROR_SIGNATURES = ("error CS",)
|
|
@@ -26,13 +18,11 @@ COMPILE_ERROR_SIGNATURES = ("error CS",)
|
|
|
26
18
|
@app.command()
|
|
27
19
|
def compile_check_unity(
|
|
28
20
|
project: Annotated[str, typer.Option(help="Unity project name (the 'name' field of its unity-devkit.json)")],
|
|
29
|
-
|
|
30
|
-
license_pin: Annotated[
|
|
21
|
+
license_cache_key: Annotated[
|
|
31
22
|
str,
|
|
32
|
-
typer.Option(
|
|
33
|
-
"--license", help="License cache pin for this run (the matrix verb's license output; empty uses today's)"
|
|
34
|
-
),
|
|
23
|
+
typer.Option("--license-cache-key", help="ORAS tag the license cache restores under; empty uses today's tag"),
|
|
35
24
|
] = "",
|
|
25
|
+
registry: Annotated[str, typer.Option(help="OCI registry root; the verb derives the cache namespace")] = "",
|
|
36
26
|
execute_method: Annotated[
|
|
37
27
|
str | None,
|
|
38
28
|
typer.Option(help="Static method to run after load (Class.Method) — one editor session per invocation"),
|
|
@@ -42,15 +32,14 @@ def compile_check_unity(
|
|
|
42
32
|
typer.Option(help="Startup build target (e.g. Android) for sessions that must open on a non-default platform"),
|
|
43
33
|
] = None,
|
|
44
34
|
) -> None:
|
|
45
|
-
|
|
35
|
+
if not registry:
|
|
36
|
+
raise SystemExit("compile-check needs --registry (the OCI registry root)")
|
|
46
37
|
project_path = resolve_unity_project(project).path
|
|
47
38
|
cache_namespace = cache_registry(registry)
|
|
48
39
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
restore_or_activate_license(cache_namespace, license_pin or None)
|
|
53
|
-
restore_upm_cache(project_path, cache_namespace)
|
|
40
|
+
install_oras()
|
|
41
|
+
restore_or_activate_license(cache_namespace, license_cache_key or None)
|
|
42
|
+
restore_upm_cache(project_path, cache_namespace)
|
|
54
43
|
|
|
55
44
|
extra_flags = ""
|
|
56
45
|
if build_target:
|
{unity_devkit-0.1.25.dev37268005125 → unity_devkit-0.3.0.dev223}/src/unity_devkit/install.py
RENAMED
|
@@ -8,8 +8,8 @@ from typing import Annotated
|
|
|
8
8
|
import typer
|
|
9
9
|
from bashrun.bash import bash, bash_handoff, bash_output
|
|
10
10
|
|
|
11
|
-
from
|
|
12
|
-
from
|
|
11
|
+
from build_artifact_registry.builds import build_reference, build_repository, list_build_tags, pull_artifact
|
|
12
|
+
from build_artifact_registry.setup_oras import install_oras
|
|
13
13
|
|
|
14
14
|
from .identity import builds_registry, cache_key
|
|
15
15
|
from .player_build import build_player
|
|
@@ -33,9 +33,9 @@ def main(
|
|
|
33
33
|
str,
|
|
34
34
|
typer.Option("--pr-number", help="Pull-request number; empty (outside PRs) selects the dev cache-key tag"),
|
|
35
35
|
] = "",
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
typer.Option("--
|
|
36
|
+
sha: Annotated[
|
|
37
|
+
str | None,
|
|
38
|
+
typer.Option("--sha", help="CI head SHA whose sha-{sha} builds-shelf tag to pull"),
|
|
39
39
|
] = None,
|
|
40
40
|
serial: Annotated[str | None, typer.Option("--serial", "-s", help="adb device serial")] = None,
|
|
41
41
|
build_locally: Annotated[
|
|
@@ -45,7 +45,7 @@ def main(
|
|
|
45
45
|
"-B",
|
|
46
46
|
help=(
|
|
47
47
|
"Build the project locally via `build-unity` and install the produced APK / "
|
|
48
|
-
"linux executable. Skips the OCI pull; --pr-number / --
|
|
48
|
+
"linux executable. Skips the OCI pull; --pr-number / --sha are ignored."
|
|
49
49
|
),
|
|
50
50
|
),
|
|
51
51
|
] = False,
|
|
@@ -95,8 +95,8 @@ def main(
|
|
|
95
95
|
return
|
|
96
96
|
|
|
97
97
|
if build_locally:
|
|
98
|
-
if pr_number or
|
|
99
|
-
print("Warning: --pr-number / --
|
|
98
|
+
if pr_number or sha:
|
|
99
|
+
print("Warning: --pr-number / --sha are ignored when --build is set")
|
|
100
100
|
if dry_run:
|
|
101
101
|
print(f"Would build {project_name} [{target_name}] locally")
|
|
102
102
|
if target_name in ADB_TARGETS:
|
|
@@ -110,7 +110,7 @@ def main(
|
|
|
110
110
|
executables = [path for path in produced if path.suffix in {".exe", ".x86_64"}]
|
|
111
111
|
else:
|
|
112
112
|
parsed_pr_number = int(pr_number) if pr_number else None
|
|
113
|
-
tag = f"
|
|
113
|
+
tag = f"sha-{sha}" if sha else cache_key(project_name, target_name, parsed_pr_number)
|
|
114
114
|
|
|
115
115
|
registry = _builds_registry()
|
|
116
116
|
reference = build_reference(registry, project_name, target_name, tag)
|
|
@@ -133,7 +133,7 @@ def main(
|
|
|
133
133
|
print(f"Using cached build: {cache_path}")
|
|
134
134
|
else:
|
|
135
135
|
cache_path.mkdir(parents=True, exist_ok=True)
|
|
136
|
-
|
|
136
|
+
pull_artifact(
|
|
137
137
|
registry,
|
|
138
138
|
project_name,
|
|
139
139
|
target_name,
|
{unity_devkit-0.1.25.dev37268005125 → unity_devkit-0.3.0.dev223}/src/unity_devkit/license.py
RENAMED
|
@@ -3,18 +3,10 @@ from __future__ import annotations
|
|
|
3
3
|
from typing import Annotated
|
|
4
4
|
|
|
5
5
|
import typer
|
|
6
|
-
from pydantic_settings import BaseSettings
|
|
7
6
|
|
|
8
|
-
from
|
|
9
|
-
from ci_devkit.setup_oras import install_oras
|
|
7
|
+
from build_artifact_registry.setup_oras import install_oras
|
|
10
8
|
from .license_restore import activate_license, restore_or_activate_license
|
|
11
9
|
|
|
12
|
-
|
|
13
|
-
class Settings(BaseSettings):
|
|
14
|
-
github_workspace: str
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
settings = Settings.model_validate({})
|
|
18
10
|
activate_app = typer.Typer(add_completion=False, pretty_exceptions_show_locals=False)
|
|
19
11
|
|
|
20
12
|
|
|
@@ -25,7 +17,6 @@ def activate_main(
|
|
|
25
17
|
] = False,
|
|
26
18
|
registry: Annotated[str, typer.Option(help="OCI registry namespace for the license cache")] = "",
|
|
27
19
|
) -> None:
|
|
28
|
-
configure_git(settings.github_workspace)
|
|
29
20
|
install_oras()
|
|
30
21
|
if oras_push:
|
|
31
22
|
if not registry:
|
{unity_devkit-0.1.25.dev37268005125 → unity_devkit-0.3.0.dev223}/src/unity_devkit/license_restore.py
RENAMED
|
@@ -9,7 +9,7 @@ import typer
|
|
|
9
9
|
from bashrun.bash import CalledProcessError, bash
|
|
10
10
|
from pydantic_settings import BaseSettings
|
|
11
11
|
|
|
12
|
-
from
|
|
12
|
+
from build_artifact_registry.cache import restore, save
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
class Settings(BaseSettings):
|
|
@@ -72,7 +72,7 @@ def license_activated() -> bool:
|
|
|
72
72
|
return True
|
|
73
73
|
|
|
74
74
|
|
|
75
|
-
# A whole CI run pins one tag (the
|
|
76
|
-
# --license) so a run straddling midnight UTC does not save/restore-miss itself.
|
|
75
|
+
# A whole CI run pins one tag (the getter's unity-license-tag output, passed as
|
|
76
|
+
# --license-cache-key) so a run straddling midnight UTC does not save/restore-miss itself.
|
|
77
77
|
def license_cache_tag() -> str:
|
|
78
78
|
return f"v-{datetime.now(UTC).strftime('%Y-%m-%d')}"
|
|
@@ -5,7 +5,6 @@ from typing import Annotated
|
|
|
5
5
|
|
|
6
6
|
import typer
|
|
7
7
|
|
|
8
|
-
from .license_restore import license_cache_tag
|
|
9
8
|
from .player_build import LICENSE_IMAGE_MODULE, PLATFORM_CONFIGS, UNITYCI_IMAGE_REVISION, read_editor_version
|
|
10
9
|
from .projects import ProjectEntry, discover_projects
|
|
11
10
|
|
|
@@ -29,9 +28,8 @@ def build_matrix(
|
|
|
29
28
|
for platform in entry.builds:
|
|
30
29
|
image_module = PLATFORM_CONFIGS[platform]["unityci_image_module"]
|
|
31
30
|
matrix.append({
|
|
32
|
-
"project
|
|
31
|
+
"project": name,
|
|
33
32
|
"platform": platform,
|
|
34
|
-
"unityci-image-module": image_module,
|
|
35
33
|
"editor-image": f"unityci/editor:{version}-{image_module}-{UNITYCI_IMAGE_REVISION}",
|
|
36
34
|
})
|
|
37
35
|
|
|
@@ -40,8 +38,7 @@ def build_matrix(
|
|
|
40
38
|
"No projects with builds declared — build-unity-matrix needs at least one unity-devkit.json "
|
|
41
39
|
"with a non-empty 'platforms' map"
|
|
42
40
|
)
|
|
43
|
-
print(
|
|
44
|
-
print(f"license={license_cache_tag()}")
|
|
41
|
+
print(json.dumps(matrix))
|
|
45
42
|
|
|
46
43
|
|
|
47
44
|
@compile_check_app.command()
|
|
@@ -56,12 +53,11 @@ def compile_check_matrix(
|
|
|
56
53
|
for name, entry in projects.items():
|
|
57
54
|
version = read_editor_version(entry.path)
|
|
58
55
|
matrix.append({
|
|
59
|
-
"project
|
|
56
|
+
"project": name,
|
|
60
57
|
"editor-image": f"unityci/editor:{version}-{LICENSE_IMAGE_MODULE}-{UNITYCI_IMAGE_REVISION}",
|
|
61
58
|
})
|
|
62
59
|
|
|
63
|
-
print(
|
|
64
|
-
print(f"license={license_cache_tag()}")
|
|
60
|
+
print(json.dumps(matrix))
|
|
65
61
|
|
|
66
62
|
|
|
67
63
|
def filtered_projects(project: str | None) -> dict[str, ProjectEntry]:
|
{unity_devkit-0.1.25.dev37268005125 → unity_devkit-0.3.0.dev223}/src/unity_devkit/player_build.py
RENAMED
|
@@ -197,7 +197,7 @@ def build_player(
|
|
|
197
197
|
build: str,
|
|
198
198
|
*,
|
|
199
199
|
version: str = "",
|
|
200
|
-
|
|
200
|
+
commit_count: int = 0,
|
|
201
201
|
development: bool = False,
|
|
202
202
|
environment_preset: str = "",
|
|
203
203
|
environment_fields: dict[str, str] | None = None,
|
|
@@ -218,12 +218,13 @@ def build_player(
|
|
|
218
218
|
prepare_unity_project(project_path)
|
|
219
219
|
|
|
220
220
|
if version:
|
|
221
|
+
bundle_version = f"{version}+{commit_count}"
|
|
221
222
|
settings_path = project_path / "ProjectSettings" / "ProjectSettings.asset"
|
|
222
223
|
rewritten = settings_path.read_text(encoding="utf-8")
|
|
223
|
-
for field_name, value in (("AndroidBundleVersionCode", str(
|
|
224
|
+
for field_name, value in (("AndroidBundleVersionCode", str(commit_count)), ("bundleVersion", bundle_version)):
|
|
224
225
|
rewritten = replace_serialized_field(rewritten, field_name, value)
|
|
225
226
|
settings_path.write_text(rewritten, encoding="utf-8")
|
|
226
|
-
print(f"Stamped bundleVersion {
|
|
227
|
+
print(f"Stamped bundleVersion {bundle_version} (AndroidBundleVersionCode={commit_count}) into ProjectSettings")
|
|
227
228
|
|
|
228
229
|
build_directory = project_path / "Build"
|
|
229
230
|
before = snapshot_artifacts(build_directory)
|
{unity_devkit-0.1.25.dev37268005125 → unity_devkit-0.3.0.dev223}/src/unity_devkit/upm_cache.py
RENAMED
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import hashlib
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
|
|
6
|
-
from
|
|
6
|
+
from build_artifact_registry.cache import restore, save
|
|
7
7
|
|
|
8
8
|
UPM_CACHE_NAME = "unity-upm"
|
|
9
9
|
UPM_CACHE_DIRECTORY = Path.home() / ".cache" / "Unity" / "upm"
|
|
@@ -46,7 +46,7 @@ def test_build_player_stamps_version_and_returns_artifacts(tmp_path: Path, monke
|
|
|
46
46
|
monkeypatch.chdir(tmp_path)
|
|
47
47
|
monkeypatch.setattr(player_build, "run_unity_batchmode", fake_unity_build_producing)
|
|
48
48
|
|
|
49
|
-
produced = player_build.build_player("tool", "AndroidMobile", version="0.2.7-dev
|
|
49
|
+
produced = player_build.build_player("tool", "AndroidMobile", version="0.2.7-dev", commit_count=42)
|
|
50
50
|
|
|
51
51
|
rewritten = (project / "ProjectSettings" / "ProjectSettings.asset").read_text()
|
|
52
52
|
assert " AndroidBundleVersionCode: 42\n" in rewritten
|
|
@@ -56,15 +56,17 @@ def test_build_player_stamps_version_and_returns_artifacts(tmp_path: Path, monke
|
|
|
56
56
|
assert RECEIVED_SESSIONS[0][0] == "-buildTarget Android -executeMethod Outernet.PlayerBuild.Entry"
|
|
57
57
|
|
|
58
58
|
|
|
59
|
-
def
|
|
59
|
+
def test_build_player_appends_the_commit_count_to_an_opaque_version_base(
|
|
60
|
+
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
|
61
|
+
) -> None:
|
|
60
62
|
project = write_repository(tmp_path, PROJECT_SETTINGS, builds=["AndroidMobile"])
|
|
61
63
|
monkeypatch.chdir(tmp_path)
|
|
62
64
|
monkeypatch.setattr(player_build, "run_unity_batchmode", fake_unity_build_producing)
|
|
63
65
|
|
|
64
|
-
player_build.build_player("tool", "AndroidMobile", version="release-2026-09-27",
|
|
66
|
+
player_build.build_player("tool", "AndroidMobile", version="release-2026-09-27", commit_count=7)
|
|
65
67
|
|
|
66
68
|
rewritten = (project / "ProjectSettings" / "ProjectSettings.asset").read_text()
|
|
67
|
-
assert " bundleVersion: release-2026-09-27\n" in rewritten
|
|
69
|
+
assert " bundleVersion: release-2026-09-27+7\n" in rewritten
|
|
68
70
|
|
|
69
71
|
|
|
70
72
|
def test_build_player_refuses_to_stamp_a_missing_field(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
@@ -75,7 +77,7 @@ def test_build_player_refuses_to_stamp_a_missing_field(tmp_path: Path, monkeypat
|
|
|
75
77
|
monkeypatch.setattr(player_build, "run_unity_batchmode", fake_unity_build_producing)
|
|
76
78
|
|
|
77
79
|
with pytest.raises(SystemExit, match="AndroidBundleVersionCode"):
|
|
78
|
-
player_build.build_player("tool", "AndroidMobile", version="0.2.7-dev
|
|
80
|
+
player_build.build_player("tool", "AndroidMobile", version="0.2.7-dev", commit_count=42)
|
|
79
81
|
|
|
80
82
|
|
|
81
83
|
def test_build_player_fails_when_no_artifact_was_produced(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
@@ -24,17 +24,14 @@ def test_matrix_emits_entries_from_catalog(
|
|
|
24
24
|
|
|
25
25
|
matrix.build_matrix()
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
assert include == [
|
|
27
|
+
legs = json.loads(capsys.readouterr().out)
|
|
28
|
+
assert legs == [
|
|
30
29
|
{
|
|
31
|
-
"project
|
|
30
|
+
"project": "tool",
|
|
32
31
|
"platform": "Linux",
|
|
33
|
-
"unityci-image-module": "linux-il2cpp",
|
|
34
32
|
"editor-image": "unityci/editor:6000.0.66f1-linux-il2cpp-3",
|
|
35
33
|
}
|
|
36
34
|
]
|
|
37
|
-
assert lines[1].startswith("license=v-")
|
|
38
35
|
|
|
39
36
|
|
|
40
37
|
def test_matrix_fails_without_builds(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
@@ -57,9 +54,8 @@ def test_matrix_project_filter_scopes_to_one_project(
|
|
|
57
54
|
|
|
58
55
|
matrix.build_matrix(project="other")
|
|
59
56
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
assert [entry["project-name"] for entry in include] == ["other"]
|
|
57
|
+
legs = json.loads(capsys.readouterr().out)
|
|
58
|
+
assert [entry["project"] for entry in legs] == ["other"]
|
|
63
59
|
|
|
64
60
|
|
|
65
61
|
def test_matrix_project_filter_fails_on_unknown_project(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
@@ -78,10 +74,8 @@ def test_check_matrix_includes_path_only_projects(
|
|
|
78
74
|
|
|
79
75
|
matrix.compile_check_matrix()
|
|
80
76
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
assert include == [{"project-name": "tool", "editor-image": "unityci/editor:6000.0.66f1-linux-il2cpp-3"}]
|
|
84
|
-
assert lines[1].startswith("license=v-")
|
|
77
|
+
legs = json.loads(capsys.readouterr().out)
|
|
78
|
+
assert legs == [{"project": "tool", "editor-image": "unityci/editor:6000.0.66f1-linux-il2cpp-3"}]
|
|
85
79
|
|
|
86
80
|
|
|
87
81
|
def test_check_matrix_project_filter_scopes_to_one_project(
|
|
@@ -96,6 +90,5 @@ def test_check_matrix_project_filter_scopes_to_one_project(
|
|
|
96
90
|
|
|
97
91
|
matrix.compile_check_matrix(project="other")
|
|
98
92
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
assert include == [{"project-name": "other", "editor-image": "unityci/editor:6000.0.66f1-linux-il2cpp-3"}]
|
|
93
|
+
legs = json.loads(capsys.readouterr().out)
|
|
94
|
+
assert legs == [{"project": "other", "editor-image": "unityci/editor:6000.0.66f1-linux-il2cpp-3"}]
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
from typing import Annotated
|
|
2
|
-
|
|
3
|
-
import typer
|
|
4
|
-
|
|
5
|
-
from .player_build import build_player, parse_environment_fields
|
|
6
|
-
|
|
7
|
-
app = typer.Typer(add_completion=False, pretty_exceptions_show_locals=False)
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
@app.command()
|
|
11
|
-
def build_unity(
|
|
12
|
-
project: Annotated[str, typer.Option(help="Unity project name (the 'name' field of its unity-devkit.json)")],
|
|
13
|
-
build: Annotated[str, typer.Option(help="Build target from the project's builds list (e.g. AndroidMobile)")],
|
|
14
|
-
version: Annotated[
|
|
15
|
-
str,
|
|
16
|
-
typer.Option(help="Version string to stamp into ProjectSettings.asset; empty builds unversioned"),
|
|
17
|
-
] = "",
|
|
18
|
-
run_number: Annotated[int, typer.Option(help="bundleVersionCode to stamp; local builds default to 0")] = 0,
|
|
19
|
-
development: Annotated[bool, typer.Option(help="Development build column")] = False,
|
|
20
|
-
environment_preset: Annotated[
|
|
21
|
-
str,
|
|
22
|
-
typer.Option(
|
|
23
|
-
help="Environment preset name (the env class's Presets key); unset leaves the workspace untouched"
|
|
24
|
-
),
|
|
25
|
-
] = "",
|
|
26
|
-
environment_field: Annotated[
|
|
27
|
-
list[str] | None,
|
|
28
|
-
typer.Option("--environment-field", help="Environment field override as path=value (repeatable)"),
|
|
29
|
-
] = None,
|
|
30
|
-
) -> None:
|
|
31
|
-
fields = parse_environment_fields(environment_field or [])
|
|
32
|
-
for artifact in build_player(
|
|
33
|
-
project,
|
|
34
|
-
build,
|
|
35
|
-
version=version,
|
|
36
|
-
run_number=run_number,
|
|
37
|
-
development=development,
|
|
38
|
-
environment_preset=environment_preset,
|
|
39
|
-
environment_fields=fields,
|
|
40
|
-
):
|
|
41
|
-
print(f"Built: {artifact}")
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import shutil
|
|
4
|
-
from pathlib import Path
|
|
5
|
-
from typing import Annotated
|
|
6
|
-
|
|
7
|
-
import typer
|
|
8
|
-
from bashrun.bash import bash
|
|
9
|
-
from pydantic_settings import BaseSettings
|
|
10
|
-
|
|
11
|
-
from ci_devkit.builds import build_repository, push_build
|
|
12
|
-
from ci_devkit.cache import restore, save
|
|
13
|
-
from ci_devkit.ci_step import ci_step
|
|
14
|
-
from ci_devkit.setup import configure_git, install_dotnet
|
|
15
|
-
from ci_devkit.setup_oras import install_oras
|
|
16
|
-
from .identity import builds_registry, cache_key, cache_registry
|
|
17
|
-
from .license_restore import restore_or_activate_license
|
|
18
|
-
from .player_build import build_player, parse_environment_fields, resolve_unity_project
|
|
19
|
-
from .upm_cache import restore_upm_cache, save_upm_cache
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class Settings(BaseSettings):
|
|
23
|
-
github_workspace: str
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
app = typer.Typer(add_completion=False, pretty_exceptions_show_locals=False)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
@app.command()
|
|
30
|
-
def ci_build_unity(
|
|
31
|
-
project: Annotated[str, typer.Option(help="Project name")],
|
|
32
|
-
platform: Annotated[str, typer.Option(help="Target platform")],
|
|
33
|
-
run_number: Annotated[int, typer.Option(help="CI run number (also the bundleVersionCode)")],
|
|
34
|
-
registry: Annotated[str, typer.Option(help="OCI registry root; the verb derives the cache and builds namespaces")],
|
|
35
|
-
pr_number: Annotated[
|
|
36
|
-
str, typer.Option(help="Pull-request number; empty on non-PR runs selects the dev cache scope")
|
|
37
|
-
] = "",
|
|
38
|
-
license_pin: Annotated[
|
|
39
|
-
str,
|
|
40
|
-
typer.Option(
|
|
41
|
-
"--license", help="License cache pin for this run (the matrix verb's license output; empty uses today's)"
|
|
42
|
-
),
|
|
43
|
-
] = "",
|
|
44
|
-
environment_preset: Annotated[
|
|
45
|
-
str, typer.Option(help="Environment preset name; empty leaves the workspace untouched")
|
|
46
|
-
] = "",
|
|
47
|
-
development: Annotated[bool, typer.Option("--development", help="Development build column")] = False,
|
|
48
|
-
environment_fields: Annotated[
|
|
49
|
-
str, typer.Option(help="Newline-separated path=value environment field overrides")
|
|
50
|
-
] = "",
|
|
51
|
-
version: Annotated[
|
|
52
|
-
str, typer.Option(help="Version string to stamp into ProjectSettings.asset; empty builds unversioned")
|
|
53
|
-
] = "",
|
|
54
|
-
) -> None:
|
|
55
|
-
settings = Settings.model_validate({})
|
|
56
|
-
fields = parse_environment_fields(environment_fields.splitlines())
|
|
57
|
-
parsed_pr_number = int(pr_number) if pr_number else None
|
|
58
|
-
derived_cache_key = cache_key(project, platform, parsed_pr_number)
|
|
59
|
-
library_cache_registry = cache_registry(registry)
|
|
60
|
-
builds_namespace = builds_registry(registry)
|
|
61
|
-
|
|
62
|
-
with ci_step("Setup"):
|
|
63
|
-
configure_git(settings.github_workspace)
|
|
64
|
-
install_dotnet("8.0")
|
|
65
|
-
install_oras()
|
|
66
|
-
restore_or_activate_license(library_cache_registry, license_pin or None)
|
|
67
|
-
project_path = resolve_unity_project(project).path
|
|
68
|
-
restore_upm_cache(project_path, library_cache_registry)
|
|
69
|
-
|
|
70
|
-
with ci_step("Restore library cache"):
|
|
71
|
-
restore(library_cache_registry, "unity-library", derived_cache_key, Path("."))
|
|
72
|
-
|
|
73
|
-
with ci_step(f"Build {project} [{platform}]"):
|
|
74
|
-
build_player(
|
|
75
|
-
project,
|
|
76
|
-
platform,
|
|
77
|
-
version=version,
|
|
78
|
-
run_number=run_number,
|
|
79
|
-
development=development,
|
|
80
|
-
environment_preset=environment_preset,
|
|
81
|
-
environment_fields=fields,
|
|
82
|
-
)
|
|
83
|
-
|
|
84
|
-
with ci_step("Save library cache"):
|
|
85
|
-
# PackageCache (~1.6 GiB) is redundant with the shared UPM cache at ~/.cache/Unity/upm/
|
|
86
|
-
package_cache = project_path / "Library" / "PackageCache"
|
|
87
|
-
if package_cache.exists():
|
|
88
|
-
shutil.rmtree(package_cache)
|
|
89
|
-
|
|
90
|
-
relative_project_path = project_path.relative_to(Path.cwd())
|
|
91
|
-
save(
|
|
92
|
-
library_cache_registry, "unity-library", derived_cache_key, Path("."), [f"{relative_project_path}/Library/"]
|
|
93
|
-
)
|
|
94
|
-
|
|
95
|
-
with ci_step("Save UPM cache"):
|
|
96
|
-
save_upm_cache(project_path, library_cache_registry)
|
|
97
|
-
|
|
98
|
-
with ci_step("Collect build artifacts"):
|
|
99
|
-
build_directory = project_path / "Build"
|
|
100
|
-
if build_directory.is_dir():
|
|
101
|
-
artifact_directory = Path("/tmp/unity-builds")
|
|
102
|
-
artifact_directory.mkdir(parents=True, exist_ok=True)
|
|
103
|
-
if platform == "Linux":
|
|
104
|
-
shutil.copytree(build_directory, artifact_directory, dirs_exist_ok=True)
|
|
105
|
-
else:
|
|
106
|
-
for file in build_directory.rglob("*"):
|
|
107
|
-
if file.suffix in {".apk", ".exe"}:
|
|
108
|
-
shutil.copy2(file, artifact_directory / file.name)
|
|
109
|
-
for report in build_directory.rglob("BuildReport.json"):
|
|
110
|
-
destination = artifact_directory / report.relative_to(build_directory)
|
|
111
|
-
destination.parent.mkdir(parents=True, exist_ok=True)
|
|
112
|
-
shutil.copy2(report, destination)
|
|
113
|
-
|
|
114
|
-
with ci_step("Push build artifacts"):
|
|
115
|
-
artifact_directory = Path("/tmp/unity-builds")
|
|
116
|
-
if not artifact_directory.is_dir() or not any(artifact_directory.iterdir()):
|
|
117
|
-
print("No build artifacts to push")
|
|
118
|
-
else:
|
|
119
|
-
tags = (derived_cache_key, f"run-{run_number}")
|
|
120
|
-
if platform == "Linux":
|
|
121
|
-
staging = Path("/tmp/unity-builds-push")
|
|
122
|
-
staging.mkdir(parents=True, exist_ok=True)
|
|
123
|
-
tar_path = staging / "build.tar"
|
|
124
|
-
bash(f"tar -cf {tar_path} -C {artifact_directory} .")
|
|
125
|
-
try:
|
|
126
|
-
for build_tag in tags:
|
|
127
|
-
push_build(builds_namespace, project, platform, build_tag, staging, ["build.tar"])
|
|
128
|
-
finally:
|
|
129
|
-
tar_path.unlink(missing_ok=True)
|
|
130
|
-
else:
|
|
131
|
-
paths = sorted(
|
|
132
|
-
str(path.relative_to(artifact_directory))
|
|
133
|
-
for path in artifact_directory.rglob("*")
|
|
134
|
-
if path.is_file()
|
|
135
|
-
)
|
|
136
|
-
for build_tag in tags:
|
|
137
|
-
push_build(builds_namespace, project, platform, build_tag, artifact_directory, paths)
|
|
138
|
-
repository = build_repository(builds_namespace, project, platform)
|
|
139
|
-
print(f"Pushed build artifacts: {repository} ({', '.join(tags)})")
|
|
File without changes
|
{unity_devkit-0.1.25.dev37268005125 → unity_devkit-0.3.0.dev223}/src/unity_devkit/__init__.py
RENAMED
|
File without changes
|
{unity_devkit-0.1.25.dev37268005125 → unity_devkit-0.3.0.dev223}/src/unity_devkit/identity.py
RENAMED
|
File without changes
|
{unity_devkit-0.1.25.dev37268005125 → unity_devkit-0.3.0.dev223}/src/unity_devkit/lock_unity.py
RENAMED
|
File without changes
|
{unity_devkit-0.1.25.dev37268005125 → unity_devkit-0.3.0.dev223}/src/unity_devkit/projects.py
RENAMED
|
File without changes
|
|
File without changes
|
{unity_devkit-0.1.25.dev37268005125 → unity_devkit-0.3.0.dev223}/src/unity_devkit/test_unity.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{unity_devkit-0.1.25.dev37268005125 → unity_devkit-0.3.0.dev223}/tests/test_run_unity_batchmode.py
RENAMED
|
File without changes
|
|
File without changes
|