unity-devkit 0.1.14__py3-none-any.whl → 0.1.16__py3-none-any.whl

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.
@@ -1,113 +1,41 @@
1
- from __future__ import annotations
2
-
3
- import json
4
- import os
5
- import shutil
6
- from pathlib import Path
7
1
  from typing import Annotated
8
2
 
9
3
  import typer
10
- from bashrun.bash import bash_output
11
- from pydantic_settings import BaseSettings
12
-
13
- from ci_devkit.cache import restore, save
14
- from ci_devkit.ci_step import ci_step
15
- from .license_restore import restore_license
16
- from ci_devkit.setup import configure_git, install_dotnet
17
- from ci_devkit.setup_oras import install_oras
18
- from .unity import prepare_unity_project, resolve_unity_build, run_unity_batchmode
19
-
20
-
21
- class Settings(BaseSettings):
22
- github_workspace: str
23
4
 
5
+ from .player_build import build_player, parse_environment_fields
24
6
 
25
7
  app = typer.Typer(add_completion=False, pretty_exceptions_show_locals=False)
26
8
 
27
9
 
28
10
  @app.command()
29
- def main(
30
- project: Annotated[str, typer.Option(help="Project name")],
31
- project_path: Annotated[Path, typer.Option(help="Path to Unity project")],
32
- platform: Annotated[str, typer.Option(help="Target platform")],
33
- cache_key: Annotated[str, typer.Option(help="Cache key prefix")],
34
- registry: Annotated[str, typer.Option(help="OCI registry path")],
35
- run_number: Annotated[int, typer.Option(help="CI run number")] = 0,
36
- branch: Annotated[str, typer.Option(help="Git branch name")] = "dev",
37
- build_env: Annotated[
38
- str, typer.Option(help="Newline-separated KEY=VALUE pairs injected into the Unity build process environment")
11
+ def build_unity(
12
+ project: Annotated[str, typer.Option(help="Unity project name (catalog key in 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
+ ),
39
25
  ] = "",
26
+ environment_field: Annotated[
27
+ list[str] | None,
28
+ typer.Option("--environment-field", help="Environment field override as path=value (repeatable)"),
29
+ ] = None,
40
30
  ) -> None:
41
- settings = Settings.model_validate({})
42
-
43
- for line in build_env.splitlines():
44
- entry = line.strip()
45
- if not entry:
46
- continue
47
- key, separator, value = entry.partition("=")
48
- if not separator:
49
- raise SystemExit(f"Invalid --build-env entry (expected KEY=VALUE): {entry!r}")
50
- os.environ[key.strip()] = value.strip()
51
-
52
- with ci_step("Setup"):
53
- configure_git(settings.github_workspace)
54
- install_dotnet("8.0")
55
- install_oras()
56
- restore_license()
57
-
58
- branch_slug = branch.replace("/", "-")
59
- tag = f"{cache_key}-{platform}-{branch_slug}"
60
- fallback_branch = "dev"
61
- fallback_tags = [f"{cache_key}-{platform}-{fallback_branch}"] if branch_slug != fallback_branch else None
62
-
63
- with ci_step("Restore library cache"):
64
- restore(registry, "unity-library", tag, Path("."), fallback_tags=fallback_tags)
65
-
66
- with ci_step("Prepare build"):
67
- project_config, build_flag, execute_method = resolve_unity_build(project, platform)
68
- unity_project_path = project_config.path
69
- if unity_project_path.resolve() != project_path.resolve():
70
- raise SystemExit(
71
- f"--project-path {project_path} does not match catalog entry '{project}' at {unity_project_path}"
72
- )
73
-
74
- with ci_step("Prepare project"):
75
- prepare_unity_project(unity_project_path)
76
-
77
- with ci_step(f"Build {project} [{platform}]"):
78
- tag_prefix = project_config.tag_prefix
79
- if tag_prefix:
80
- version = latest_tag_version(f"{tag_prefix}-v") or "0.0.0"
81
- full_version = f"{version}-dev+{run_number}" if branch != "main" else f"{version}+{run_number}"
82
- version_file = unity_project_path / ".build-version.json"
83
- version_file.write_text(json.dumps({"version": full_version, "runNumber": run_number}))
84
- print(f"Wrote version {full_version} (bundleVersionCode={run_number}) to {version_file}")
85
-
86
- run_unity_batchmode(unity_project_path, f"{build_flag} -executeMethod {execute_method}", nographics=False)
87
-
88
- with ci_step("Save library cache"):
89
- # PackageCache (~1.6 GiB) is redundant with the shared UPM cache at ~/.cache/Unity/upm/
90
- package_cache = project_path / "Library" / "PackageCache"
91
- if package_cache.exists():
92
- shutil.rmtree(package_cache)
93
-
94
- save(registry, "unity-library", tag, Path("."), [f"{project_path}/Library/"])
95
-
96
- with ci_step("Collect build artifacts"):
97
- build_directory = project_path / "Build"
98
- if build_directory.is_dir():
99
- artifact_directory = Path("/tmp/unity-builds")
100
- artifact_directory.mkdir(parents=True, exist_ok=True)
101
- if platform == "linux64":
102
- shutil.copytree(build_directory, artifact_directory, dirs_exist_ok=True)
103
- else:
104
- for file in build_directory.rglob("*"):
105
- if file.suffix in {".apk", ".exe"}:
106
- shutil.copy2(file, artifact_directory / file.name)
107
-
108
-
109
- def latest_tag_version(prefix: str) -> str | None:
110
- output = bash_output(f'git tag --list "{prefix}*" --sort=-v:refname').strip()
111
- if not output:
112
- return None
113
- return output.splitlines()[0][len(prefix) :]
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}")
@@ -0,0 +1,98 @@
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 pydantic_settings import BaseSettings
9
+
10
+ from ci_devkit.cache import restore, save
11
+ from ci_devkit.ci_step import ci_step
12
+ from .license_restore import restore_license
13
+ from ci_devkit.setup import configure_git, install_dotnet
14
+ from ci_devkit.setup_oras import install_oras
15
+ from .player_build import build_player, parse_environment_fields, resolve_unity_project
16
+
17
+
18
+ class Settings(BaseSettings):
19
+ github_workspace: str
20
+
21
+
22
+ app = typer.Typer(add_completion=False, pretty_exceptions_show_locals=False)
23
+
24
+
25
+ @app.command()
26
+ def ci_build_unity(
27
+ project: Annotated[str, typer.Option(help="Project name")],
28
+ project_path: Annotated[Path, typer.Option(help="Path to Unity project")],
29
+ platform: Annotated[str, typer.Option(help="Target platform")],
30
+ cache_key: Annotated[str, typer.Option(help="Cache key prefix")],
31
+ registry: Annotated[str, typer.Option(help="OCI registry path")],
32
+ run_number: Annotated[int, typer.Option(help="CI run number")] = 0,
33
+ branch: Annotated[str, typer.Option(help="Git branch name")] = "dev",
34
+ environment_preset: Annotated[
35
+ str, typer.Option(help="Environment preset name; empty leaves the workspace untouched")
36
+ ] = "",
37
+ development: Annotated[bool, typer.Option("--development", help="Development build column")] = False,
38
+ environment_fields: Annotated[
39
+ str, typer.Option(help="Newline-separated path=value environment field overrides")
40
+ ] = "",
41
+ version: Annotated[
42
+ str, typer.Option(help="Version string to stamp into ProjectSettings.asset; empty builds unversioned")
43
+ ] = "",
44
+ ) -> None:
45
+ settings = Settings.model_validate({})
46
+ fields = parse_environment_fields(environment_fields.splitlines())
47
+
48
+ with ci_step("Setup"):
49
+ configure_git(settings.github_workspace)
50
+ install_dotnet("8.0")
51
+ install_oras()
52
+ restore_license()
53
+
54
+ branch_slug = branch.replace("/", "-")
55
+ tag = f"{cache_key}-{platform}-{branch_slug}"
56
+ fallback_branch = "dev"
57
+ fallback_tags = [f"{cache_key}-{platform}-{fallback_branch}"] if branch_slug != fallback_branch else None
58
+
59
+ with ci_step("Restore library cache"):
60
+ restore(registry, "unity-library", tag, Path("."), fallback_tags=fallback_tags)
61
+
62
+ with ci_step("Prepare build"):
63
+ unity_project_path = resolve_unity_project(project).path
64
+ if unity_project_path.resolve() != project_path.resolve():
65
+ raise SystemExit(
66
+ f"--project-path {project_path} does not match catalog entry '{project}' at {unity_project_path}"
67
+ )
68
+
69
+ with ci_step(f"Build {project} [{platform}]"):
70
+ build_player(
71
+ project,
72
+ platform,
73
+ version=version,
74
+ run_number=run_number,
75
+ development=development,
76
+ environment_preset=environment_preset,
77
+ environment_fields=fields,
78
+ )
79
+
80
+ with ci_step("Save library cache"):
81
+ # PackageCache (~1.6 GiB) is redundant with the shared UPM cache at ~/.cache/Unity/upm/
82
+ package_cache = project_path / "Library" / "PackageCache"
83
+ if package_cache.exists():
84
+ shutil.rmtree(package_cache)
85
+
86
+ save(registry, "unity-library", tag, Path("."), [f"{project_path}/Library/"])
87
+
88
+ with ci_step("Collect build artifacts"):
89
+ build_directory = project_path / "Build"
90
+ if build_directory.is_dir():
91
+ artifact_directory = Path("/tmp/unity-builds")
92
+ artifact_directory.mkdir(parents=True, exist_ok=True)
93
+ if platform == "Linux":
94
+ shutil.copytree(build_directory, artifact_directory, dirs_exist_ok=True)
95
+ else:
96
+ for file in build_directory.rglob("*"):
97
+ if file.suffix in {".apk", ".exe"}:
98
+ shutil.copy2(file, artifact_directory / file.name)
@@ -0,0 +1,33 @@
1
+ from typing import Annotated
2
+
3
+ import typer
4
+
5
+ from .player_build import prepare_unity_project, resolve_unity_project, run_unity_batchmode
6
+
7
+ app = typer.Typer(add_completion=False, pretty_exceptions_show_locals=False)
8
+
9
+ COMPILE_ERROR_SIGNATURES = ("error CS",)
10
+
11
+
12
+ @app.command()
13
+ def compile_check_unity(
14
+ project: Annotated[str, typer.Option(help="Unity project name (catalog key in unity-devkit.json)")],
15
+ execute_method: Annotated[
16
+ str | None,
17
+ typer.Option(help="Static method to run after load (Class.Method) — one editor session per invocation"),
18
+ ] = None,
19
+ build_target: Annotated[
20
+ str | None,
21
+ typer.Option(help="Startup build target (e.g. Android) for sessions that must open on a non-default platform"),
22
+ ] = None,
23
+ ) -> None:
24
+ project_path = resolve_unity_project(project).path
25
+ extra_flags = ""
26
+ if build_target:
27
+ extra_flags += f" -buildTarget {build_target}"
28
+ if execute_method:
29
+ extra_flags += f" -executeMethod {execute_method}"
30
+ print(f"Compile-checking {project}...")
31
+ prepare_unity_project(project_path)
32
+ run_unity_batchmode(project_path, extra_flags, extra_failure_signatures=COMPILE_ERROR_SIGNATURES)
33
+ print(" Compiles clean")
@@ -0,0 +1,266 @@
1
+ import json
2
+ from pathlib import Path
3
+ from typing import Annotated
4
+
5
+ import typer
6
+ from pydantic import BaseModel, ConfigDict
7
+
8
+ from .compile_check_unity import COMPILE_ERROR_SIGNATURES
9
+ from .projects import load_catalog
10
+ from .player_build import prepare_unity_project, run_unity_batchmode
11
+
12
+ app = typer.Typer(add_completion=False, pretty_exceptions_show_locals=False)
13
+
14
+ DUMP_EXECUTE_METHOD = "Outernet.PlayerBuild.DumpEnvironment"
15
+ DISPATCH_INPUT_CAP = 25
16
+ NUMERIC_FIELD_TYPES = {
17
+ "Byte",
18
+ "SByte",
19
+ "Int16",
20
+ "UInt16",
21
+ "Int32",
22
+ "UInt32",
23
+ "Int64",
24
+ "UInt64",
25
+ "Single",
26
+ "Double",
27
+ "Decimal",
28
+ }
29
+
30
+
31
+ class EnvironmentEnum(BaseModel):
32
+ model_config = ConfigDict(extra="forbid")
33
+
34
+ name: str
35
+ values: list[str]
36
+ flags: bool
37
+
38
+
39
+ class EnvironmentFieldDump(BaseModel):
40
+ model_config = ConfigDict(extra="forbid")
41
+
42
+ path: str
43
+ field_type: str
44
+
45
+
46
+ class EnvironmentDump(BaseModel):
47
+ model_config = ConfigDict(extra="forbid")
48
+
49
+ class_name: str
50
+ mode_field: str
51
+ target_path: str
52
+ presets: dict[str, str]
53
+ enums: list[EnvironmentEnum]
54
+ fields: list[EnvironmentFieldDump]
55
+
56
+
57
+ @app.command()
58
+ def unity_dispatch(
59
+ project: Annotated[str, typer.Option(help="Unity project name (catalog key in unity-devkit.json)")],
60
+ output: Annotated[Path, typer.Option(help="Workflow file to write (or check with --check)")],
61
+ build_workflow: Annotated[
62
+ str,
63
+ typer.Option(
64
+ help="The build job's uses: value — the consumer's pinned build-unity.yml reference "
65
+ "(owner/repo/.github/workflows/build-unity.yml@sha, or ./.github/workflows/build-unity.yml inside unity-devkit)"
66
+ ),
67
+ ],
68
+ environment_config_class: Annotated[
69
+ str | None,
70
+ typer.Option(help="Env class file path override (defaults to build-config.json's environment_config)"),
71
+ ] = None,
72
+ check: Annotated[
73
+ bool, typer.Option("--check", help="Compare against the existing file instead of writing; fail on drift")
74
+ ] = False,
75
+ ) -> None:
76
+ projects = load_catalog()
77
+ if project not in projects:
78
+ raise SystemExit(f"Unknown project '{project}'. Valid: {', '.join(projects)}")
79
+
80
+ project_path = projects[project].path
81
+ env = {"ENVIRONMENT_CONFIG_CLASS": environment_config_class} if environment_config_class else None
82
+ print(f"Dumping environment of {project}...")
83
+ prepare_unity_project(project_path)
84
+ log_path = run_unity_batchmode(
85
+ project_path,
86
+ f"-executeMethod {DUMP_EXECUTE_METHOD}",
87
+ extra_failure_signatures=COMPILE_ERROR_SIGNATURES,
88
+ env=env,
89
+ )
90
+ dump = extract_environment_dump(log_path)
91
+ workflow = render_dispatch_workflow(dump, project=project, output=output.as_posix(), build_workflow=build_workflow)
92
+
93
+ if check:
94
+ if not output.is_file():
95
+ raise SystemExit(f"Dispatch workflow drift: {output} does not exist — generate it")
96
+ committed = output.read_text(encoding="utf-8")
97
+ if committed != workflow:
98
+ raise SystemExit(
99
+ f"Dispatch workflow drift: {output} differs from the environment class — regenerate with "
100
+ f"'{regen_command(project, output.as_posix(), build_workflow)}'"
101
+ )
102
+ print(f" {output} matches the environment class")
103
+ return
104
+
105
+ output.parent.mkdir(parents=True, exist_ok=True)
106
+ output.write_text(workflow, encoding="utf-8")
107
+ print(f" wrote {output}")
108
+
109
+
110
+ def extract_environment_dump(log_path: Path) -> EnvironmentDump:
111
+ for line in log_path.read_text(encoding="utf-8", errors="replace").splitlines():
112
+ candidate = line.strip()
113
+ if not candidate.startswith("{"):
114
+ continue
115
+ try:
116
+ payload = json.loads(candidate)
117
+ except json.JSONDecodeError:
118
+ continue
119
+ if isinstance(payload, dict) and "class_name" in payload:
120
+ return EnvironmentDump.model_validate(payload)
121
+
122
+ raise SystemExit(f"No environment dump JSON found in the editor log at {log_path}")
123
+
124
+
125
+ def render_dispatch_workflow(dump: EnvironmentDump, *, project: str, output: str, build_workflow: str) -> str:
126
+ enums_by_name = {environment_enum.name: environment_enum for environment_enum in dump.enums}
127
+ input_blocks: list[tuple[str, str, list[str], str | None]] = [
128
+ (
129
+ "environment-preset",
130
+ "Environment preset (Presets key) — empty leaves the workspace untouched",
131
+ ["type: choice", "options:", *indent_lines([*(f'- "{preset}"' for preset in ("", *dump.presets))], 2)],
132
+ 'default: ""',
133
+ ),
134
+ ("development", "Development build column", ["type: boolean"], "default: false"),
135
+ ]
136
+ for field in dump.fields:
137
+ if field.path == dump.mode_field:
138
+ continue
139
+ input_blocks.append(field_input_block(field, enums_by_name))
140
+
141
+ input_count = len(input_blocks)
142
+ if input_count > DISPATCH_INPUT_CAP:
143
+ raise SystemExit(
144
+ f"Dispatch panel would carry {input_count} inputs — GitHub's workflow_dispatch cap is {DISPATCH_INPUT_CAP}. "
145
+ "Shrink the env class or move configuration out of it."
146
+ )
147
+
148
+ transport_lines = [field_transport_line(field) for field in dump.fields if field.path != dump.mode_field]
149
+ header = (
150
+ f"# Generated by '{regen_command(project, output, build_workflow)}' from the environment class "
151
+ f"{dump.class_name} —"
152
+ )
153
+
154
+ lines = [
155
+ header,
156
+ "# edit the class, then regenerate this workflow.",
157
+ "name: Unity Dispatch Build",
158
+ "",
159
+ "on:",
160
+ " workflow_dispatch:",
161
+ " inputs:",
162
+ *indent_lines([line for block in input_blocks for line in build_input_block(block)], 6),
163
+ "",
164
+ "jobs:",
165
+ " build:",
166
+ f" uses: {build_workflow}",
167
+ " with:",
168
+ " environment-preset: ${{ inputs['environment-preset'] }}",
169
+ " development: ${{ inputs.development }}",
170
+ ]
171
+ if transport_lines:
172
+ lines.append(" environment-fields: |-")
173
+ lines.extend(indent_lines(transport_lines, 8))
174
+ else:
175
+ lines.append(" environment-fields: ''")
176
+ lines.append(" secrets: inherit")
177
+ return "\n".join(lines) + "\n"
178
+
179
+
180
+ def regen_command(project: str, output: str, build_workflow: str) -> str:
181
+ return f"uv run unity-dispatch --project {project} --output {output} --build-workflow {build_workflow}"
182
+
183
+
184
+ def field_input_block(
185
+ field: EnvironmentFieldDump, enums_by_name: dict[str, EnvironmentEnum]
186
+ ) -> tuple[str, str, list[str], str | None]:
187
+ input_id = field.path.replace(".", "-")
188
+ if field.field_type == "Boolean":
189
+ return (
190
+ input_id,
191
+ f"environment field {field.path} (bool) — checked overrides the preset value",
192
+ ["type: boolean"],
193
+ "default: false",
194
+ )
195
+
196
+ if field.field_type in enums_by_name:
197
+ environment_enum = enums_by_name[field.field_type]
198
+ if environment_enum.flags:
199
+ return (
200
+ input_id,
201
+ (
202
+ f"environment field {field.path} (flags: {', '.join(environment_enum.values)}) — "
203
+ "comma-separated names, empty keeps the preset value"
204
+ ),
205
+ ["type: string"],
206
+ 'default: ""',
207
+ )
208
+ return (
209
+ input_id,
210
+ f"environment field {field.path} ({environment_enum.name}) — empty keeps the preset value",
211
+ [
212
+ "type: choice",
213
+ "options:",
214
+ *indent_lines([*(f'- "{value}"' for value in ("", *environment_enum.values))], 2),
215
+ ],
216
+ 'default: ""',
217
+ )
218
+
219
+ if field.field_type == "String":
220
+ return (
221
+ input_id,
222
+ f"environment field {field.path} (string) — empty keeps the preset value",
223
+ ["type: string"],
224
+ 'default: ""',
225
+ )
226
+
227
+ if field.field_type in NUMERIC_FIELD_TYPES:
228
+ return (
229
+ input_id,
230
+ f"environment field {field.path} ({field.field_type}) — empty keeps the preset value",
231
+ ["type: number"],
232
+ None,
233
+ )
234
+
235
+ raise SystemExit(
236
+ f"Environment field '{field.path}' has type '{field.field_type}' — no dispatch input type maps to it "
237
+ "(arrays and object references self-exclude from the dump; this is contract drift)"
238
+ )
239
+
240
+
241
+ def field_transport_line(field: EnvironmentFieldDump) -> str:
242
+ input_id = field.path.replace(".", "-")
243
+ guard = "" if field.field_type == "Boolean" else " != ''"
244
+ return (
245
+ "${{ inputs['"
246
+ + input_id
247
+ + "']"
248
+ + guard
249
+ + " && format('"
250
+ + field.path
251
+ + "={0}', inputs['"
252
+ + input_id
253
+ + "']) || '' }}"
254
+ )
255
+
256
+
257
+ def build_input_block(input_block: tuple[str, str, list[str], str | None]) -> list[str]:
258
+ input_id, description, type_lines, default_line = input_block
259
+ lines = [f"{input_id}:", f' description: "{description}"', *[f" {line}" for line in type_lines]]
260
+ if default_line is not None:
261
+ lines.append(f" {default_line}")
262
+ return lines
263
+
264
+
265
+ def indent_lines(lines: list[str], spaces: int) -> list[str]:
266
+ return [f"{' ' * spaces}{line}" if line else line for line in lines]
unity_devkit/install.py CHANGED
@@ -6,13 +6,13 @@ from pathlib import Path
6
6
  from typing import Annotated, Any
7
7
 
8
8
  import typer
9
- from bashrun.bash import bash, bash_check, bash_handoff, bash_output
9
+ from bashrun.bash import bash, bash_handoff, bash_output
10
10
 
11
- from .compile_unity import build_unity_project
12
11
  from .projects import load_catalog
12
+ from .player_build import build_player
13
13
 
14
- INSTALLABLE_TARGETS = {"android-mobile", "magicleap", "linux64"}
15
- ADB_TARGETS = {"android-mobile", "magicleap"}
14
+ INSTALLABLE_TARGETS = {"AndroidMobile", "MagicLeap2", "Linux"}
15
+ ADB_TARGETS = {"AndroidMobile", "MagicLeap2"}
16
16
  CACHE_ROOT = Path.home() / ".unity-devkit" / "builds"
17
17
 
18
18
  app = typer.Typer(add_completion=False, pretty_exceptions_show_locals=False)
@@ -23,7 +23,7 @@ def main(
23
23
  project: Annotated[str, typer.Option("--project", "-p", help="Unity project name")],
24
24
  target: Annotated[
25
25
  str | None,
26
- typer.Option("--target", "-t", help="Device target (android-mobile, magicleap, linux64)"),
26
+ typer.Option("--target", "-t", help="Device target (AndroidMobile, MagicLeap2, Linux)"),
27
27
  ] = None,
28
28
  branch: Annotated[
29
29
  str | None,
@@ -31,23 +31,13 @@ def main(
31
31
  ] = None,
32
32
  run: Annotated[int | None, typer.Option("--run", "-r", help="Specific GitHub Actions run ID")] = None,
33
33
  serial: Annotated[str | None, typer.Option("--serial", "-s", help="adb device serial")] = None,
34
- no_grant_permissions: Annotated[
35
- bool,
36
- typer.Option(
37
- "--no-grant-permissions",
38
- help=(
39
- "Skip the post-install `adb shell pm grant` calls listed under `grant_permissions` "
40
- "for the project's entry in unity-devkit.json. Permissions are granted by default."
41
- ),
42
- ),
43
- ] = False,
44
34
  build_locally: Annotated[
45
35
  bool,
46
36
  typer.Option(
47
37
  "--build",
48
38
  "-B",
49
39
  help=(
50
- "Compile the project locally via `compile-unity` and install the produced APK / "
40
+ "Build the project locally via `build-unity` and install the produced APK / "
51
41
  "linux executable. Skips the GitHub Actions artifact lookup; --branch / --run are ignored."
52
42
  ),
53
43
  ),
@@ -81,9 +71,9 @@ def main(
81
71
  if build_locally:
82
72
  if branch or run:
83
73
  print("Warning: --branch / --run are ignored when --build is set")
84
- produced = build_unity_project(project_name, target_name)
74
+ produced = build_player(project_name, target_name)
85
75
  apks = [path for path in produced if path.suffix == ".apk"]
86
- executables = [path for path in produced if path.suffix == ".exe"]
76
+ executables = [path for path in produced if path.suffix in {".exe", ".x86_64"}]
87
77
  else:
88
78
  artifact_name = f"{project_name}-{target_name}"
89
79
 
@@ -127,7 +117,7 @@ def main(
127
117
  apks = sorted(cache_path.rglob("*.apk"))
128
118
 
129
119
  executables = []
130
- if target_name == "linux64":
120
+ if target_name == "Linux":
131
121
  executable = next(
132
122
  (
133
123
  item
@@ -137,7 +127,7 @@ def main(
137
127
  None,
138
128
  )
139
129
  if executable is None:
140
- print("No linux64 executable found in artifact (expected a file with a matching _Data/ directory)")
130
+ print("No Linux executable found in artifact (expected a file with a matching _Data/ directory)")
141
131
  raise SystemExit(1)
142
132
  executables = [executable]
143
133
 
@@ -148,19 +138,7 @@ def main(
148
138
 
149
139
  print(f"Installing: {apks[0].name}")
150
140
  adb_prefix = f"adb -s {serial}" if serial else "adb"
151
- package = project_config.package
152
- if package:
153
- bash_check(f"{adb_prefix} uninstall {package}")
154
141
  bash(f"{adb_prefix} install {apks[0]}")
155
- permissions = project_config.grant_permissions
156
- if permissions and not no_grant_permissions:
157
- if not package:
158
- raise typer.BadParameter(
159
- f"{project_name} has grant_permissions but no 'package' field in its unity-devkit.json entry"
160
- )
161
- for permission in permissions:
162
- print(f"Granting {permission} to {package}")
163
- bash(f"{adb_prefix} shell pm grant {package} {permission}")
164
142
  print("Done.")
165
143
  else:
166
144
  if not executables:
@@ -3,7 +3,7 @@ from typing import Annotated
3
3
  import typer
4
4
 
5
5
  from .projects import load_catalog
6
- from .unity import prepare_unity_project, run_unity_batchmode
6
+ from .player_build import prepare_unity_project, run_unity_batchmode
7
7
 
8
8
  app = typer.Typer(add_completion=False, pretty_exceptions_show_locals=False)
9
9