unity-devkit 0.1.25__tar.gz → 0.2.0.dev215__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.
Files changed (26) hide show
  1. {unity_devkit-0.1.25 → unity_devkit-0.2.0.dev215}/PKG-INFO +1 -1
  2. {unity_devkit-0.1.25 → unity_devkit-0.2.0.dev215}/pyproject.toml +1 -1
  3. {unity_devkit-0.1.25 → unity_devkit-0.2.0.dev215}/src/unity_devkit/build_unity.py +4 -2
  4. {unity_devkit-0.1.25 → unity_devkit-0.2.0.dev215}/src/unity_devkit/ci_build_unity.py +29 -9
  5. {unity_devkit-0.1.25 → unity_devkit-0.2.0.dev215}/src/unity_devkit/install.py +7 -7
  6. {unity_devkit-0.1.25 → unity_devkit-0.2.0.dev215}/src/unity_devkit/player_build.py +3 -3
  7. {unity_devkit-0.1.25 → unity_devkit-0.2.0.dev215}/tests/test_build_player.py +3 -3
  8. {unity_devkit-0.1.25 → unity_devkit-0.2.0.dev215}/.gitignore +0 -0
  9. {unity_devkit-0.1.25 → unity_devkit-0.2.0.dev215}/src/unity_devkit/__init__.py +0 -0
  10. {unity_devkit-0.1.25 → unity_devkit-0.2.0.dev215}/src/unity_devkit/compile_check_unity.py +0 -0
  11. {unity_devkit-0.1.25 → unity_devkit-0.2.0.dev215}/src/unity_devkit/identity.py +0 -0
  12. {unity_devkit-0.1.25 → unity_devkit-0.2.0.dev215}/src/unity_devkit/license.py +0 -0
  13. {unity_devkit-0.1.25 → unity_devkit-0.2.0.dev215}/src/unity_devkit/license_restore.py +0 -0
  14. {unity_devkit-0.1.25 → unity_devkit-0.2.0.dev215}/src/unity_devkit/lock_unity.py +0 -0
  15. {unity_devkit-0.1.25 → unity_devkit-0.2.0.dev215}/src/unity_devkit/matrix.py +0 -0
  16. {unity_devkit-0.1.25 → unity_devkit-0.2.0.dev215}/src/unity_devkit/projects.py +0 -0
  17. {unity_devkit-0.1.25 → unity_devkit-0.2.0.dev215}/src/unity_devkit/py.typed +0 -0
  18. {unity_devkit-0.1.25 → unity_devkit-0.2.0.dev215}/src/unity_devkit/test_unity.py +0 -0
  19. {unity_devkit-0.1.25 → unity_devkit-0.2.0.dev215}/src/unity_devkit/upm_cache.py +0 -0
  20. {unity_devkit-0.1.25 → unity_devkit-0.2.0.dev215}/tests/fixtures/environment-dump.json +0 -0
  21. {unity_devkit-0.1.25 → unity_devkit-0.2.0.dev215}/tests/test_identity.py +0 -0
  22. {unity_devkit-0.1.25 → unity_devkit-0.2.0.dev215}/tests/test_matrix.py +0 -0
  23. {unity_devkit-0.1.25 → unity_devkit-0.2.0.dev215}/tests/test_prepare.py +0 -0
  24. {unity_devkit-0.1.25 → unity_devkit-0.2.0.dev215}/tests/test_projects.py +0 -0
  25. {unity_devkit-0.1.25 → unity_devkit-0.2.0.dev215}/tests/test_run_unity_batchmode.py +0 -0
  26. {unity_devkit-0.1.25 → unity_devkit-0.2.0.dev215}/tests/test_upm_cache.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: unity-devkit
3
- Version: 0.1.25
3
+ Version: 0.2.0.dev215
4
4
  Summary: Unity build, license, and CI tooling
5
5
  Requires-Python: >=3.13
6
6
  Requires-Dist: bashrun>=0.1.0
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "unity-devkit"
3
- version = "0.1.25"
3
+ version = "0.2.0.dev215"
4
4
  description = "Unity build, license, and CI tooling"
5
5
  requires-python = ">=3.13"
6
6
  dependencies = [
@@ -15,7 +15,9 @@ def build_unity(
15
15
  str,
16
16
  typer.Option(help="Version string to stamp into ProjectSettings.asset; empty builds unversioned"),
17
17
  ] = "",
18
- run_number: Annotated[int, typer.Option(help="bundleVersionCode to stamp; local builds default to 0")] = 0,
18
+ version_code: Annotated[
19
+ int, typer.Option(help="Android bundleVersionCode to stamp; local builds default to 0")
20
+ ] = 0,
19
21
  development: Annotated[bool, typer.Option(help="Development build column")] = False,
20
22
  environment_preset: Annotated[
21
23
  str,
@@ -33,7 +35,7 @@ def build_unity(
33
35
  project,
34
36
  build,
35
37
  version=version,
36
- run_number=run_number,
38
+ version_code=version_code,
37
39
  development=development,
38
40
  environment_preset=environment_preset,
39
41
  environment_fields=fields,
@@ -5,7 +5,7 @@ from pathlib import Path
5
5
  from typing import Annotated
6
6
 
7
7
  import typer
8
- from bashrun.bash import bash
8
+ from bashrun.bash import bash, bash_output
9
9
  from pydantic_settings import BaseSettings
10
10
 
11
11
  from ci_devkit.builds import build_repository, push_build
@@ -30,7 +30,9 @@ app = typer.Typer(add_completion=False, pretty_exceptions_show_locals=False)
30
30
  def ci_build_unity(
31
31
  project: Annotated[str, typer.Option(help="Project name")],
32
32
  platform: Annotated[str, typer.Option(help="Target platform")],
33
- run_number: Annotated[int, typer.Option(help="CI run number (also the bundleVersionCode)")],
33
+ version_code: Annotated[
34
+ int, typer.Option(help="Android bundleVersionCode (the per-app commit count from get-app-version)")
35
+ ],
34
36
  registry: Annotated[str, typer.Option(help="OCI registry root; the verb derives the cache and builds namespaces")],
35
37
  pr_number: Annotated[
36
38
  str, typer.Option(help="Pull-request number; empty on non-PR runs selects the dev cache scope")
@@ -75,7 +77,7 @@ def ci_build_unity(
75
77
  project,
76
78
  platform,
77
79
  version=version,
78
- run_number=run_number,
80
+ version_code=version_code,
79
81
  development=development,
80
82
  environment_preset=environment_preset,
81
83
  environment_fields=fields,
@@ -99,15 +101,22 @@ def ci_build_unity(
99
101
  build_directory = project_path / "Build"
100
102
  if build_directory.is_dir():
101
103
  artifact_directory = Path("/tmp/unity-builds")
102
- artifact_directory.mkdir(parents=True, exist_ok=True)
104
+ shutil.rmtree(artifact_directory, ignore_errors=True)
105
+ artifact_directory.mkdir(parents=True)
106
+ report_directory = Path("/tmp/unity-build-reports")
107
+ shutil.rmtree(report_directory, ignore_errors=True)
103
108
  if platform == "Linux":
104
109
  shutil.copytree(build_directory, artifact_directory, dirs_exist_ok=True)
105
110
  else:
106
- for file in build_directory.rglob("*"):
107
- if file.suffix in {".apk", ".exe"}:
108
- shutil.copy2(file, artifact_directory / file.name)
111
+ binaries = [file for file in build_directory.rglob("*") if file.suffix in {".apk", ".exe"}]
112
+ if len(binaries) != 1:
113
+ raise SystemExit(
114
+ f"build for ({project}, {platform}) must produce exactly one binary, "
115
+ f"found {len(binaries)} ({', '.join(file.name for file in binaries)})"
116
+ )
117
+ shutil.copy2(binaries[0], artifact_directory / binaries[0].name)
109
118
  for report in build_directory.rglob("BuildReport.json"):
110
- destination = artifact_directory / report.relative_to(build_directory)
119
+ destination = report_directory / report.relative_to(build_directory)
111
120
  destination.parent.mkdir(parents=True, exist_ok=True)
112
121
  shutil.copy2(report, destination)
113
122
 
@@ -116,7 +125,8 @@ def ci_build_unity(
116
125
  if not artifact_directory.is_dir() or not any(artifact_directory.iterdir()):
117
126
  print("No build artifacts to push")
118
127
  else:
119
- tags = (derived_cache_key, f"run-{run_number}")
128
+ head_sha = bash_output("git rev-parse HEAD").strip()
129
+ tags = (derived_cache_key, f"sha-{head_sha}")
120
130
  if platform == "Linux":
121
131
  staging = Path("/tmp/unity-builds-push")
122
132
  staging.mkdir(parents=True, exist_ok=True)
@@ -135,5 +145,15 @@ def ci_build_unity(
135
145
  )
136
146
  for build_tag in tags:
137
147
  push_build(builds_namespace, project, platform, build_tag, artifact_directory, paths)
148
+
149
+ report_directory = Path("/tmp/unity-build-reports")
150
+ if report_directory.is_dir():
151
+ report_paths = sorted(
152
+ str(path.relative_to(report_directory)) for path in report_directory.rglob("BuildReport.json")
153
+ )
154
+ for build_tag in tags:
155
+ push_build(
156
+ builds_namespace, project, f"{platform}-report", build_tag, report_directory, report_paths
157
+ )
138
158
  repository = build_repository(builds_namespace, project, platform)
139
159
  print(f"Pushed build artifacts: {repository} ({', '.join(tags)})")
@@ -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
- run: Annotated[
37
- int | None,
38
- typer.Option("--run", "-r", help="Specific CI run number (github.run_number) to pull"),
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 / --run are ignored."
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 run:
99
- print("Warning: --pr-number / --run are ignored when --build is set")
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"run-{run}" if run else cache_key(project_name, target_name, parsed_pr_number)
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)
@@ -197,7 +197,7 @@ def build_player(
197
197
  build: str,
198
198
  *,
199
199
  version: str = "",
200
- run_number: int = 0,
200
+ version_code: int = 0,
201
201
  development: bool = False,
202
202
  environment_preset: str = "",
203
203
  environment_fields: dict[str, str] | None = None,
@@ -220,10 +220,10 @@ def build_player(
220
220
  if version:
221
221
  settings_path = project_path / "ProjectSettings" / "ProjectSettings.asset"
222
222
  rewritten = settings_path.read_text(encoding="utf-8")
223
- for field_name, value in (("AndroidBundleVersionCode", str(run_number)), ("bundleVersion", version)):
223
+ for field_name, value in (("AndroidBundleVersionCode", str(version_code)), ("bundleVersion", version)):
224
224
  rewritten = replace_serialized_field(rewritten, field_name, value)
225
225
  settings_path.write_text(rewritten, encoding="utf-8")
226
- print(f"Stamped bundleVersion {version} (bundleVersionCode={run_number}) into ProjectSettings")
226
+ print(f"Stamped bundleVersion {version} (bundleVersionCode={version_code}) into ProjectSettings")
227
227
 
228
228
  build_directory = project_path / "Build"
229
229
  before = snapshot_artifacts(build_directory)
@@ -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+42", run_number=42)
49
+ produced = player_build.build_player("tool", "AndroidMobile", version="0.2.7-dev+42", version_code=42)
50
50
 
51
51
  rewritten = (project / "ProjectSettings" / "ProjectSettings.asset").read_text()
52
52
  assert " AndroidBundleVersionCode: 42\n" in rewritten
@@ -61,7 +61,7 @@ def test_build_player_treats_the_version_string_as_opaque(tmp_path: Path, monkey
61
61
  monkeypatch.chdir(tmp_path)
62
62
  monkeypatch.setattr(player_build, "run_unity_batchmode", fake_unity_build_producing)
63
63
 
64
- player_build.build_player("tool", "AndroidMobile", version="release-2026-09-27", run_number=7)
64
+ player_build.build_player("tool", "AndroidMobile", version="release-2026-09-27", version_code=7)
65
65
 
66
66
  rewritten = (project / "ProjectSettings" / "ProjectSettings.asset").read_text()
67
67
  assert " bundleVersion: release-2026-09-27\n" in rewritten
@@ -75,7 +75,7 @@ def test_build_player_refuses_to_stamp_a_missing_field(tmp_path: Path, monkeypat
75
75
  monkeypatch.setattr(player_build, "run_unity_batchmode", fake_unity_build_producing)
76
76
 
77
77
  with pytest.raises(SystemExit, match="AndroidBundleVersionCode"):
78
- player_build.build_player("tool", "AndroidMobile", version="0.2.7-dev+42", run_number=42)
78
+ player_build.build_player("tool", "AndroidMobile", version="0.2.7-dev+42", version_code=42)
79
79
 
80
80
 
81
81
  def test_build_player_fails_when_no_artifact_was_produced(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: