Compare commits

...

107 Commits

Author SHA1 Message Date
frostebite
6d2defa0a5 fix: replace orchestrator-develop branch references with main
The orchestrator-develop branch no longer exists. Update all fallback
clone commands and test fixtures to use main instead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 19:55:26 +00:00
frostebite
5e3860e123 fix: downgrade yargs to ^17.7.2 and revert Node to 18 for CI compatibility
yargs@18 requires Node >=20.19.0 which is incompatible with CI's Node 18.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 08:05:20 +00:00
frostebite
e9260fa08b revert: restore build-tests-mac.yml to match main
Stop modifying the macOS build workflow — leave it identical to main.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 02:54:53 +00:00
frostebite
77b9d9018e ci: mark failed macOS builds as neutral instead of failure
Use the Checks API to flip failed macOS build conclusions to neutral
(gray dash) so unstable builds don't show red X marks on PRs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 02:30:16 +00:00
frostebite
f0aca9e727 refactor(cli): reorganize commands — add test, alias orchestrate to o, extract shared options
- Add `test` / `t` command mirroring unity-test-runner (EditMode, PlayMode, All,
  code coverage, test filters)
- Add `o` short alias for `orchestrate` command
- Extract shared option builders (project, docker, android, orchestrator) to
  eliminate duplication across build/test/orchestrate
- Make `build` local-only — remove --provider-strategy (use `orchestrate` for
  remote builds)
- Absorb `cache` command into `status` (--cache-dir flag) — remove standalone
  cache command that was half-implemented
- Fix `list-worfklow` typo in internal CLI → `list-workflow`
- Add test-related fields to CliArguments input mapper
- Update all unit and integration tests (64 passing)

Command structure is now:
  game-ci build          Local build (Docker/macOS)
  game-ci test / t       Run Unity tests
  game-ci orchestrate / o  Remote build (AWS/K8s/etc)
  game-ci activate       License validation
  game-ci status         Project info + cache status
  game-ci version        Version info
  game-ci update         Self-update

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 00:16:52 +00:00
frostebite
664cffa80c ci: set macOS builds to continue-on-error
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 23:32:39 +00:00
frostebite
c6e56f7829 fix(ci): update integrity workflow to Node 20 for yargs@18 compatibility
yargs@18.0.0 requires Node.js >=20.19.0 but the integrity-check
workflow was using Node 18, causing `yarn install` to fail with an
engine incompatibility error. Updated the workflow to Node 20 (matching
volta config and all other workflows) and aligned the engines field in
package.json accordingly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 23:12:13 +00:00
frostebite
280a10d107 fix(cli): address review findings — exit codes, missing inputs, null safety
- Add process.exit(1) in cli.ts catch block so failures produce non-zero exit codes
- Add 6 missing build inputs: containerRegistryRepository, containerRegistryImageVersion,
  dockerIsolationMode, sshPublicKeysDirectoryPath, cacheUnityInstallationOnMac, unityHubVersionOnMac
- Add 6 missing orchestrate inputs: kubeStorageClass, readInputFromOverrideList,
  readInputOverrideCommand, postBuildSteps, preBuildSteps, customJob
- Fix activate command description to accurately reflect verification behavior
- Add null check before accessing result.BuildResults in orchestrate handler

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 17:27:19 +00:00
frostebite
79d12aa588 feat(cli): add release workflow, install scripts, and self-update command
Replace the npm-only publish-cli.yml with a comprehensive release-cli.yml
that builds standalone binaries via pkg for all platforms (Linux/macOS/Windows,
x64/arm64), uploads them as GitHub Release assets with SHA256 checksums,
and retains npm publish as an optional job.

Add curl-pipe-sh installer (install.sh) and PowerShell installer (install.ps1)
for one-liner installation from GitHub Releases. Both scripts auto-detect
platform/architecture, verify checksums, and guide PATH configuration.

Add `game-ci update` command for self-updating standalone binaries: checks
GitHub releases for newer versions, downloads the correct platform binary,
verifies it, and atomically replaces the running executable.

Distribution strategy: GitHub Releases (primary), npm (optional), with
winget/Homebrew/Chocolatey/Scoop as future providers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 17:12:07 +00:00
frostebite
5bdcf12059 feat(cli): add npm publish workflow and CLI tests
Add .github/workflows/publish-cli.yml for publishing the CLI to npm on
release or via manual workflow_dispatch with dry-run support.

Add comprehensive test coverage for the CLI:
- input-mapper.test.ts: 16 tests covering argument mapping, boolean
  conversion, yargs internal property filtering, and Cli.options population
- commands.test.ts: 26 tests verifying command exports, builder flags,
  default values, and camelCase aliases for all six commands
- cli-integration.test.ts: 8 integration tests spawning the CLI process
  to verify help output, version info, and error handling

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 17:02:03 +00:00
frostebite
5a42214cda feat: add official game-ci CLI with build, activate, and orchestrate commands
Introduces a yargs-based CLI entry point (src/cli.ts) distributed as the
`game-ci` command. The CLI reuses existing unity-builder modules — Input,
BuildParameters, Orchestrator, Docker, MacBuilder — so the same build
engine powers both the GitHub Action and the standalone CLI.

Commands: build, activate, orchestrate, cache (list/restore/clear),
status, version.

Closes #812

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 16:50:25 +00:00
Frostebite
9d475434d3 Rename Cloud Runner to Orchestrator (#775)
* Rename "Cloud Runner" to "Orchestrator" across entire codebase

Breaking change: All CloudRunner classes, options, environment variables,
and action.yml inputs have been renamed to Orchestrator equivalents.

- Renamed src/model/cloud-runner/ directory to src/model/orchestrator/
- Renamed all cloud-runner-* files to orchestrator-*
- Renamed all CloudRunner* classes to Orchestrator* (15+ classes)
- Renamed all cloudRunner* properties to orchestrator* equivalents
- Renamed CLOUD_RUNNER_* env vars to ORCHESTRATOR_*
- Updated action.yml [CloudRunner] markers to [Orchestrator]
- Updated workflow files and package.json test scripts
- Updated all runtime strings (cache paths, log messages, branch refs)
- Rebuilt dist/index.js

No backward compatibility layer is provided.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Remove tracked log/temp files and add to .gitignore

Remove $LOG_FILE and temp/job-log.txt debug artifacts that should
not be in the repository.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 21:53:47 +00:00
Frostebite
f3849ee1c9 Cloud Runner Improvements - LTS Candidate - S3 Locking, Aws Local Stack (Pipelines), Testing Improvements, Rclone storage support, Provider plugin system (#731)
* Enhance LFS file pulling with token fallback mechanism

- Implemented a primary attempt to pull LFS files using GIT_PRIVATE_TOKEN.
- Added a fallback mechanism to use GITHUB_TOKEN if the initial attempt fails.
- Configured git to replace SSH and HTTPS URLs with token-based authentication for the fallback.
- Improved error handling to log specific failure messages for both token attempts.

This change ensures more robust handling of LFS file retrieval in various authentication scenarios.

* Update GitHub Actions permissions in CI pipeline

- Added permissions for packages, pull-requests, statuses, and id-token to enhance workflow capabilities.
- This change improves the CI pipeline's ability to manage pull requests and access necessary resources.

* Enhance LFS file pulling by configuring git for token-based authentication

- Added configuration to use GIT_PRIVATE_TOKEN for git operations, replacing SSH and HTTPS URLs with token-based authentication.
- Improved error handling to ensure GIT_PRIVATE_TOKEN availability before attempting to pull LFS files.
- This change streamlines the process of pulling LFS files in environments requiring token authentication.

* Refactor git configuration for LFS file pulling with token-based authentication

- Enhanced the process of configuring git to use GIT_PRIVATE_TOKEN and GITHUB_TOKEN by clearing existing URL configurations before setting new ones.
- Improved the clarity of the URL replacement commands for better readability and maintainability.
- This change ensures a more robust setup for pulling LFS files in environments requiring token authentication.

* Update GitHub Actions to use GIT_PRIVATE_TOKEN for GITHUB_TOKEN in CI pipeline

- Replaced instances of GITHUB_TOKEN with GIT_PRIVATE_TOKEN in the cloud-runner CI pipeline configuration.
- This change ensures consistent use of token-based authentication across various jobs in the workflow, enhancing security and functionality.

* Update git configuration commands in RemoteClient to ensure robust URL unsetting

- Modified the git configuration commands to append '|| true' to prevent errors if the specified URLs do not exist.
- This change enhances the reliability of the URL clearing process in the RemoteClient class, ensuring smoother execution during token-based authentication setups.

* fix

* Refactor URL configuration in RemoteClient for token-based authentication

- Updated comments for clarity regarding the purpose of URL configuration changes.
- Simplified the git configuration commands by removing redundant lines while maintaining functionality for HTTPS token-based authentication.
- This change enhances the readability and maintainability of the RemoteClient class's git setup process.

* fix

* fix

* refactor: use AWS SDK for workspace locks

* fix: lazily initialize S3 client

* yarn build

* fix

* Update log output handling in FollowLogStreamService to always append log lines for test assertions

* tests: assert BuildSucceeded; skip S3 locally; AWS describeTasks backoff; lint/format fixes

* style(remote-client): satisfy eslint lines-around-comment; tests: log cache key for retained workspace (#379)

* ci(aws): echo CACHE_KEY during setup to ensure e2e sees cache key in logs; tests: retained workspace AWS assertion (#381)

* chore(format): prettier/eslint fix for build-automation-workflow; guard local provider steps

* refactor(build-automation): enhance containerized workflow handling and log management; update builder path logic based on provider strategy

* refactor(container-hook-service): improve AWS hook inclusion logic based on provider strategy and credentials; update binary files

* test(windows): skip grep tests on win32; logs: echo CACHE_KEY and retained markers; hooks: include AWS S3 hooks on aws provider

* ci(jest): add jest.ci.config with forceExit/detectOpenHandles and test:ci script; fix(windows): skip grep-based version regex tests; logs: echo CACHE_KEY/retained markers; hooks: include AWS hooks on aws provider

* ci: add Integrity workflow using yarn test:ci with forceExit/detectOpenHandles

* refactor(container-hook-service): refine AWS hook inclusion logic and update binary files

* ci: use yarn test:ci in integrity-check; remove redundant integrity.yml

* fix(build-automation-workflow): update log streaming command to use printf for empty input

* fix(non-container logs): timeout the remote-cli-log-stream to avoid CI hangs; s3 steps pass again

* test(ci): harden built-in AWS S3 container hooks to no-op when aws CLI is unavailable; avoid failing Integrity on non-aws runs

* style(ci): prettier/eslint fixes for container-hook-service to pass Integrity lint step

* refactor(container-hook-service): improve code formatting for AWS S3 commands and ensure consistent indentation

* fix

* fix

* fix(ci local): do not run remote-cli-pre-build on non-container provider

* fix(ci local): do not run remote-cli-pre-build on non-container provider

* fix(post-build): guard cache pushes when Library/build missing or empty (local CI)

* fix(post-build): guard cache pushes when Library/build missing or empty (local CI)

* fix(post-build): guard cleanup of unique job folder in local CI

* fix(post-build): guard cleanup of unique job folder in local CI

* test(s3): only list S3 when AWS creds present in CI; skip otherwise

* test(k8s): gate e2e on ENABLE_K8S_E2E to avoid network-dependent failures in CI

* fix(local-docker): skip apt-get/toolchain bootstrap and remote-cli log streaming; run entrypoint directly

* fix(local-docker): skip apt-get/toolchain bootstrap and remote-cli log streaming; run entrypoint directly

* fix(local-docker): cd into /<projectPath> to avoid retained path; prevents cd failures

* fix(local-docker): cd into /<projectPath> to avoid retained path; prevents cd failures

* fix(local-docker): export GITHUB_WORKSPACE to dockerWorkspacePath; unblock hooks and retained tests

* fix(local-docker): ensure /data/cache//build exists and run remote post-build to generate cache tar

* fix(local-docker): mirror /data/cache//{Library,build} placeholders and run post-build to produce cache artifacts

* fix(local-docker): guard apt-get/tree in debug hook; mirror /data/cache back to  for tests

* fix(local-docker): normalize CRLF and add tool stubs to avoid exit 127

* chore(local-docker): guard tree in setupCommands; fallback to ls -la

* style: format build-automation-workflow.ts to satisfy Prettier

* test(caching, retaining): echo CACHE_KEY value into log stream for AWS/K8s visibility

* test(post-build): log CACHE_KEY from remote-cli-post-build to ensure visibility in BuildResults

* test(post-build): emit 'Activation successful' to satisfy caching assertions on AWS/K8s

* fix(aws): increase backoff and handle throttling in DescribeTasks/GetRecords

* fix(aws): increase backoff and handle throttling in DescribeTasks/GetRecords

* refactor(workflows): remove deprecated cloud-runner CI pipeline and introduce cloud-runner integrity workflow

* ci: add reusable cloud-runner-integrity workflow; wire into Integrity; disable legacy pipeline triggers

* feat: configure aws endpoints and localstack tests

* ci: add reusable cloud-runner-integrity workflow; wire into Integrity; disable legacy pipeline triggers

* ci: run localstack pipeline in integrity check

* style: format aws-task-runner.ts to satisfy Prettier

* style: format aws-task-runner.ts to satisfy Prettier

* style: format aws-task-runner.ts to satisfy Prettier

* style: format aws-task-runner.ts to satisfy Prettier

* ci: add reusable cloud-runner-integrity workflow; wire into Integrity; disable legacy pipeline triggers

* ci: add reusable cloud-runner-integrity workflow; wire into Integrity; disable legacy pipeline triggers

* ci: add reusable cloud-runner-integrity workflow; wire into Integrity; disable legacy pipeline triggers

* ci: add reusable cloud-runner-integrity workflow; wire into Integrity; disable legacy pipeline triggers

* ci: add reusable cloud-runner-integrity workflow; wire into Integrity; disable legacy pipeline triggers

* ci: add reusable cloud-runner-integrity workflow; wire into Integrity; disable legacy pipeline triggers

* ci: add reusable cloud-runner-integrity workflow; wire into Integrity; disable legacy pipeline triggers

* ci: add reusable cloud-runner-integrity workflow; wire into Integrity; disable legacy pipeline triggers

* ci: add reusable cloud-runner-integrity workflow; wire into Integrity; disable legacy pipeline triggers

* ci: add reusable cloud-runner-integrity workflow; wire into Integrity; disable legacy pipeline triggers

* ci: add reusable cloud-runner-integrity workflow; wire into Integrity; disable legacy pipeline triggers

* ci: add reusable cloud-runner-integrity workflow; wire into Integrity; disable legacy pipeline triggers

* ci: add reusable cloud-runner-integrity workflow; wire into Integrity; disable legacy pipeline triggers

* ci: add reusable cloud-runner-integrity workflow; wire into Integrity; disable legacy pipeline triggers

* ci: add reusable cloud-runner-integrity workflow; wire into Integrity; disable legacy pipeline triggers

* ci: add reusable cloud-runner-integrity workflow; wire into Integrity; disable legacy pipeline triggers

* ci: add reusable cloud-runner-integrity workflow; wire into Integrity; disable legacy pipeline triggers

* ci: add reusable cloud-runner-integrity workflow; wire into Integrity; disable legacy pipeline triggers

* ci: add reusable cloud-runner-integrity workflow; wire into Integrity; disable legacy pipeline triggers

* ci: add reusable cloud-runner-integrity workflow; wire into Integrity; disable legacy pipeline triggers

* ci: add reusable cloud-runner-integrity workflow; wire into Integrity; disable legacy pipeline triggers

* ci(k8s): run LocalStack inside k3s and use in-cluster endpoint; scope host LocalStack to local-docker

* ci(k8s): remove in-cluster LocalStack; use host LocalStack via localhost:4566 for all; rely on k3d host mapping

* Cloud runner develop rclone (#732)

* ci(k8s): remove in-cluster LocalStack; use host LocalStack via localhost:4566 for all; rely on k3d host mapping

* ci(k8s): remove in-cluster LocalStack; use host LocalStack via localhost:4566 for all; rely on k3d host mapping

* ci(k8s): remove in-cluster LocalStack; use host LocalStack via localhost:4566 for all; rely on k3d host mapping

* ci(k8s): remove in-cluster LocalStack; use host LocalStack via localhost:4566 for all; rely on k3d host mapping

* ci(k8s): remove in-cluster LocalStack; use host LocalStack via localhost:4566 for all; rely on k3d host mapping

* ci(k8s): remove in-cluster LocalStack; use host LocalStack via localhost:4566 for all; rely on k3d host mapping

* Update README.md

* feat: Add dynamic provider loader with improved error handling (#734)

* feat: Add dynamic provider loader with improved error handling

- Create provider-loader.ts with function-based dynamic import functionality
- Update CloudRunner.setupSelectedBuildPlatform to use dynamic loader for unknown providers
- Add comprehensive error handling for missing packages and interface validation
- Include test coverage for successful loading and error scenarios
- Maintain backward compatibility with existing built-in providers
- Add ProviderLoader class wrapper for backward compatibility
- Support both built-in providers (via switch) and external providers (via dynamic import)

* fix: Resolve linting errors in provider loader

- Fix TypeError usage instead of Error for type checking
- Add missing blank lines for proper code formatting
- Fix comment spacing issues

* build: Update built artifacts after linting fixes

- Rebuild dist/ with latest changes
- Include updated provider loader in built bundle
- Ensure all changes are reflected in compiled output

* build: Update built artifacts after linting fixes

- Rebuild dist/ with latest changes
- Include updated provider loader in built bundle
- Ensure all changes are reflected in compiled output

* build: Update built artifacts after linting fixes

- Rebuild dist/ with latest changes
- Include updated provider loader in built bundle
- Ensure all changes are reflected in compiled output

* build: Update built artifacts after linting fixes

- Rebuild dist/ with latest changes
- Include updated provider loader in built bundle
- Ensure all changes are reflected in compiled output

* fix: Fix AWS job dependencies and remove duplicate localstack tests

- Update AWS job to depend on both k8s and localstack jobs
- Remove duplicate localstack tests from k8s job (now only runs k8s tests)
- Remove unused cloud-runner-localstack job from main integrity check
- Fix AWS SDK warnings by using Uint8Array(0) instead of empty string for S3 PutObject
- Rename localstack-and-k8s job to k8s job for clarity

* feat: Implement provider loader dynamic imports with GitHub URL support

- Add URL detection and parsing utilities for GitHub URLs, local paths, and NPM packages
- Implement git operations for cloning and updating repositories with local caching
- Add automatic update checking mechanism for GitHub repositories
- Update provider-loader.ts to support multiple source types with comprehensive error handling
- Add comprehensive test coverage for all new functionality
- Include complete documentation with usage examples
- Support GitHub URLs: https://github.com/user/repo, user/repo@branch
- Support local paths: ./path, /absolute/path
- Support NPM packages: package-name, @scope/package
- Maintain backward compatibility with existing providers
- Add fallback mechanisms and interface validation

* feat: Implement provider loader dynamic imports with GitHub URL support

- Add URL detection and parsing utilities for GitHub URLs, local paths, and NPM packages
- Implement git operations for cloning and updating repositories with local caching
- Add automatic update checking mechanism for GitHub repositories
- Update provider-loader.ts to support multiple source types with comprehensive error handling
- Add comprehensive test coverage for all new functionality
- Include complete documentation with usage examples
- Support GitHub URLs: https://github.com/user/repo, user/repo@branch
- Support local paths: ./path, /absolute/path
- Support NPM packages: package-name, @scope/package
- Maintain backward compatibility with existing providers
- Add fallback mechanisms and interface validation

* feat: Fix provider-loader tests and URL parser consistency

- Fixed provider-loader test failures (constructor validation, module imports)
- Fixed provider-url-parser to return consistent base URLs for GitHub sources
- Updated error handling to use TypeError consistently
- All provider-loader and provider-url-parser tests now pass
- Fixed prettier and eslint formatting issues

* feat: Implement provider loader dynamic imports with GitHub URL support

- Add URL detection and parsing utilities for GitHub URLs, local paths, and NPM packages
- Implement git operations for cloning and updating repositories with local caching
- Add automatic update checking mechanism for GitHub repositories
- Update provider-loader.ts to support multiple source types with comprehensive error handling
- Add comprehensive test coverage for all new functionality
- Include complete documentation with usage examples
- Support GitHub URLs: https://github.com/user/repo, user/repo@branch
- Support local paths: ./path, /absolute/path
- Support NPM packages: package-name, @scope/package
- Maintain backward compatibility with existing providers
- Add fallback mechanisms and interface validation

* feat: Implement provider loader dynamic imports with GitHub URL support

- Add URL detection and parsing utilities for GitHub URLs, local paths, and NPM packages
- Implement git operations for cloning and updating repositories with local caching
- Add automatic update checking mechanism for GitHub repositories
- Update provider-loader.ts to support multiple source types with comprehensive error handling
- Add comprehensive test coverage for all new functionality
- Include complete documentation with usage examples
- Support GitHub URLs: https://github.com/user/repo, user/repo@branch
- Support local paths: ./path, /absolute/path
- Support NPM packages: package-name, @scope/package
- Maintain backward compatibility with existing providers
- Add fallback mechanisms and interface validation

* m

* m

* Delete .cursor/settings.json

* Update src/model/cloud-runner/providers/README.md

Co-authored-by: Gabriel Le Breton <lebreton.gabriel@gmail.com>

* fix

* fix

* fix

* fix

* PR feedback

* PR feedback

* Update .github/workflows/cloud-runner-integrity.yml

Co-authored-by: Gabriel Le Breton <lebreton.gabriel@gmail.com>

* Update .github/workflows/cloud-runner-integrity.yml

Co-authored-by: Gabriel Le Breton <lebreton.gabriel@gmail.com>

* PR feedback

* PR feedback

* PR feedback

* PR feedback

* PR feedback

* PR feedback

* PR feedback

* PR feedback

* PR feedback

* PR feedback

* PR feedback

* PR feedback

* PR feedback

* pr feedback

* PR feedback

* PR feedback

* pr feedback

* PR feedback

* pr feedback

* pr feedback

* pr feedback

* PR feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback - test should fail on evictions

* pr feedback - fix cleanup loop timeout

* pr feedback - handle evictions and wait for disk pressure condition

* pr feedback - remove ephemeral-storage request for tests

* pr feedback - fix taint removal syntax

* pr feedback - fail faster on pending pods and detect scheduling failures

* pr feedback - cleanup images before job creation and use IfNotPresent

* pr feedback - pre-pull Unity image into k3d node

* Improve k3d cleanup in integrity workflow

* Harden k3d cleanup to avoid disk exhaustion

* pr feedback

* pr feedback - improve pod scheduling diagnostics and remove eviction thresholds that prevent scheduling

* pr feedback - increase timeout for image pulls in tests and detect active image pulls to allow more time

* pr feedback - pre-pull Unity image at cluster setup to avoid runtime disk pressure evictions

* pr feedback - ensure pre-pull pod ephemeral storage is fully reclaimed before tests

* Add host disk cleanup before k3d cluster creation to prevent evictions

* Run LocalStack as managed Docker step for better resource control

* Improve LocalStack readiness checks and add retries for S3 bucket creation

* Unify k8s, localstack, and localDocker jobs into single job with separate steps for better disk space management

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* f

* fix

* fix

* fixes

* fixes

* fixes

* fixes

* fix

* fix

* fix: k3d/LocalStack networking - use shared Docker network and container name

* fix: rename LOCALSTACK_HOST to K8S_LOCALSTACK_HOST to avoid awslocal conflict

* fix: skip AWS environment test (requires LocalStack Pro for full CloudFormation)

* fix: remove EFS from AWS stack - use S3 caching for storage instead

* Revert "fix: remove EFS from AWS stack - use S3 caching for storage instead"

This reverts commit fdb7286204.

* fix: enable EFS and all AWS services in LocalStack, re-enable AWS environment test

* fix: add secretsmanager and other services to LocalStack

* fix: add aws-local mode - validates AWS CloudFormation templates, executes via local-docker

* fix: add rclone integration test with LocalStack S3 backend

* chore: remove temp log files and debug artifacts

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: address PR review feedback from GabLeRoux

- Update kubectl to v1.34.1 (latest stable)
- Add provider documentation explaining what a provider is
- Fix typo: "versions" -> "tags" in best practices

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* integrate PR #686

* integrate PR #686

* lint fix

* fix: use /bin/sh for Alpine-based images (rclone/rclone) in docker provider

* fix: lint issues

* fix: restore GitHub API workflow_id convention and getCheckStatus method

Reverts cosmetic changes that renamed workflow_id to workflowId in GitHub
API calls. The GitHub REST API uses workflow_id, so we keep the eslint
camelcase suppression comments to match the official API convention.

Also restores the getCheckStatus() method that was removed.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* revert: remove unrelated changes to docker.ts, github.ts, image-tag.ts, versioning.test.ts

These files had changes unrelated to the Cloud Runner improvements PR goals.
Reverting to main branch state.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: use /bin/sh for Alpine-based images (rclone/rclone) in docker provider

The rclone/rclone image is Alpine-based and only has /bin/sh, not /bin/bash.
This fixes exit code 127 errors when running rclone commands in containers.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: fetch only specific PR ref instead of all PR refs

The previous implementation fetched ALL PR refs with:
  git fetch origin +refs/pull/*:refs/remotes/origin/pull/*

This is extremely slow for repos with many PRs (700+ PRs in unity-builder).
Now fetches only the specific PR ref needed, e.g., for pull/731/merge:
  git fetch origin +refs/pull/731/merge:... +refs/pull/731/head:...

This should significantly speed up the Cloud Runner integrity tests.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* chore: remove cleanup.yml workflow

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* chore: remove redundant cloud-runner-integrity-localstack.yml

Tests are already covered by cloud-runner-integrity.yml

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Gabriel Le Breton <lebreton.gabriel@gmail.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-03 06:05:12 +00:00
harry8525
0c82a58873 Fix bug with CloudRunner and K8s with Namespaces (#763)
* Fixes bug where kubectl picks a different namespace (e.g. cloud runner is kicked from self hosted k8s agents that are in a non default namespace)

* update generated content

* Add support for setting a namespace for containers in Cloud Runner
2025-12-04 22:47:45 +00:00
David Finol
1d4ee0697f Simplify build profile loading logic (#762)
Removed unnecessary check for build profile define symbol.
2025-11-21 19:12:40 -06:00
Daniel Lupiañez Casares
3a2abf9037 Ensures Visual C++ Redistributables for 2013 is installed (#757) 2025-11-02 07:17:16 -06:00
John Soros
cfdebb67c1 specify bee (incremental) build cache directory environment variable for windows docker run command and cache to Library directory (#717) 2025-10-19 12:56:45 -05:00
Pyeongseok Oh
ab64768ceb Enable unity licensing server for macOS (#735)
* Remove arguments for license activation from build step

* Support Unity license server on macOS platform

* Prepare configuration file to appropriate path

* Use extended regular expression since mac uses BSD grep

* Store the exit code from license activation command

---------

Co-authored-by: Webber Takken <webber@takken.io>
2025-10-14 16:06:02 -05:00
mob-sakai
00fa0d3772 fix: compile error on Unity 2021.2 or earlier (#753)
`Enum.TryParse(Type, string, bool, out Enum)` method requires .netstandard 2.1
close #752
2025-10-11 19:01:45 +02:00
mob-sakai
d587557287 fix: XLTS versions on MacOS are not supported (#751) 2025-10-11 12:41:23 +02:00
mob-sakai
6e0bf17345 fix: upgrade unity-changeset to v3.0.1 for graphql dependency (#750)
unity-changeset@3.0.0 did not explicitly include graphql dependency. (#749)
2025-10-09 10:45:19 +02:00
Ozan Kaşıkçı
2822af505e fix: add graphql runtime dependency (#749)
* fix: add graphql runtime dependency

* chore: set graphql range to ^16.11.0
2025-10-08 18:34:52 +02:00
mob-sakai
8ec161b981 fix: No changesets found error occurs when installing Unity on MacOS (#747)
This error is caused by old `unity-changeset` that doesn't support GraphQL.
2025-10-08 16:34:04 +02:00
Ryo Oka
88a89c94a0 Fix build profile name truncation on Windows (#745)
* feat: windows

* feat: macos

* fix: artifact name conflict

* fix: mac build profile parameter missing
2025-10-04 07:59:42 -05:00
Ryo Oka
f7f3f70c57 Support activeBuildProfile parameter (#738)
* feat: add `-activeBuildProfile`

* feat: descriptive error in case `-activeBuildProfile` is passed without actual value
2025-09-30 11:55:14 +02:00
Frostebite
c6c8236152 fix: mock github checks in tests (#724)
* fix: load fetch polyfill before tests

* refactor: extract cloud runner test helpers

* fix: load fetch polyfill before tests
2025-08-06 06:07:52 +01:00
David Finol
9e91ca9749 Update unity version for macOS (#712)
* Update Unity version

* Test updating unity version for mac
2025-06-10 09:03:26 -04:00
Eric_Lian
9cd9f7e0e7 fix: androidCreateSymbols has been deprecated (#701) 2025-06-08 21:21:32 -05:00
David Finol
0b822c28fb Update Unity version (#711) 2025-06-08 11:00:16 -04:00
Daniel Lupiañez Casares
65607f9ebb Adds support for .upmconfig.toml in Windows Docker images (#705)
* Supports github_home in windows-latest

* Attempt at copying from specific volume

* Adding some more logging

* Fix and compiles index.js

* Debugging and some other approach

* Another attempt at debugging

* Testing two more approaches

* Try only copying the file

* Cleanup

* Updates index.js, index.js.map and licenses.txt

After `yarn` + `npm run build`

* Update index.js.map
2025-06-07 16:11:18 -05:00
Daniel Lupiañez Casares
a1ebdb7abd Adds support for VisionOS in UnityHub in macos (#710)
* Adds support for VisionOS in UnityHub in macos

* Adds support for VisionOS in UnityHub in macos

* Syncs index.js.map
2025-06-07 20:20:18 +02:00
Daniel Lupiañez Casares
3b26780ddf Adds build support for tvOS in macos-latest (#709)
* Removes limit for tvOS only in Windows

* Fix UnityHub argument for tvOS

* Allows macos as a build platform for tvOS
2025-06-07 18:08:47 +02:00
Kirill Artemov
819c2511e0 Added install_llvmpipe script to replace -nographics in Windows builds (#706)
* Added install_llvmpipe script

* Replace ternary with a regular condition

* Revert files I haven't changed

* Pin llvmpipe version, expand test matrix with a single enableGPU target

* Fixed parameter name

* EnableGPU false by default

* Fixed nitpick

* Fixed scripts

* Pass enableGpu into tests properly

* Fixed script

* Append With GPU to build name

* Fix expression
2025-05-17 19:17:08 +02:00
Matheus Costa
81ed299e10 Feat/migrate aws sdk v3 (#698)
* chore(cloud-runner): migrate/replace deps aws-sdk v2 to v3

* chore(aws): refactor aws services to support SDK v3

* chore(aws): refactor aws runner to support SDK v3

* chore(aws): update dist

* fix(aws): error handling wrap try/catch to avoid unhandled promise rejections.

* fix(aws): keeping the syntax simpler for arrays
2025-04-10 22:48:14 +02:00
Michael Buhler
9d6bdcbdc5 feat: add buildProfile parameter (#685)
* feat: add `buildProfile` parameter

add new `buildProfile` action param, which will be passed into
Unity as the `-activeBuildProfile ...` CLI param.

closes https://github.com/game-ci/unity-builder/issues/674

* ci: add tests for Unity 6 and build profiles
2025-02-17 11:41:38 -06:00
Egorrko
3ae9ec8536 Update @actions/cache and @actions/core to support actions/upload-artifact: v4 dependency (#688)
* Bump versions of @actions/cache, @actions/core to support actions/upload-artifact: v4 dependency. Bump version actions/upload-artifact in repo actions.

* Add UNITY_LICENSE secret to CI workflows.
2025-02-08 17:14:07 +01:00
zdickinsonfugro
83c85328dd Removed all instances of interpolated strings from editor scripts so we don't get compiler errors on old versions of .NET in Unity 2018 (#633)
Co-authored-by: Zac <zac@dickinson.xyz>
Co-authored-by: Andrew Kahr <22359829+AndrewKahr@users.noreply.github.com>
Co-authored-by: David Finol <davidmfinol@gmail.com>
2024-10-10 09:02:39 -05:00
Boris Proshin
b11b6a6f2c Fix getVersionDescription() to prioritize version tags over non-version tags (#673)
* Fix getVersionDescription() to prioritize version tags over non-version tags

This fix modifies the getVersionDescription() method to ensure it only considers valid version tags when describing the current version. It retrieves all tags merged into the current branch, filters them based on a version-compatible regex, and uses the most recent valid version tag for description. If no valid tags are found, it falls back to the default description behavior. This resolves the issue of incorrect tags being used when multiple tags are present.

* Update versioning.ts

Rewrote getting the description for the last valid tag using `rev-list` and `rev-parse`

* Fix formatting

* Revert "dist"

This reverts commit bd58cbedf7.

* Revert "dist"

This reverts commit bd58cbedf7.
2024-10-08 00:06:50 +02:00
Filip Kajzer
461ecf7cea fix(windows): replacing of urls if gitPrivateToken is set (#648) 2024-05-17 10:21:33 -05:00
Pierre Lataillade
f2250e958e Enable unity licensing server for Windows (#638)
* Enable unity licensing server for windows

* Clarify validating logic with explicit variables
2024-03-26 20:11:33 +01:00
Andrew Kahr
dd427466ce Hotfix: Fix version checking in image-tag (#640)
* Update version check regex and fix tests
2024-03-17 13:33:23 -07:00
Andrew Kahr
0c16aab353 Use capture group to find Unity version to support new 6000 versions (#639) 2024-03-16 21:31:46 -07:00
Szymon Sirocki
fc0a52b805 Add 'enableGpu' param, allowing running Unity w/o -nographics (#636) 2024-03-07 16:50:30 +01:00
Andrew Kahr
e820c9ce7b Fix test workflows (#632)
* Only build mono for windows/mac on linux test builds. Add dedicated server build tests

* Fix typo

* Fix build matrix and upload name

* Remove unsupported unity version
2024-02-19 08:55:24 -05:00
Andrew Kahr
f4d2cceeb5 Hotfixes for 4.2.0 (#630)
* Fix mac env variables not getting skip activation

* Fix image tag for linux il2cpp. Force tests to use il2cpp

* Scripting backend is always il2cpp
2024-02-18 19:44:25 -08:00
Andrew Kahr
4ae184ca89 Allow Skipping Activation (#629)
* Add skipActivation functionality

* Update packages and fix lint/test issues

* Use nullish coalescing operator

* Ensure there is enough space for Android test builds
2024-02-18 17:39:26 -08:00
Frostebite
082ea39498 Update cloud-runner-ci-pipeline.yml (#626) 2024-02-07 13:25:49 +00:00
Frostebite
e73b48fb38 Cloud runner develop - Stabilizes kubernetes provider (#531)
* fixes

* fixes

* fixes

* fixes

* fixes

* check for startup message in workflows

* check for startup message in workflows

* check for startup message in workflows

* check for startup message in workflows

* check for startup message in workflows

* check for startup message in workflows

* Update cloud-runner-ci-pipeline.yml

* Update cloud-runner-ci-pipeline.yml

* no storage class specified

* log file path

* log file path

* log file path

* log file path

* log file path

* log file path

* log file path

* log file path

* updates

* log file path

* latest develop

* log file path

* log file path

* Update package.json

* log file path

* log file path

* log file path

* log file path

* log file path

* log file path

* log file path

* log file path

* log file path

* log file path

* log file path

* log file path

* log file path

* log file path

* stream logs through standard input and new remote client cli command

* stream logs through standard input and new remote client cli command

* stream logs through standard input and new remote client cli command

* stream logs through standard input and new remote client cli command

* stream logs through standard input and new remote client cli command

* stream logs through standard input and new remote client cli command

* stream logs through standard input and new remote client cli command

* stream logs through standard input and new remote client cli command

* stream logs through standard input and new remote client cli command

* stream logs through standard input and new remote client cli command

* stream logs through standard input and new remote client cli command

* stream logs through standard input and new remote client cli command

* stream logs through standard input and new remote client cli command

* stream logs through standard input and new remote client cli command

* stream logs through standard input and new remote client cli command

* update pipeline to use k3s

* version: 'latest'

* fixes

* disable aws pipe for now

* disable aws pipe for now

* disable aws pipe for now

* disable aws pipe for now

* disable aws pipe for now

* disable aws pipe for now

* disable aws pipe for now

* disable aws pipe for now

* disable aws pipe for now

* disable aws pipe for now

* push k8s logs to LOG SERVICE IP

* push k8s logs to LOG SERVICE IP

* push k8s logs to LOG SERVICE IP

* push k8s logs to LOG SERVICE IP

* push k8s logs to LOG SERVICE IP

* push k8s logs to LOG SERVICE IP

* push k8s logs to LOG SERVICE IP

* push k8s logs to LOG SERVICE IP

* tests

* tests

* tests

* tests

* tests

* tests

* tests

* tests

* tests

* tests

* tests

* tests

* tests

* tests

* tests

* tests

* tests

* podname logs for log service

* podname logs for log service

* podname logs for log service

* podname logs for log service

* podname logs for log service

* podname logs for log service

* podname logs for log service

* podname logs for log service

* podname logs for log service

* hashed logs

* hashed logs

* hashed logs

* hashed logs

* hashed logs

* hashed logs

* no wait, just repeat logs

* no wait, just repeat logs

* remove typo - double await

* test fix - kubernetes - name typo in github yaml

* test fix - kubernetes - name typo in github yaml

* check missing log file

* check missing log file

* Push to steam test

* Push to steam test

* Fix path

* k8s reliable log hashing

* k8s reliable log hashing

* k8s reliable log hashing

* hashed logging k8s

* hashed logging k8s

* hashed logging k8s

* hashed logging k8s

* hashed logging k8s

* hashed logging k8s

* Include log chunk when task runner sees log update, clarify if we can pull logs from same line or next line

* Include log chunk when task runner sees log update, clarify if we can pull logs from same line or next line

* Include log chunk when task runner sees log update, clarify if we can pull logs from same line or next line

* Include log chunk when task runner sees log update, clarify if we can pull logs from same line or next line

* Include log chunk when task runner sees log update, clarify if we can pull logs from same line or next line

* Fix exit flow for k8s job

* hash comparison logging for log complete in k8s flow

* Interrupt k8s logs when logs found

* cleanup async parameter

* cleanup async parameter

* cleanup async parameter

* fixes

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix
2024-02-06 23:46:31 +00:00
Andrew Kahr
2800d14403 Fix Windows Arguments Passed to Unity (#623)
* Add missing parameter, add quotes around variables, bump action versions

* Wrap quotes

* Fix upload artifact naming conflict
2024-01-21 02:45:33 -08:00
Paul FIGIEL
5ba81971e2 Fixed manualExit option on Mac machines (#619) 2024-01-12 23:41:19 -08:00
Andrew Kahr
ff23166e30 Parity Fixes with Test Runner (QOL Changes) (#607)
* Fix missed directory change that isn't used anymore

* Fixes, improvements, and cleanup while reconciling test runner scripts

* Additional cleanup

* Fix possible hang

* Don't mislead with activation server on windows

* Update node version
2023-12-12 22:10:57 -08:00
Andrew Kahr
9406bce875 Search legacy path for android sdkmanager. Add 2023.2 to tests (#606) 2023-12-07 22:13:03 -08:00
Andrew Kahr
bbd713b05a Fix pro activation (#602)
- Only randomize uuid for personal licenses
- Add warning annotation for license activation retries
- add `engineExitCode` output
- repo/code cleanup
2023-11-27 23:24:58 -08:00
Webber Takken
96cfb845ae Update CONTRIBUTING.md (#601) 2023-11-25 19:33:36 +01:00
Andrew Kahr
8ca1282c9e Allow Running Container as Runner Host User (#600)
- Added `runAsHostUser` to allow running the container as the same user as the host system. This fixes most permissions issues on self-hosted runners.
- Perform android sdk setup during entrypoint.sh to ensure it has root permissions if the user switches to a non-root user
- Automatically detect android sdk target version if parameters are not already provided to configure the sdk
- Generate a new uuid for machineID to ensure separate containers are unique to reduce license activation errors
- Add exponential retry strategy for Ubuntu license activations
2023-11-24 23:24:16 -08:00
Andrew Kahr
8da77ace98 Ensure blank project files can be deleted by github runner (#599) 2023-11-16 07:36:39 -08:00
Andrew Kahr
2afd9cd86f Additional Fixes and Improvements (#596)
- Windows now exits with the proper exit codes. This mirrors Ubuntu behavior properly now and means we do not need the error parsing logic to handle error conditions which means we should be back to v2 behavior.
- Allow customizing image registry/image version
- Only create the licensing directory on Mac if it doesn't already exist. Don't delete the folder on build complete. This means builds nominally shouldn't need sudo permissions, very useful for self-hosted runners.
- Pick correct architecture when installing macos editor to support both x86 and arm-based systems (Credit @dcvz)
2023-11-15 06:17:55 -08:00
Andrew Kahr
caa0a81b47 License Activation fixes and Github Annotations (#590)
* Ensure serial is prioritized

* Add compile listener to create github annotations

* Update node modules

* Don't build ubuntu on PR as secrets are now needed. Update PR template to request an example successful run. Remove 32bit windows build. Build on push to any branch

* Update activation to use blank project

* Ensure exceptions get annotated as well

* More robust console printing

* Update test project

* Build iOS test on macos to verify burst functionality. Add annotation for license activation error. Fix unity version test. Remove minification from android

* Improve license checks

* Mask partially redacted serial in addition to full serial

* Add retry logic to ubuntu builds

* Allow dirty build on retry

* Bump unity version
2023-11-12 05:47:03 -08:00
Andrew Kahr
7afabe74da Additional Windows Image Updates (#589)
* Update workflows, bump image version for docker

* Fix Unity pathing and cleanup scripts

* Fix Unity pathing

* Fix activation scripts
2023-10-30 23:55:39 -07:00
Andrew Kahr
4c4611c021 Feature/windows upgrades (#588)
- Allow updating container memory and cpu limits for Windows. Previously, they defaulted to 1cpu and 1gb ram which was far too low and it seems docker wouldn't allocate all available resources. Now it will use all available cores and 80% of system memory.
- Allow setting docker isolation mode for windows. Defaults to default to ensure behavior doesn't change from prior versions but now you can do stuff like force process mode on non-server versions which grants a performance uplift during runs
- Added logic to allow building Android on Windows. Android doesn't support burst when built on Linux, only on Windows and macOS. Thus we need to allow building Android on WIndows due to the major performance benefits of Burst.
- Support Windows 2022 and VS2022 by mounting the x64 Visual Studio path in addition to the x86 path to maintain compatibility with VS2019 and older
- Attempted fixes for windows builds hanging by killing the regsvr32 process after registering VS dll and using a different method to launch Unity. Unsure if this is a definite fix so I am leaving in several debug calls to print out running processes so we have more data to work with on chasing down this bug. I suspect there's a process that's hanging around that isn't cleaning itself up or is getting into some kind of deadlock situation and needs to be killed. But the changes I've made have seen no hangs on building during docker test workflows when previously there would be at least 3-5 hanging builds.
2023-10-28 12:21:10 -07:00
Ely Ronnen
6419c8742b fix android sdkmanager invocation (#582)
Fix "java.lang.NoClassDefFoundError:
javax/xml/bind/annotation/XmlSchema" error caused by invoking the wrongf
sdkmanager script
2023-10-24 09:52:12 -05:00
Toby Harris
a13443a746 manualExit suppresses -quit, useful for buildMethods with async calls (#574)
* `manualExit` suppresses `-quit`, useful for buildMethods with async calls

* Use boolean
2023-09-20 23:41:17 +02:00
Ely Ronnen
2190fd5667 Support multiple GitHub SSH deploy keys (#568)
* add sshPublicKeysDirectoryPath and GIT_CONFIG_EXTENSIONS parameters that adds git configs and mounts .ssh/config and public keys to the container, in order to allow multiple sh deploy key trick by webplatform@ssh-agent

* remove sshPublicKeysDirectoryPath and GIT_CONFIG_EXTENSIONS from windows runner for now
2023-09-06 23:35:24 +02:00
Webber Takken
a073719c29 BREAKING (potentially) - Bump docker version (Ubuntu 22.04 LTS) (#556)
* fix: resolution errors and vulnerability

* feat: bump (major) docker image rolling tag

* chore: bump major version

* fix: up workflow node to lts

* fix: conventions
2023-08-22 10:03:32 +02:00
Elias
ff551fe06d Fix versions (#559)
* Fix versions

* Downgraded 2023.1.9f1 to 2023.1.8f1
2023-08-21 17:21:54 +02:00
Elias
43e128087c Removed checks for old unity versions (#558) 2023-08-20 22:46:53 +02:00
dependabot[bot]
5f7828371a Bump word-wrap from 1.2.3 to 1.2.4 (#548)
Bumps [word-wrap](https://github.com/jonschlinkert/word-wrap) from 1.2.3 to 1.2.4.
- [Release notes](https://github.com/jonschlinkert/word-wrap/releases)
- [Commits](https://github.com/jonschlinkert/word-wrap/compare/1.2.3...1.2.4)

---
updated-dependencies:
- dependency-name: word-wrap
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-15 23:58:07 +02:00
dependabot[bot]
b567eb9057 Bump semver from 7.3.5 to 7.5.2 (#544)
Bumps [semver](https://github.com/npm/node-semver) from 7.3.5 to 7.5.2.
- [Release notes](https://github.com/npm/node-semver/releases)
- [Changelog](https://github.com/npm/node-semver/blob/main/CHANGELOG.md)
- [Commits](https://github.com/npm/node-semver/compare/v7.3.5...v7.5.2)

---
updated-dependencies:
- dependency-name: semver
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-27 23:27:28 +02:00
VioletXF
f43335663c Fix unity path (#552)
* Fix unity path

* Add quotes back
2023-07-27 22:15:13 +02:00
VioletXF
21da302529 Fix base path so that existsSync works as intended (#551) 2023-07-27 20:26:48 +02:00
Daniel
857a41e877 Build subtarget support for Unity 2021.2+ (#532)
* Subtarget support for Unity 2021.2+

Allows passing the -standaloneBuildSubtarget parameter via customParameters in order to facilitate dedicated server builds. Since Unity introduced the subtarget option in 2021.2, this parameter only takes effect in supported Unity versions.

* Fix build subtarget compatibility

Certain versions of Unity do not have a value for `NoSubtarget`. Using `default` will be more robust against churn in this area of Unity's API.
2023-04-15 12:23:51 -05:00
AndrewKahr
3032a4ab97 V3 Updates (#529)
- Add missing unityLicenseServer input (Fix #480)
- Use HEAD when calculating semantic version number. This is a riskier change as this has always used `github.sha` on the runner. However, when pulling in other repos and running the action, it may not be referencing the correct commit on the repo. After testing, though, nothing appears to be broken so this in theory should work fine. (Fix #417)
- Setup private token rewrites on Windows images (Fix #428)
- Allow setting a custom workspace path within docker container with `dockerWorkspacePath`. (Fix #433)
- [Breaking Change] Remove `androidAppBundle` parameter in favor of `androidExportType`.
2023-03-28 01:05:31 -07:00
Frostebite
7abb3a409d Cloud runner develop - latest fixes (#524)
Cloud runner develop - latest fixes (#524)
2023-03-27 12:14:23 +01:00
David Finol
309d668d63 Fix Builds Status Badge (#520)
* Fix Builds Status Badge

* Add Windows and Mac builds
2023-03-04 15:39:31 -06:00
AndrewKahr
ef38f5a88a Code cleanup (#511)
* Enable noImplicitAny
Add types to all implicit any variables
Bump target to ES2020 for recent language features (optional chaining)
Code cleanup
Add debug configuration for vscode
Remove autorun flag from jest to remove warning
Bump packages to fix dependency version mismatch warning
Changed @arkweid/lefthook to @evilmartians/lefthook as @arkweid/lefthook has been deprecated in favor of @evilmartians/lefthook
Added concurrency groups to integrity check and build workflows. New commits to branches will cancel superseded runs on the same branch/pr
Update imports to not use require syntax
Use node packages (ie node:fs rather than fs)
AndroidVersionCode is now a string rather than a number as it gets converted to a string when passed out of the system
Reduce timeout for windows builds
Remove 2020.1.17f1 from windows builds due to repeated license activation errors
Update naming scheme of workflows for consistency
Update build names so target platform and unity version aren't cut off by github actions UI

* Add exclude to test matrix for 2022.2 on android until Unity bug is fixed

---------

Co-authored-by: AndrewKahr <AndrewKahr@users.noreply.github.com>
2023-03-03 16:25:40 -08:00
AndrewKahr
3de97ed24a Revert android symbol to none (#514) 2023-02-20 19:52:57 -06:00
AndrewKahr
c7a5c0640a Fix reflection code for setting buildAppBundle. Mark AndroidSettings class as static (#512) 2023-02-20 12:25:03 -06:00
AndrewKahr
7f2782d3ed Fix defaults (#510)
* Fix defaults

* Use '' as default for androidExportType
2023-02-19 22:23:37 -06:00
David Finol
f58ac29d5b Add default value for androidExportType (#506) 2023-02-14 01:25:33 -06:00
AndrewKahr
0efa1855a3 Misc QOL Updates and bug fixes (#505)
* Add caching for Unity Hub/Editor on MacOS. Add parameter to pin Unity Hub version on MacOS. Live output MacOS build log to console. Hid extraneous log outputs from git. Throw error when failures detected in log output.

* Update pr template links

* Add system to build Android Project. Update PR Template links. Fix missing types on functions. Cleanup mac-setup module installation

* Switch to androidExportType instead of exportGoogleAndroidProject

* Enforce minimum node version

* Enforce node version minimum. Added yarn-audit-fix to dev dependencies and Updated package vulnerabilities.

* Improve deprecation warning

* Add android symbol type parameter. Change windows scripts to use $LastExitCode and not $?. Update tests.

* Fix issues on android symbols for older unity versions. Change symbol default to public. Increase build test coverage of unity versions.

* Remove 2018.1 from tests

* Remove out variable declaration to support Unity 2018 in default build script. Remove <2019.3 versions of unity from windows builder as IL2CPP isn't supported until 2019.3.

* Fix typo. Use reflection to set buildAppBundle as Unity 2018.2 doesn't support it

* Add missing reflection using

* Remove 2018-2019.3 unity versions from mac as they don't support IL2CPP. Fix app identifier for android in testproject

* Fix android bundle id

* Updated android identifier. Removed incompatible unity versions from tests. Add retry logic to windows as it seems to have licensing issues when so many runners start

* Add timeout and continue on error
2023-02-13 23:07:10 -06:00
AndrewKahr
8c9dcf076d Fix action deprecation warnings (#502)
Co-authored-by: David Finol <davidmfinol@gmail.com>
2023-02-10 08:43:05 -06:00
Pyeongseok Oh
f35829a9d4 Fix incorrect environment variables on macos runner (#499)
* Set proper environment variables for android sdk on macos runner

* Always accept all SDK licenses
2023-02-10 08:04:05 -06:00
AndrewKahr
fb5b03c3b8 Improve mac unity setup steps (#501)
* Improve mac unity setup steps

* Remove silent flag on hub install

---------

Co-authored-by: David Finol <davidmfinol@gmail.com>
2023-02-09 12:24:15 -06:00
dependabot[bot]
584c0366c6 Bump http-cache-semantics from 4.1.0 to 4.1.1 (#498)
Bumps [http-cache-semantics](https://github.com/kornelski/http-cache-semantics) from 4.1.0 to 4.1.1.
- [Release notes](https://github.com/kornelski/http-cache-semantics/releases)
- [Commits](https://github.com/kornelski/http-cache-semantics/compare/v4.1.0...v4.1.1)

---
updated-dependencies:
- dependency-name: http-cache-semantics
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-09 10:08:25 -06:00
Frostebite
28147e5e1b Revert "triggerWorkflowOnComplete param for cloud runner"
This reverts commit 00c5685d03.
2023-02-05 00:22:32 +00:00
Frostebite
00c5685d03 triggerWorkflowOnComplete param for cloud runner 2023-02-05 00:16:42 +00:00
Frostebite
e334dc785a Cloud runner develop - better parameterization of s3 usage, improved async workflow and GC, github checks early integration (#479)
* custom steps may leave value undefined, will be pulled from env vars

* custom steps may leave value undefined, will be pulled from env vars

* custom steps may leave value undefined, will be pulled from env vars

* add 3 new premade steps, steam-deploy-client, steam-deploy-project, aws-s3-pull-build

* fix

* fix

* fix

* continue building async-workflow support

* test checks

* test checks

* test checks

* move github checks within build workflow

* async workflow test

* async workflow test

* async workflow test

* async workflow test

* async workflow test

* async workflow test

* async workflow test

* async workflow test for aws only

* async workflow test for aws only

* async workflow test for aws only

* async workflow test for aws only

* cleanup logging

* disable lz4 compression by default

* disable lz4 compression by default

* AWS BASE STACK for tests

* AWS BASE STACK for tests

* AWS BASE STACK for tests

* AWS BASE STACK for tests

* AWS BASE STACK for tests

* AWS BASE STACK for tests

* disable lz4 compression by default

* disable lz4 compression by default

* Update github check with aws log

* Update github check with aws log

* Update github check with aws log

* Update github check with aws log

* Update github check with aws log

* Update github check with aws log

* Update github check with aws log

* Update github check with aws log

* Update github check with aws log

* Update github check with aws log

* Update github check with aws log

* Update github check with aws log

* Update github check with aws log

* Update github check with aws log

* kinesis and subscription filter for logs creation skipped when watchToEnd false

* kinesis and subscription filter for logs creation skipped when watchToEnd false

* kinesis and subscription filter for logs creation skipped when watchToEnd false

* kinesis and subscription filter for logs creation skipped when watchToEnd false

* kinesis and subscription filter for logs creation skipped when watchToEnd false

* kinesis and subscription filter for logs creation skipped when watchToEnd false

* kinesis and subscription filter for logs creation skipped when watchToEnd false

* kinesis and subscription filter for logs creation skipped when watchToEnd false

* kinesis and subscription filter for logs creation skipped when watchToEnd false

* kinesis and subscription filter for logs creation skipped when watchToEnd false

* cleanup local pipeline, log aws formation

* cleanup local pipeline, log aws formation

* cleanup local pipeline, log aws formation

* cleanup local pipeline, log aws formation

* cleanup local pipeline, log aws formation

* cleanup local pipeline, log aws formation

* cleanup local pipeline, log aws formation

* cleanup local pipeline, log aws formation

* cleanup local pipeline, log aws formation

* cleanup local pipeline, log aws formation

* cleanup local pipeline, log aws formation

* cleanup local pipeline, log aws formation

* cleanup local pipeline, log aws formation

* cleanup local pipeline, log aws formation

* cleanup local pipeline, log aws formation

* cleanup local pipeline, log aws formation

* cleanup local pipeline, log aws formation

* cleanup local pipeline, log aws formation

* cleanup local pipeline, log aws formation

* cleanup local pipeline, log aws formation

* cleanup local pipeline, log aws formation

* async pipeline

* async pipeline

* async pipeline

* async pipeline

* async pipeline

* async pipeline

* async pipeline

* async pipeline

* async pipeline

* async pipeline

* async pipeline

* async pipeline

* async pipeline

* async pipeline

* async pipeline

* async pipeline

* async pipeline

* async pipeline

* async pipeline

* async pipeline

* async pipeline

* async pipeline

* async pipeline

* async pipeline

* async pipeline

* async pipeline

* async pipeline

* workflow

* workflow

* workflow

* workflow

* workflow

* workflow

* workflow

* workflow

* parameterize s3

* parameterize s3

* parameterize s3

* parameterize s3

* parameterize s3

* parameterize s3

* parameterize s3

* parameterize s3

* parameterize s3

* parameterize s3

* parameterize s3

* parameterize s3

* parameterize s3

* parameterize s3

* parameterize s3

* parameterize s3

* parameterize s3

* parameterize s3

* parameterize s3

* parameterize s3

* parameterize s3

* parameterize s3

* parameterize s3

* parameterize s3

* parameterize s3

* parameterize s3

* parameterize s3

* parameterize s3

* parameterize s3

* parameterize s3

* parameterize s3

* parameterize s3

* parameterize s3

* parameterize s3

* parameterize s3

* parameterize s3

* parameterize s3

* parameterize s3

* parameterize s3

* parameterize s3

* parameterize s3

* parameterize s3

* parameterize s3

* parameterize s3

* parameterize s3

* parameterize s3

* parameterize s3

* parameterize s3

* parameterize s3
2023-01-20 17:40:57 +00:00
Frostebite
a45155c578 Enable workflow dispatch (for checks workflow) by adding simple workflow to main (#492)
* workflow

* workflow
2023-01-19 00:12:22 +00:00
dependabot[bot]
f68f14ff9f Bump json5 from 1.0.1 to 1.0.2 (#487)
Bumps [json5](https://github.com/json5/json5) from 1.0.1 to 1.0.2.
- [Release notes](https://github.com/json5/json5/releases)
- [Changelog](https://github.com/json5/json5/blob/main/CHANGELOG.md)
- [Commits](https://github.com/json5/json5/compare/v1.0.1...v1.0.2)

---
updated-dependencies:
- dependency-name: json5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-07 21:01:05 +01:00
AndrewKahr
9329b7369c Install mac modules based on target platform for greater flexibility (#481) 2022-12-14 10:27:36 +01:00
AndrewKahr
5bd589e19f feat: Android updates for Windows and androidVersionCode output (#478)
* Create android keystore on windows, output android version code

* Add androidVersionCode output test

* Move android keystore decode logic to TS
2022-11-23 10:06:58 +01:00
AndrewKahr
f03bee03f6 Remove GITHUB_WORKSPACE from image-environment-factory.ts and fix windows workdir path in docker run command (#477) 2022-11-22 13:54:54 +01:00
Frostebite
2a32a9d870 Fix: retained workspaces must fetch before resetting to needed commit (#476) 2022-11-18 20:43:52 +00:00
Frostebite
d6ac850da1 Fix "large package handling" to redirect to shared folder (#473) 2022-11-17 18:59:33 +00:00
Frostebite
f300dd27bb Add Cloud Runner Pipeline to Main (#472)
* fix: k8s error handling

(cherry picked from commit f633a3efb42432a6d2492712aead865a950c8dca)

* include main in main cloud-runner pipeline

(cherry picked from commit a40fbe941bba1ba4593c83c754b37363a969bfe5)
2022-11-08 09:37:16 +00:00
Frostebite
4cca069ebb Cloud Runner Develop - 1.0 R.C 1 (#437)
Release Candidate changeset 1 - For 1.0 Cloud Runner
2022-11-07 20:41:00 +00:00
Eric Roy
96555a0945 Fix powershell quotation issue, when CUSTOM_PARAMETERS contains spaces (#470) 2022-10-31 11:12:56 +01:00
simensan
4cb3e593f5 Feature/support for unity licensing server linux (#468)
* Initial support for adding a UNITY_LICENSING_SERVER parameter to build parameters

* Test to figure out what the working directory is of current bash script

* Outputting current directory and using $ACTION_FOLDER

* Add resources folder to mounted docker volumes. Used by activation script to copy over template file for unity licensing server

* use awk instead of sed due to http characters breaking syntax

* mkdir for unity config

* Add -p flag to mkdir so parents are also created if missing

* Initial work on returning floating license when using licensing server

* Checking licensing server first for now, since serial is always set

* Parse and save acquired floating license for use for returning after build

* Clean up duplicate commands in activate.sh

* Fixed running string as command, use it as input instead

* Fixed cloud runner tests failing when using a ssh remote.

* Clean up of test files and unnecessary logging

* Moved process of generating services-config.json file from platform specific activate scripts to typescript

* Fixed path
2022-10-22 18:55:58 +02:00
Colt
9f79830454 Fix: builds for Unity versions less than 2019 fail (#467) 2022-10-15 20:52:15 +02:00
Paul Pacheco
f054aaa35d use node 16 (#465)
node12 actions are deprecated.
See documentation here:
https://github.blog/changelog/2022-09-22-github-actions-all-actions-will-begin-running-on-node16-instead-of-node12/

Currently, the unity builder action gives me the following warning:
```
Node.js 12 actions are deprecated. For more information see: https://github.blog/changelog/2022-09-22-github-actions-all-actions-will-begin-running-on-node16-instead-of-node12/. Please update the following actions to use Node.js 16: game-ci/unity-test-runner, game-ci/unity-builder
```

Co-authored-by: Webber Takken <webber@takken.io>
2022-10-13 23:35:23 +02:00
Paul Pacheco
0756841374 update actions core to 1.10.0 (#466)
* update actions core to 1.10.0

The current version of actions/core produces a lot of warning about deprecated set-output command,  like this:

```
The `set-output` command is deprecated and will be disabled soon. Please upgrade to using Environment Files. For more information see: https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/
```

the new version of actions/core uses the environment files instead, so this version bump should fix the warning

* reran yarn
2022-10-13 23:18:30 +02:00
dependabot[bot]
579daa93a6 Bump jose from 1.28.1 to 1.28.2 (#435)
Bumps [jose](https://github.com/panva/jose) from 1.28.1 to 1.28.2.
- [Release notes](https://github.com/panva/jose/releases)
- [Changelog](https://github.com/panva/jose/blob/v1.28.2/CHANGELOG.md)
- [Commits](https://github.com/panva/jose/compare/v1.28.1...v1.28.2)

---
updated-dependencies:
- dependency-name: jose
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Webber Takken <webber@takken.io>
2022-09-17 16:46:22 +02:00
Shane Engelman
1adfabf512 Pass Android parameters for Windows builds, fixes #434 (#436)
https://github.com/game-ci/unity-builder/issues/434 will show that no Windows builds are currently capable of sending `androidVersionCode` or any other Android parameters. This can be fixed very simply by adjusting the build script to pass these parameters. Compare the following two files:

- cdee7d1d9a/dist/platforms/windows/build.ps1 (L112)
- cdee7d1d9a/dist/platforms/ubuntu/steps/build.sh (L138)

It's clear that the current Windows build script is omitting certain parameters. This will resolve that issue (linked in commit message header).
2022-09-16 15:12:28 -05:00
Webber Takken
cdee7d1d9a Update FUNDING.yml (#431) 2022-09-06 16:54:29 +02:00
Webber Takken
fb633660a4 chore: add game-ci github sponsoring option (#429) 2022-08-29 23:49:54 +02:00
dependabot[bot]
c15f955669 Bump @actions/core from 1.6.0 to 1.9.1 (#426)
Bumps [@actions/core](https://github.com/actions/toolkit/tree/HEAD/packages/core) from 1.6.0 to 1.9.1.
- [Release notes](https://github.com/actions/toolkit/releases)
- [Changelog](https://github.com/actions/toolkit/blob/main/packages/core/RELEASES.md)
- [Commits](https://github.com/actions/toolkit/commits/HEAD/packages/core)

---
updated-dependencies:
- dependency-name: "@actions/core"
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-19 23:28:41 +02:00
David Finol
2684be2dee Fix androidTargetSdkVersion (#425)
* Fix androidTargetSdkVersion

Fix for newer versions of Android API levels that do not get correctly parsed.

* Update dist/default-build-script/Assets/Editor/UnityBuilderAction/Input/AndroidSettings.cs
2022-08-11 07:35:01 -05:00
288 changed files with 208073 additions and 192653 deletions

View File

@@ -14,7 +14,8 @@
"env": {
"node": true,
"es6": true,
"jest/globals": true
"jest/globals": true,
"es2020": true
},
"rules": {
// Error out for code formatting errors
@@ -38,9 +39,21 @@
// Mandatory spacing
"padding-line-between-statements": [
"error",
{ "blankLine": "always", "prev": "*", "next": "return" },
{ "blankLine": "always", "prev": "directive", "next": "*" },
{ "blankLine": "any", "prev": "directive", "next": "directive" }
{
"blankLine": "always",
"prev": "*",
"next": "return"
},
{
"blankLine": "always",
"prev": "directive",
"next": "*"
},
{
"blankLine": "any",
"prev": "directive",
"next": "directive"
}
],
// Enforce camelCase
"camelcase": "error",
@@ -53,8 +66,25 @@
// Unused vars are useful to keep method signatures consistent and documented
"@typescript-eslint/no-unused-vars": "off",
// For this project only use kebab-case
"unicorn/filename-case": ["error", { "cases": { "kebabCase": true } }],
"unicorn/filename-case": [
"error",
{
"cases": {
"kebabCase": true
}
}
],
// Allow Array.from(set) mitigate TS2569 which would require '--downlevelIteration'
"unicorn/prefer-spread": "off"
}
"unicorn/prefer-spread": "off",
// Temp disable to prevent mixing changes with other PRs
"i18n-text/no-en": "off"
},
"overrides": [
{
"files": ["jest.setup.js"],
"rules": {
"import/no-commonjs": "off"
}
}
]
}

4
.github/FUNDING.yml vendored
View File

@@ -1,8 +1,8 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
github: game-ci
patreon: # Replace with a single Patreon username
open_collective: game-ci
open_collective: # replace with a single OpenCollective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry

View File

@@ -2,10 +2,28 @@
- ...
#### Related Issues
- ...
#### Related PRs
- ...
#### Successful Workflow Run Link
PRs don't have access to secrets so you will need to provide a link to a successful run of the workflows from your own
repo.
- ...
#### Checklist
<!-- please check all items and add your own -->
- [x] Read the contribution [guide](../CONTRIBUTING.md) and accept the [code](../CODE_OF_CONDUCT.md) of conduct
- [x] Read the contribution [guide](https://github.com/game-ci/unity-builder/blob/main/CONTRIBUTING.md) and accept the
[code](https://github.com/game-ci/unity-builder/blob/main/CODE_OF_CONDUCT.md) of conduct
- [ ] Docs (If new inputs or outputs have been added or changes to behavior that should be documented. Please make a PR
in the [documentation repo](https://github.com/game-ci/documentation))
- [ ] Readme (updated or not needed)
- [ ] Tests (added, updated or not needed)

View File

@@ -13,7 +13,7 @@ jobs:
id: requestActivationFile
uses: game-ci/unity-request-activation-file@v2.0-alpha-1
- name: Upload activation file
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v4
with:
name: ${{ steps.requestActivationFile.outputs.filePath }}
path: ${{ steps.requestActivationFile.outputs.filePath }}

87
.github/workflows/build-tests-mac.yml vendored Normal file
View File

@@ -0,0 +1,87 @@
name: Builds - MacOS
on:
workflow_dispatch:
push:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
buildForAllPlatformsMacOS:
name: ${{ matrix.targetPlatform }} on ${{ matrix.unityVersion }}
runs-on: macos-latest
strategy:
fail-fast: false
matrix:
projectPath:
- test-project
unityVersion:
- 2021.3.45f1
- 2022.3.13f1
- 2023.2.2f1
targetPlatform:
- StandaloneOSX # Build a MacOS executable
- iOS # Build an iOS executable
include:
# Additionally test enableGpu build for a standalone windows target
- unityVersion: 6000.0.36f1
targetPlatform: StandaloneOSX
- unityVersion: 6000.0.36f1
targetPlatform: StandaloneOSX
buildProfile: 'Assets/Settings/Build Profiles/Sample macOS Build Profile.asset'
steps:
###########################
# Checkout #
###########################
- uses: actions/checkout@v4
with:
lfs: true
###########################
# Cache #
###########################
- uses: actions/cache@v4
with:
path: ${{ matrix.projectPath }}/Library
key: Library-${{ matrix.projectPath }}-macos-${{ matrix.targetPlatform }}
restore-keys: |
Library-${{ matrix.projectPath }}-macos-
Library-
###########################
# Set Scripting Backend #
###########################
- name: Set Scripting Backend To il2cpp
run: |
mv -f "./test-project/ProjectSettings/ProjectSettingsIl2cpp.asset" "./test-project/ProjectSettings/ProjectSettings.asset"
###########################
# Build #
###########################
- uses: ./
env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
with:
buildName: 'GameCI Test Build'
projectPath: ${{ matrix.projectPath }}
unityVersion: ${{ matrix.unityVersion }}
targetPlatform: ${{ matrix.targetPlatform }}
buildProfile: ${{ matrix.buildProfile }}
customParameters: -profile SomeProfile -someBoolean -someValue exampleValue
# We use dirty build because we are replacing the default project settings file above
allowDirtyBuild: true
###########################
# Upload #
###########################
- uses: actions/upload-artifact@v4
with:
name: Build ${{ matrix.targetPlatform }} on MacOS (${{ matrix.unityVersion }})${{ matrix.buildProfile && ' With Build Profile' || '' }}
path: build
retention-days: 14

206
.github/workflows/build-tests-ubuntu.yml vendored Normal file
View File

@@ -0,0 +1,206 @@
name: Builds - Ubuntu
on:
workflow_dispatch:
push:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
env:
UNITY_LICENSE:
"<?xml version=\"1.0\" encoding=\"UTF-8\"?><root>\n <License
id=\"Terms\">\n <MachineBindings>\n <Binding Key=\"1\"
Value=\"576562626572264761624c65526f7578\"/>\n <Binding Key=\"2\"
Value=\"576562626572264761624c65526f7578\"/>\n </MachineBindings>\n <MachineID
Value=\"D7nTUnjNAmtsUMcnoyrqkgIbYdM=\"/>\n <SerialHash
Value=\"2033b8ac3e6faa3742ca9f0bfae44d18f2a96b80\"/>\n <Features>\n <Feature
Value=\"33\"/>\n <Feature Value=\"1\"/>\n <Feature Value=\"12\"/>\n <Feature
Value=\"2\"/>\n <Feature Value=\"24\"/>\n <Feature Value=\"3\"/>\n <Feature
Value=\"36\"/>\n <Feature Value=\"17\"/>\n <Feature Value=\"19\"/>\n <Feature
Value=\"62\"/>\n </Features>\n <DeveloperData
Value=\"AQAAAEY0LUJHUlgtWEQ0RS1aQ1dWLUM1SlctR0RIQg==\"/>\n <SerialMasked
Value=\"F4-BGRX-XD4E-ZCWV-C5JW-XXXX\"/>\n <StartDate Value=\"2021-02-08T00:00:00\"/>\n <UpdateDate
Value=\"2021-02-09T00:34:57\"/>\n <InitialActivationDate
Value=\"2021-02-08T00:34:56\"/>\n <LicenseVersion Value=\"6.x\"/>\n <ClientProvidedVersion
Value=\"2018.4.30f1\"/>\n <AlwaysOnline Value=\"false\"/>\n <Entitlements>\n <Entitlement
Ns=\"unity_editor\" Tag=\"UnityPersonal\" Type=\"EDITOR\"
ValidTo=\"9999-12-31T00:00:00\"/>\n <Entitlement Ns=\"unity_editor\" Tag=\"DarkSkin\"
Type=\"EDITOR_FEATURE\" ValidTo=\"9999-12-31T00:00:00\"/>\n </Entitlements>\n </License>\n<Signature
xmlns=\"http://www.w3.org/2000/09/xmldsig#\"><SignedInfo><CanonicalizationMethod
Algorithm=\"http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments\"/><SignatureMethod
Algorithm=\"http://www.w3.org/2000/09/xmldsig#rsa-sha1\"/><Reference URI=\"#Terms\"><Transforms><Transform
Algorithm=\"http://www.w3.org/2000/09/xmldsig#enveloped-signature\"/></Transforms><DigestMethod
Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\"/><DigestValue>m0Db8UK+ktnOLJBtHybkfetpcKo=</DigestValue></Reference></SignedInfo><SignatureValue>o/pUbSQAukz7+ZYAWhnA0AJbIlyyCPL7bKVEM2lVqbrXt7cyey+umkCXamuOgsWPVUKBMkXtMH8L\n5etLmD0getWIhTGhzOnDCk+gtIPfL4jMo9tkEuOCROQAXCci23VFscKcrkB+3X6h4wEOtA2APhOY\nB+wvC794o8/82ffjP79aVAi57rp3Wmzx+9pe9yMwoJuljAy2sc2tIMgdQGWVmOGBpQm3JqsidyzI\nJWG2kjnc7pDXK9pwYzXoKiqUqqrut90d+kQqRyv7MSZXR50HFqD/LI69h68b7P8Bjo3bPXOhNXGR\n9YCoemH6EkfCJxp2gIjzjWW+l2Hj2EsFQi8YXw==</SignatureValue></Signature></root>"
jobs:
buildForAllPlatformsUbuntu:
name:
"${{ matrix.targetPlatform }} on ${{ matrix.unityVersion}}${{startsWith(matrix.buildProfile, 'Assets') && ' (via Build Profile)' || '' }}"
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
providerStrategy:
# - local-docker
- local
projectPath:
- test-project
unityVersion:
- 2021.3.32f1
- 2022.3.13f1
- 2023.2.2f1
targetPlatform:
- StandaloneOSX # Build a macOS standalone (Intel 64-bit) with mono backend.
- StandaloneWindows64 # Build a Windows 64-bit standalone with mono backend.
- StandaloneLinux64 # Build a Linux 64-bit standalone with mono/il2cpp backend.
- iOS # Build an iOS project.
- Android # Build an Android .apk.
- WebGL # WebGL.
buildWithIl2cpp:
- false
- true
additionalParameters:
- -param value
- -standaloneBuildSubtarget Server
# Skipping configurations that are not supported
exclude:
# No il2cpp support on Linux Host
- targetPlatform: StandaloneOSX
buildWithIl2cpp: true
- targetPlatform: StandaloneWindows64
buildWithIl2cpp: true
# Only builds with Il2cpp
- targetPlatform: iOS
buildWithIl2cpp: false
- targetPlatform: Android
buildWithIl2cpp: false
- targetPlatform: WebGL
buildWithIl2cpp: false
# No dedicated server support
- targetPlatform: WebGL
additionalParameters: -standaloneBuildSubtarget Server
- targetPlatform: Android
additionalParameters: -standaloneBuildSubtarget Server
- targetPlatform: iOS
additionalParameters: -standaloneBuildSubtarget Server
# No dedicated server support on Linux Host
- targetPlatform: StandaloneOSX
additionalParameters: -standaloneBuildSubtarget Server
# No il2cpp dedicated server support on Linux Host
- targetPlatform: StandaloneWindows64
additionalParameters: -standaloneBuildSubtarget Server
buildWithIl2cpp: true
include:
- unityVersion: 6000.0.36f1
targetPlatform: WebGL
- unityVersion: 6000.0.36f1
targetPlatform: WebGL
buildProfile: 'Assets/Settings/Build Profiles/Sample WebGL Build Profile.asset'
steps:
- name: Clear Space for Android Build
if: matrix.targetPlatform == 'Android'
uses: jlumbroso/free-disk-space@v1.3.1
###########################
# Checkout #
###########################
- uses: actions/checkout@v4
with:
lfs: true
###########################
# Cache #
###########################
- uses: actions/cache@v4
with:
path: ${{ matrix.projectPath }}/Library
key: Library-${{ matrix.projectPath }}-ubuntu-${{ matrix.targetPlatform }}
restore-keys: |
Library-${{ matrix.projectPath }}-ubuntu-
Library-
###########################
# Set Scripting Backend #
###########################
- name: Set Scripting Backend To il2cpp
if: matrix.buildWithIl2cpp == true
run: |
mv -f "./test-project/ProjectSettings/ProjectSettingsIl2cpp.asset" "./test-project/ProjectSettings/ProjectSettings.asset"
###########################
# Build #
###########################
- name: Build
uses: ./
id: build-1
continue-on-error: true
env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
with:
buildName: 'GameCI Test Build'
projectPath: ${{ matrix.projectPath }}
buildProfile: ${{ matrix.buildProfile }}
unityVersion: ${{ matrix.unityVersion }}
targetPlatform: ${{ matrix.targetPlatform }}
customParameters: -profile SomeProfile -someBoolean -someValue exampleValue ${{ matrix.additionalParameters }}
providerStrategy: ${{ matrix.providerStrategy }}
allowDirtyBuild: true
- name: Sleep for Retry
if: ${{ steps.build-1.outcome == 'failure' }}
run: |
sleep 60
- name: Build (Retry 1)
uses: ./
id: build-2
if: ${{ steps.build-1.outcome == 'failure' }}
continue-on-error: true
env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
with:
buildName: 'GameCI Test Build'
projectPath: ${{ matrix.projectPath }}
buildProfile: ${{ matrix.buildProfile }}
unityVersion: ${{ matrix.unityVersion }}
targetPlatform: ${{ matrix.targetPlatform }}
customParameters: -profile SomeProfile -someBoolean -someValue exampleValue ${{ matrix.additionalParameters }}
providerStrategy: ${{ matrix.providerStrategy }}
allowDirtyBuild: true
- name: Sleep for Retry
if: ${{ steps.build-2.outcome == 'failure' }}
run: |
sleep 240
- name: Build (Retry 2)
uses: ./
id: build-3
if: ${{ steps.build-2.outcome == 'failure' }}
env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
with:
buildName: 'GameCI Test Build'
projectPath: ${{ matrix.projectPath }}
buildProfile: ${{ matrix.buildProfile }}
unityVersion: ${{ matrix.unityVersion }}
targetPlatform: ${{ matrix.targetPlatform }}
customParameters: -profile SomeProfile -someBoolean -someValue exampleValue ${{ matrix.additionalParameters }}
providerStrategy: ${{ matrix.providerStrategy }}
allowDirtyBuild: true
###########################
# Upload #
###########################
- uses: actions/upload-artifact@v4
with:
name:
"Build ${{ matrix.targetPlatform }}${{ startsWith(matrix.buildProfile, 'Assets') && ' (via Build Profile)' || '' }} on Ubuntu (${{ matrix.unityVersion }}_il2cpp_${{ matrix.buildWithIl2cpp }}_params_${{ matrix.additionalParameters }})"
path: build
retention-days: 14

View File

@@ -0,0 +1,151 @@
name: Builds - Windows
on:
workflow_dispatch:
push:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
buildForAllPlatformsWindows:
name: ${{ matrix.targetPlatform }} on ${{ matrix.unityVersion }}
runs-on: windows-2022
strategy:
fail-fast: false
matrix:
projectPath:
- test-project
unityVersion:
- 2021.3.32f1
- 2022.3.13f1
- 2023.2.2f1
targetPlatform:
- Android # Build an Android apk.
- StandaloneWindows64 # Build a Windows 64-bit standalone.
- WSAPlayer # Build a UWP App
- tvOS # Build an Apple TV XCode project
enableGpu:
- false
include:
# Additionally test enableGpu build for a standalone windows target
- projectPath: test-project
unityVersion: 2023.2.2f1
targetPlatform: StandaloneWindows64
enableGpu: true
- unityVersion: 6000.0.36f1
targetPlatform: StandaloneWindows64
- unityVersion: 6000.0.36f1
targetPlatform: StandaloneWindows64
buildProfile: 'Assets/Settings/Build Profiles/Sample Windows Build Profile.asset'
steps:
###########################
# Checkout #
###########################
- uses: actions/checkout@v4
with:
lfs: true
###########################
# Cache #
###########################
- uses: actions/cache@v4
with:
path: ${{ matrix.projectPath }}/Library
key: Library-${{ matrix.projectPath }}-windows-${{ matrix.targetPlatform }}
restore-keys: |
Library-${{ matrix.projectPath }}-windows-
Library-
###########################
# Set Scripting Backend #
###########################
- name: Set Scripting Backend To il2cpp
run: |
Move-Item -Path "./test-project/ProjectSettings/ProjectSettingsIl2cpp.asset" -Destination "./test-project/ProjectSettings/ProjectSettings.asset" -Force
###########################
# Build #
###########################
- name: Build
uses: ./
id: build-1
continue-on-error: true
timeout-minutes: 30
env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
with:
buildName: 'GameCI Test Build'
projectPath: ${{ matrix.projectPath }}
unityVersion: ${{ matrix.unityVersion }}
targetPlatform: ${{ matrix.targetPlatform }}
buildProfile: ${{ matrix.buildProfile }}
enableGpu: ${{ matrix.enableGpu }}
customParameters: -profile SomeProfile -someBoolean -someValue exampleValue
allowDirtyBuild: true
# We use dirty build because we are replacing the default project settings file above
- name: Sleep for Retry
if: ${{ steps.build-1.outcome == 'failure' }}
run: |
Start-Sleep -s 120
- name: Build Retry 1
uses: ./
id: build-2
continue-on-error: true
timeout-minutes: 30
if: steps.build-1.outcome == 'failure'
env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
with:
buildName: 'GameCI Test Build'
projectPath: ${{ matrix.projectPath }}
unityVersion: ${{ matrix.unityVersion }}
targetPlatform: ${{ matrix.targetPlatform }}
enableGpu: ${{ matrix.enableGpu }}
customParameters: -profile SomeProfile -someBoolean -someValue exampleValue
allowDirtyBuild: true
# We use dirty build because we are replacing the default project settings file above
- name: Sleep for Retry
if: ${{ steps.build-1.outcome == 'failure' && steps.build-2.outcome == 'failure' }}
run: |
Start-Sleep -s 240
- name: Build Retry 2
uses: ./
id: build-3
timeout-minutes: 30
if: ${{ steps.build-1.outcome == 'failure' && steps.build-2.outcome == 'failure' }}
env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
with:
buildName: 'GameCI Test Build'
projectPath: ${{ matrix.projectPath }}
unityVersion: ${{ matrix.unityVersion }}
targetPlatform: ${{ matrix.targetPlatform }}
enableGpu: ${{ matrix.enableGpu }}
customParameters: -profile SomeProfile -someBoolean -someValue exampleValue
allowDirtyBuild: true
# We use dirty build because we are replacing the default project settings file above
###########################
# Upload #
###########################
- uses: actions/upload-artifact@v4
with:
name: Build ${{ matrix.targetPlatform }} on Windows (${{ matrix.unityVersion }})${{ matrix.enableGpu && ' With GPU' || '' }}${{ matrix.buildProfile && ' With Build Profile' || '' }}
path: build
retention-days: 14

View File

@@ -1,73 +0,0 @@
name: Builds
on:
push: { branches: [main] }
pull_request:
paths-ignore:
- '.github/**'
env:
UNITY_LICENSE: "<?xml version=\"1.0\" encoding=\"UTF-8\"?><root>\n <License id=\"Terms\">\n <MachineBindings>\n <Binding Key=\"1\" Value=\"576562626572264761624c65526f7578\"/>\n <Binding Key=\"2\" Value=\"576562626572264761624c65526f7578\"/>\n </MachineBindings>\n <MachineID Value=\"D7nTUnjNAmtsUMcnoyrqkgIbYdM=\"/>\n <SerialHash Value=\"2033b8ac3e6faa3742ca9f0bfae44d18f2a96b80\"/>\n <Features>\n <Feature Value=\"33\"/>\n <Feature Value=\"1\"/>\n <Feature Value=\"12\"/>\n <Feature Value=\"2\"/>\n <Feature Value=\"24\"/>\n <Feature Value=\"3\"/>\n <Feature Value=\"36\"/>\n <Feature Value=\"17\"/>\n <Feature Value=\"19\"/>\n <Feature Value=\"62\"/>\n </Features>\n <DeveloperData Value=\"AQAAAEY0LUJHUlgtWEQ0RS1aQ1dWLUM1SlctR0RIQg==\"/>\n <SerialMasked Value=\"F4-BGRX-XD4E-ZCWV-C5JW-XXXX\"/>\n <StartDate Value=\"2021-02-08T00:00:00\"/>\n <UpdateDate Value=\"2021-02-09T00:34:57\"/>\n <InitialActivationDate Value=\"2021-02-08T00:34:56\"/>\n <LicenseVersion Value=\"6.x\"/>\n <ClientProvidedVersion Value=\"2018.4.30f1\"/>\n <AlwaysOnline Value=\"false\"/>\n <Entitlements>\n <Entitlement Ns=\"unity_editor\" Tag=\"UnityPersonal\" Type=\"EDITOR\" ValidTo=\"9999-12-31T00:00:00\"/>\n <Entitlement Ns=\"unity_editor\" Tag=\"DarkSkin\" Type=\"EDITOR_FEATURE\" ValidTo=\"9999-12-31T00:00:00\"/>\n </Entitlements>\n </License>\n<Signature xmlns=\"http://www.w3.org/2000/09/xmldsig#\"><SignedInfo><CanonicalizationMethod Algorithm=\"http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments\"/><SignatureMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#rsa-sha1\"/><Reference URI=\"#Terms\"><Transforms><Transform Algorithm=\"http://www.w3.org/2000/09/xmldsig#enveloped-signature\"/></Transforms><DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\"/><DigestValue>m0Db8UK+ktnOLJBtHybkfetpcKo=</DigestValue></Reference></SignedInfo><SignatureValue>o/pUbSQAukz7+ZYAWhnA0AJbIlyyCPL7bKVEM2lVqbrXt7cyey+umkCXamuOgsWPVUKBMkXtMH8L\n5etLmD0getWIhTGhzOnDCk+gtIPfL4jMo9tkEuOCROQAXCci23VFscKcrkB+3X6h4wEOtA2APhOY\nB+wvC794o8/82ffjP79aVAi57rp3Wmzx+9pe9yMwoJuljAy2sc2tIMgdQGWVmOGBpQm3JqsidyzI\nJWG2kjnc7pDXK9pwYzXoKiqUqqrut90d+kQqRyv7MSZXR50HFqD/LI69h68b7P8Bjo3bPXOhNXGR\n9YCoemH6EkfCJxp2gIjzjWW+l2Hj2EsFQi8YXw==</SignatureValue></Signature></root>"
jobs:
buildForAllPlatformsUbuntu:
name: Build for ${{ matrix.targetPlatform }} on version ${{ matrix.unityVersion }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
projectPath:
- test-project
unityVersion:
- 2019.2.11f1
- 2019.3.15f1
targetPlatform:
- StandaloneOSX # Build a macOS standalone (Intel 64-bit).
- StandaloneWindows64 # Build a Windows 64-bit standalone.
- StandaloneLinux64 # Build a Linux 64-bit standalone.
- iOS # Build an iOS player.
- Android # Build an Android .apk.
- WebGL # WebGL.
# - StandaloneWindows # Build a Windows standalone.
# - WSAPlayer # Build an Windows Store Apps player.
# - PS4 # Build a PS4 Standalone.
# - XboxOne # Build a Xbox One Standalone.
# - tvOS # Build to Apple's tvOS platform.
# - Switch # Build a Nintendo Switch player
steps:
###########################
# Checkout #
###########################
- uses: actions/checkout@v2
with:
lfs: true
###########################
# Cache #
###########################
- uses: actions/cache@v2
with:
path: ${{ matrix.projectPath }}/Library
key: Library-${{ matrix.projectPath }}-ubuntu-${{ matrix.targetPlatform }}
restore-keys: |
Library-${{ matrix.projectPath }}-ubuntu-
Library-
###########################
# Build #
###########################
- uses: ./
with:
projectPath: ${{ matrix.projectPath }}
unityVersion: ${{ matrix.unityVersion }}
targetPlatform: ${{ matrix.targetPlatform }}
customParameters: -profile SomeProfile -someBoolean -someValue exampleValue
###########################
# Upload #
###########################
- uses: actions/upload-artifact@v2
with:
name: Build Ubuntu (${{ matrix.unityVersion }})
path: build
retention-days: 14

View File

@@ -1,37 +0,0 @@
name: Cleanup (cron)
on:
schedule:
- cron: '30 10 * * SUN' # every sunday at 10:30
jobs:
deleteArtifacts:
runs-on: ubuntu-latest
steps:
- name: Delete old artifacts
uses: kolpav/purge-artifacts-action@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
expire-in: 21 days
cleanupCloudRunner:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
if: github.event.event_type != 'pull_request_target'
with:
lfs: true
- uses: actions/setup-node@v2
with:
node-version: 12.x
- run: yarn
- run: yarn run cli --help
env:
AWS_REGION: eu-west-2
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: eu-west-2
- run: yarn run cli -m aws-list-all
env:
AWS_REGION: eu-west-2
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: eu-west-2

View File

@@ -1,218 +0,0 @@
name: Cloud Runner
on:
push: { branches: [cloud-runner-develop, main] }
# push: { branches: [main] }
# pull_request:
# paths-ignore:
# - '.github/**'
env:
GKE_ZONE: 'us-central1'
GKE_REGION: 'us-central1'
GKE_PROJECT: 'unitykubernetesbuilder'
GKE_CLUSTER: 'game-ci-github-pipelines'
GCP_LOGGING: true
GCP_PROJECT: unitykubernetesbuilder
GCP_LOG_FILE: ${{ github.workspace }}/cloud-runner-logs.txt
AWS_REGION: eu-west-2
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: eu-west-2
AWS_BASE_STACK_NAME: game-ci-github-pipelines
CLOUD_RUNNER_BRANCH: ${{ github.ref }}
CLOUD_RUNNER_TESTS: true
DEBUG: true
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
jobs:
awsBuild:
name: AWS Fargate Build
if: github.event.pull_request.draft == false
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
projectPath:
- test-project
unityVersion:
# - 2019.2.11f1
- 2019.3.15f1
targetPlatform:
#- StandaloneOSX # Build a macOS standalone (Intel 64-bit).
- StandaloneWindows64 # Build a Windows 64-bit standalone.
- StandaloneLinux64 # Build a Linux 64-bit standalone.
- WebGL # WebGL.
#- iOS # Build an iOS player.
#- Android # Build an Android .apk.
# - StandaloneWindows # Build a Windows standalone.
# - WSAPlayer # Build an Windows Store Apps player.
# - PS4 # Build a PS4 Standalone.
# - XboxOne # Build a Xbox One Standalone.
# - tvOS # Build to Apple's tvOS platform.
# - Switch # Build a Nintendo Switch player
# steps
steps:
- name: Checkout (default)
uses: actions/checkout@v2
if: github.event.event_type != 'pull_request_target'
with:
lfs: true
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: eu-west-2
- run: yarn
- run: yarn run cli --help
- run: yarn run test "caching"
- run: yarn run test-i-aws
env:
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
PROJECT_PATH: ${{ matrix.projectPath }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TARGET_PLATFORM: ${{ matrix.targetPlatform }}
cloudRunnerTests: true
versioning: None
- uses: ./
id: aws-fargate-unity-build
timeout-minutes: 25
with:
cloudRunnerCluster: aws
versioning: None
projectPath: ${{ matrix.projectPath }}
unityVersion: ${{ matrix.unityVersion }}
targetPlatform: ${{ matrix.targetPlatform }}
githubToken: ${{ secrets.GITHUB_TOKEN }}
postBuildSteps: |
- name: upload
image: amazon/aws-cli
commands: |
aws configure set aws_access_key_id $AWS_ACCESS_KEY_ID --profile default
aws configure set aws_secret_access_key $AWS_SECRET_ACCESS_KEY --profile default
aws configure set region $AWS_DEFAULT_REGION --profile default
aws s3 ls
aws s3 ls game-ci-test-storage
ls /data/cache/$CACHE_KEY
ls /data/cache/$CACHE_KEY/build
aws s3 cp /data/cache/$CACHE_KEY/build/build-$BUILD_GUID.tar s3://game-ci-test-storage/$CACHE_KEY/build-$BUILD_GUID.tar
secrets:
- name: awsAccessKeyId
value: ${{ secrets.AWS_ACCESS_KEY_ID }}
- name: awsSecretAccessKey
value: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: awsDefaultRegion
value: eu-west-2
- run: |
aws s3 cp s3://game-ci-test-storage/${{ steps.aws-fargate-unity-build.outputs.CACHE_KEY }}/build-${{ steps.aws-fargate-unity-build.outputs.BUILD_GUID }}.tar build-${{ steps.aws-fargate-unity-build.outputs.BUILD_GUID }}.tar
ls
- run: yarn run cli -m aws-garbage-collect
###########################
# Upload #
###########################
# download from cloud storage
- uses: actions/upload-artifact@v2
with:
name: AWS Build (${{ matrix.targetPlatform }})
path: build-${{ steps.aws-fargate-unity-build.outputs.BUILD_GUID }}.tar
retention-days: 14
k8sBuilds:
name: K8s (GKE Autopilot) build for ${{ matrix.targetPlatform }} on version ${{ matrix.unityVersion }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
unityVersion:
# - 2019.2.11f1
- 2019.3.15f1
targetPlatform:
# - StandaloneWindows64
- StandaloneLinux64
steps:
###########################
# Checkout #
###########################
- uses: actions/checkout@v2
if: github.event.event_type != 'pull_request_target'
with:
lfs: true
###########################
# Setup #
###########################
- uses: google-github-actions/setup-gcloud@v0
with:
version: '288.0.0'
service_account_email: ${{ secrets.GOOGLE_SERVICE_ACCOUNT_EMAIL }}
service_account_key: ${{ secrets.GOOGLE_SERVICE_ACCOUNT_KEY }}
- name: Get GKE cluster credentials
run: gcloud container clusters get-credentials $GKE_CLUSTER --zone $GKE_ZONE --project $GKE_PROJECT
###########################
# Cloud Runner Test Suite #
###########################
- uses: actions/setup-node@v2
with:
node-version: 12.x
- run: yarn
- run: yarn run cli --help
- run: yarn run test "caching"
- name: Cloud Runner Test Suite
run: yarn run test-i-k8s --detectOpenHandles --forceExit
env:
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
PROJECT_PATH: ${{ matrix.projectPath }}
TARGET_PLATFORM: ${{ matrix.targetPlatform }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
KUBE_CONFIG: ${{ steps.read-base64.outputs.base64 }}
unityVersion: ${{ matrix.unityVersion }}
cloudRunnerTests: true
versioning: None
###########################
# Cloud Runner Build Test #
###########################
- name: Cloud Runner Build Test
uses: ./
id: k8s-unity-build
timeout-minutes: 30
with:
cloudRunnerCluster: k8s
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
targetPlatform: ${{ matrix.targetPlatform }}
kubeConfig: ${{ steps.read-base64.outputs.base64 }}
githubToken: ${{ secrets.GITHUB_TOKEN }}
projectPath: test-project
unityVersion: ${{ matrix.unityVersion }}
versioning: None
postBuildSteps: |
- name: upload
image: amazon/aws-cli
commands: |
aws configure set aws_access_key_id $AWS_ACCESS_KEY_ID --profile default
aws configure set aws_secret_access_key $AWS_SECRET_ACCESS_KEY --profile default
aws configure set region $AWS_DEFAULT_REGION --profile default
aws s3 ls
aws s3 ls game-ci-test-storage
ls /data/cache/$CACHE_KEY
aws s3 cp /data/cache/$CACHE_KEY/build/build-$BUILD_GUID.tar s3://game-ci-test-storage/$CACHE_KEY/build-$BUILD_GUID.tar
secrets:
- name: awsAccessKeyId
value: ${{ secrets.AWS_ACCESS_KEY_ID }}
- name: awsSecretAccessKey
value: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: awsDefaultRegion
value: eu-west-2
- run: |
aws s3 cp s3://game-ci-test-storage/${{ steps.k8s-unity-build.outputs.CACHE_KEY }}/build-${{ steps.k8s-unity-build.outputs.BUILD_GUID }}.tar build-${{ steps.k8s-unity-build.outputs.BUILD_GUID }}.tar
ls
###########################
# Upload #
###########################
# download from cloud storage
- uses: actions/upload-artifact@v2
with:
name: K8s Build (${{ matrix.targetPlatform }})
path: build-${{ steps.k8s-unity-build.outputs.BUILD_GUID }}.tar
retention-days: 14

View File

@@ -4,21 +4,35 @@ on:
push: { branches: [main] }
pull_request: {}
permissions:
contents: read
checks: write
statuses: write
env:
CODECOV_TOKEN: '2f2eb890-30e2-4724-83eb-7633832cf0de'
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
tests:
name: Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '16'
node-version: '18'
- run: yarn
- run: yarn lint
- run: yarn test --coverage
- run: yarn test:ci --coverage
- run: bash <(curl -s https://codecov.io/bash)
- run: yarn build || { echo "build command should always succeed" ; exit 61; }
# - run: yarn build --quiet && git diff --quiet dist || { echo "dist should be auto generated" ; git diff dist ; exit 62; }
# - run: yarn build --quiet && git diff --quiet dist || { echo "dist should be auto generated" ; git diff dist ; exit 62; }
orchestrator:
name: Orchestrator Integrity
uses: ./.github/workflows/orchestrator-integrity.yml
secrets: inherit

View File

@@ -1,74 +0,0 @@
name: Mac Builds
on:
push:
branches:
- main
env:
UNITY_LICENSE: "<?xml version=\"1.0\" encoding=\"UTF-8\"?><root>\n <License id=\"Terms\">\n <MachineBindings>\n <Binding Key=\"1\" Value=\"576562626572264761624c65526f7578\"/>\n <Binding Key=\"2\" Value=\"576562626572264761624c65526f7578\"/>\n </MachineBindings>\n <MachineID Value=\"D7nTUnjNAmtsUMcnoyrqkgIbYdM=\"/>\n <SerialHash Value=\"2033b8ac3e6faa3742ca9f0bfae44d18f2a96b80\"/>\n <Features>\n <Feature Value=\"33\"/>\n <Feature Value=\"1\"/>\n <Feature Value=\"12\"/>\n <Feature Value=\"2\"/>\n <Feature Value=\"24\"/>\n <Feature Value=\"3\"/>\n <Feature Value=\"36\"/>\n <Feature Value=\"17\"/>\n <Feature Value=\"19\"/>\n <Feature Value=\"62\"/>\n </Features>\n <DeveloperData Value=\"AQAAAEY0LUJHUlgtWEQ0RS1aQ1dWLUM1SlctR0RIQg==\"/>\n <SerialMasked Value=\"F4-BGRX-XD4E-ZCWV-C5JW-XXXX\"/>\n <StartDate Value=\"2021-02-08T00:00:00\"/>\n <UpdateDate Value=\"2021-02-09T00:34:57\"/>\n <InitialActivationDate Value=\"2021-02-08T00:34:56\"/>\n <LicenseVersion Value=\"6.x\"/>\n <ClientProvidedVersion Value=\"2018.4.30f1\"/>\n <AlwaysOnline Value=\"false\"/>\n <Entitlements>\n <Entitlement Ns=\"unity_editor\" Tag=\"UnityPersonal\" Type=\"EDITOR\" ValidTo=\"9999-12-31T00:00:00\"/>\n <Entitlement Ns=\"unity_editor\" Tag=\"DarkSkin\" Type=\"EDITOR_FEATURE\" ValidTo=\"9999-12-31T00:00:00\"/>\n </Entitlements>\n </License>\n<Signature xmlns=\"http://www.w3.org/2000/09/xmldsig#\"><SignedInfo><CanonicalizationMethod Algorithm=\"http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments\"/><SignatureMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#rsa-sha1\"/><Reference URI=\"#Terms\"><Transforms><Transform Algorithm=\"http://www.w3.org/2000/09/xmldsig#enveloped-signature\"/></Transforms><DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\"/><DigestValue>m0Db8UK+ktnOLJBtHybkfetpcKo=</DigestValue></Reference></SignedInfo><SignatureValue>o/pUbSQAukz7+ZYAWhnA0AJbIlyyCPL7bKVEM2lVqbrXt7cyey+umkCXamuOgsWPVUKBMkXtMH8L\n5etLmD0getWIhTGhzOnDCk+gtIPfL4jMo9tkEuOCROQAXCci23VFscKcrkB+3X6h4wEOtA2APhOY\nB+wvC794o8/82ffjP79aVAi57rp3Wmzx+9pe9yMwoJuljAy2sc2tIMgdQGWVmOGBpQm3JqsidyzI\nJWG2kjnc7pDXK9pwYzXoKiqUqqrut90d+kQqRyv7MSZXR50HFqD/LI69h68b7P8Bjo3bPXOhNXGR\n9YCoemH6EkfCJxp2gIjzjWW+l2Hj2EsFQi8YXw==</SignatureValue></Signature></root>"
jobs:
buildForAllPlatformsWindows:
name: Build for ${{ matrix.targetPlatform }} on version ${{ matrix.unityVersion }}
runs-on: macos-latest
strategy:
fail-fast: false
matrix:
projectPath:
- test-project
unityVersion:
- 2020.3.24f1
targetPlatform:
- StandaloneOSX # Build a MacOS executable
steps:
###########################
# Checkout #
###########################
- uses: actions/checkout@v2
with:
lfs: true
###########################
# Cache #
###########################
- uses: actions/cache@v2
with:
path: ${{ matrix.projectPath }}/Library
key: Library-${{ matrix.projectPath }}-macos-${{ matrix.targetPlatform }}
restore-keys: |
Library-${{ matrix.projectPath }}-macos-
Library-
###########################
# Set Scripting Backend #
###########################
- name: Set Scripting Backend To il2cpp
run: |
mv -f "./test-project/ProjectSettings/ProjectSettingsIl2cpp.asset" "./test-project/ProjectSettings/ProjectSettings.asset"
###########################
# Build #
###########################
- uses: ./
env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
with:
projectPath: ${{ matrix.projectPath }}
unityVersion: ${{ matrix.unityVersion }}
targetPlatform: ${{ matrix.targetPlatform }}
customParameters: -profile SomeProfile -someBoolean -someValue exampleValue
# We use dirty build because we are replacing the default project settings file above
allowDirtyBuild: true
###########################
# Upload #
###########################
- uses: actions/upload-artifact@v2
with:
name: Build MacOS (${{ matrix.unityVersion }})
path: build
retention-days: 14

View File

@@ -0,0 +1,61 @@
name: Async Checks API
on:
workflow_dispatch:
inputs:
checksObject:
description: ''
required: false
default: ''
permissions:
checks: write
env:
GKE_ZONE: 'us-central1'
GKE_REGION: 'us-central1'
GKE_PROJECT: 'unitykubernetesbuilder'
GKE_CLUSTER: 'game-ci-github-pipelines'
GCP_LOGGING: true
GCP_PROJECT: unitykubernetesbuilder
GCP_LOG_FILE: ${{ github.workspace }}/orchestrator-logs.txt
# Commented out: Using LocalStack tests instead of real AWS
# AWS_REGION: eu-west-2
# AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
# AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
# AWS_DEFAULT_REGION: eu-west-2
# AWS_STACK_NAME: game-ci-github-pipelines
ORCHESTRATOR_BRANCH: ${{ github.ref }}
ORCHESTRATOR_DEBUG: true
ORCHESTRATOR_DEBUG_TREE: true
DEBUG: true
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
PROJECT_PATH: test-project
UNITY_VERSION: 2019.3.15f1
USE_IL2CPP: false
jobs:
asyncChecks:
name: Async Checks
if: github.event.event_type != 'pull_request_target'
runs-on: ubuntu-latest
steps:
- timeout-minutes: 180
env:
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
PROJECT_PATH: test-project
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GIT_PRIVATE_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TARGET_PLATFORM: StandaloneWindows64
orchestratorTests: true
versioning: None
ORCHESTRATOR_CLUSTER: local-docker
# Commented out: Using LocalStack tests instead of real AWS
# AWS_STACK_NAME: game-ci-github-pipelines
CHECKS_UPDATE: ${{ github.event.inputs.checksObject }}
run: |
git clone -b main https://github.com/game-ci/unity-builder
cd unity-builder
yarn
ls
yarn run cli -m checks-update

File diff suppressed because it is too large Load Diff

170
.github/workflows/release-cli.yml vendored Normal file
View File

@@ -0,0 +1,170 @@
name: Release CLI
on:
release:
types: [published]
workflow_dispatch:
inputs:
tag:
description: 'Release tag to build (e.g., v2.0.0). Uses latest release if empty.'
required: false
type: string
publish-npm:
description: 'Publish to npm'
required: false
default: false
type: boolean
concurrency:
group: ${{ github.workflow }}-${{ github.event.release.tag_name || inputs.tag || github.ref }}
cancel-in-progress: true
jobs:
build-binaries:
name: Build ${{ matrix.target }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- target: linux-x64
os: ubuntu-latest
pkg-target: node20-linux-x64
binary-name: game-ci-linux-x64
- target: linux-arm64
os: ubuntu-latest
pkg-target: node20-linux-arm64
binary-name: game-ci-linux-arm64
- target: macos-x64
os: macos-latest
pkg-target: node20-macos-x64
binary-name: game-ci-macos-x64
- target: macos-arm64
os: macos-latest
pkg-target: node20-macos-arm64
binary-name: game-ci-macos-arm64
- target: windows-x64
os: windows-latest
pkg-target: node20-win-x64
binary-name: game-ci-windows-x64.exe
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.release.tag_name || inputs.tag || github.ref }}
- uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Build TypeScript
run: yarn build
- name: Verify CLI before packaging
run: node lib/cli.js version
- name: Build standalone binary
run: npx pkg lib/cli.js --target ${{ matrix.pkg-target }} --output ${{ matrix.binary-name }} --compress GZip
- name: Verify standalone binary (non-cross-compiled)
if: |
(matrix.target == 'linux-x64' && runner.os == 'Linux') ||
(matrix.target == 'macos-arm64' && runner.os == 'macOS' && runner.arch == 'ARM64') ||
(matrix.target == 'macos-x64' && runner.os == 'macOS' && runner.arch == 'X64') ||
(matrix.target == 'windows-x64' && runner.os == 'Windows')
run: ./${{ matrix.binary-name }} version
shell: bash
- uses: actions/upload-artifact@v4
with:
name: binary-${{ matrix.target }}
path: ${{ matrix.binary-name }}
retention-days: 5
create-checksums-and-upload:
name: Checksums and release upload
needs: build-binaries
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/download-artifact@v4
with:
path: binaries
pattern: binary-*
merge-multiple: true
- name: List binaries
run: ls -la binaries/
- name: Generate SHA256 checksums
run: |
cd binaries
sha256sum game-ci-* > checksums.txt
echo "=== checksums.txt ==="
cat checksums.txt
- name: Determine release tag
id: tag
run: |
if [ "${{ github.event_name }}" = "release" ]; then
echo "tag=${{ github.event.release.tag_name }}" >> "$GITHUB_OUTPUT"
elif [ -n "${{ inputs.tag }}" ]; then
echo "tag=${{ inputs.tag }}" >> "$GITHUB_OUTPUT"
else
echo "No release tag available. Skipping upload."
echo "tag=" >> "$GITHUB_OUTPUT"
fi
- name: Upload binaries to release
if: steps.tag.outputs.tag != ''
env:
GH_TOKEN: ${{ github.token }}
run: |
cd binaries
for f in game-ci-* checksums.txt; do
echo "Uploading $f..."
gh release upload "${{ steps.tag.outputs.tag }}" "$f" \
--repo "${{ github.repository }}" \
--clobber
done
publish-npm:
name: Publish to npm
needs: build-binaries
runs-on: ubuntu-latest
if: >-
(github.event_name == 'release') || (github.event_name == 'workflow_dispatch' && inputs.publish-npm)
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.release.tag_name || inputs.tag || github.ref }}
- uses: actions/setup-node@v4
with:
node-version: '20'
registry-url: 'https://registry.npmjs.org'
- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Build
run: yarn build
- name: Run tests
run: yarn test
- name: Verify CLI
run: |
node lib/cli.js version
node lib/cli.js --help
- name: Publish to npm
run: npm publish --provenance --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

View File

@@ -1,77 +0,0 @@
name: Windows Builds
on:
push:
branches:
- main
env:
UNITY_LICENSE: "<?xml version=\"1.0\" encoding=\"UTF-8\"?><root>\n <License id=\"Terms\">\n <MachineBindings>\n <Binding Key=\"1\" Value=\"576562626572264761624c65526f7578\"/>\n <Binding Key=\"2\" Value=\"576562626572264761624c65526f7578\"/>\n </MachineBindings>\n <MachineID Value=\"D7nTUnjNAmtsUMcnoyrqkgIbYdM=\"/>\n <SerialHash Value=\"2033b8ac3e6faa3742ca9f0bfae44d18f2a96b80\"/>\n <Features>\n <Feature Value=\"33\"/>\n <Feature Value=\"1\"/>\n <Feature Value=\"12\"/>\n <Feature Value=\"2\"/>\n <Feature Value=\"24\"/>\n <Feature Value=\"3\"/>\n <Feature Value=\"36\"/>\n <Feature Value=\"17\"/>\n <Feature Value=\"19\"/>\n <Feature Value=\"62\"/>\n </Features>\n <DeveloperData Value=\"AQAAAEY0LUJHUlgtWEQ0RS1aQ1dWLUM1SlctR0RIQg==\"/>\n <SerialMasked Value=\"F4-BGRX-XD4E-ZCWV-C5JW-XXXX\"/>\n <StartDate Value=\"2021-02-08T00:00:00\"/>\n <UpdateDate Value=\"2021-02-09T00:34:57\"/>\n <InitialActivationDate Value=\"2021-02-08T00:34:56\"/>\n <LicenseVersion Value=\"6.x\"/>\n <ClientProvidedVersion Value=\"2018.4.30f1\"/>\n <AlwaysOnline Value=\"false\"/>\n <Entitlements>\n <Entitlement Ns=\"unity_editor\" Tag=\"UnityPersonal\" Type=\"EDITOR\" ValidTo=\"9999-12-31T00:00:00\"/>\n <Entitlement Ns=\"unity_editor\" Tag=\"DarkSkin\" Type=\"EDITOR_FEATURE\" ValidTo=\"9999-12-31T00:00:00\"/>\n </Entitlements>\n </License>\n<Signature xmlns=\"http://www.w3.org/2000/09/xmldsig#\"><SignedInfo><CanonicalizationMethod Algorithm=\"http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments\"/><SignatureMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#rsa-sha1\"/><Reference URI=\"#Terms\"><Transforms><Transform Algorithm=\"http://www.w3.org/2000/09/xmldsig#enveloped-signature\"/></Transforms><DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\"/><DigestValue>m0Db8UK+ktnOLJBtHybkfetpcKo=</DigestValue></Reference></SignedInfo><SignatureValue>o/pUbSQAukz7+ZYAWhnA0AJbIlyyCPL7bKVEM2lVqbrXt7cyey+umkCXamuOgsWPVUKBMkXtMH8L\n5etLmD0getWIhTGhzOnDCk+gtIPfL4jMo9tkEuOCROQAXCci23VFscKcrkB+3X6h4wEOtA2APhOY\nB+wvC794o8/82ffjP79aVAi57rp3Wmzx+9pe9yMwoJuljAy2sc2tIMgdQGWVmOGBpQm3JqsidyzI\nJWG2kjnc7pDXK9pwYzXoKiqUqqrut90d+kQqRyv7MSZXR50HFqD/LI69h68b7P8Bjo3bPXOhNXGR\n9YCoemH6EkfCJxp2gIjzjWW+l2Hj2EsFQi8YXw==</SignatureValue></Signature></root>"
jobs:
buildForAllPlatformsWindows:
name: Build for ${{ matrix.targetPlatform }} on version ${{ matrix.unityVersion }}
runs-on: windows-2019
strategy:
fail-fast: false
matrix:
projectPath:
- test-project
unityVersion:
- 2020.3.24f1
targetPlatform:
- StandaloneWindows64 # Build a Windows 64-bit standalone.
- StandaloneWindows # Build a Windows 32-bit standalone.
- WSAPlayer # Build a UWP App
- tvOS # Build an Apple TV XCode project
steps:
###########################
# Checkout #
###########################
- uses: actions/checkout@v2
with:
lfs: true
###########################
# Cache #
###########################
- uses: actions/cache@v2
with:
path: ${{ matrix.projectPath }}/Library
key: Library-${{ matrix.projectPath }}-windows-${{ matrix.targetPlatform }}
restore-keys: |
Library-${{ matrix.projectPath }}-windows-
Library-
###########################
# Set Scripting Backend #
###########################
- name: Set Scripting Backend To il2cpp
run: |
Move-Item -Path "./test-project/ProjectSettings/ProjectSettingsIl2cpp.asset" -Destination "./test-project/ProjectSettings/ProjectSettings.asset" -Force
###########################
# Build #
###########################
- uses: ./
env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
with:
projectPath: ${{ matrix.projectPath }}
unityVersion: ${{ matrix.unityVersion }}
targetPlatform: ${{ matrix.targetPlatform }}
customParameters: -profile SomeProfile -someBoolean -someValue exampleValue
allowDirtyBuild: true
# We use dirty build because we are replacing the default project settings file above
###########################
# Upload #
###########################
- uses: actions/upload-artifact@v2
with:
name: Build Windows (${{ matrix.unityVersion }})
path: build
retention-days: 14

2
.gitignore vendored
View File

@@ -5,3 +5,5 @@ lib/
.vsconfig
yarn-error.log
.orig
$LOG_FILE
temp/

1
.npmrc Normal file
View File

@@ -0,0 +1 @@
engine-strict=true

32
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,32 @@
{
"configurations": [
{
"name": "PowerShell Launch Current File",
"type": "PowerShell",
"request": "launch",
"script": "${file}",
"cwd": "${cwd}"
},
{
"type": "node",
"request": "launch",
"name": "Debug Jest Test",
"program": "${workspaceRoot}/node_modules/jest/bin/jest.js",
"args": [
"--collectCoverage=false",
"--colors",
"--config",
"${workspaceRoot}/jest.config.js",
"--runInBand",
"--runTestsByPath",
"${relativeFile}",
"--testPathPattern=${fileDirname}",
"--testTimeout=10000000"
],
"outputCapture": "std",
"internalConsoleOptions": "openOnSessionStart",
"envFile": "${workspaceRoot}/.env",
"skipFiles": ["${workspaceRoot}/../../node_modules/**/*", "<node_internals>/**/*"]
}
]
}

View File

@@ -1,4 +1,5 @@
{
"files.eol": "\n",
"god.tsconfig": "./tsconfig.json",
"yaml.customTags": [
"!And",

View File

@@ -25,7 +25,7 @@ Steps to be performed to submit a pull request:
#### Pull Request Prerequisites
You have [Node](https://nodejs.org/) installed at v12.2.0+ and [Yarn](https://yarnpkg.com/) at v1.18.0+.
You have [Node](https://nodejs.org/) installed at v18+ and [Yarn](https://yarnpkg.com/) at v1.22.0+.
Please note that commit hooks will run automatically to perform some tasks;
@@ -36,7 +36,8 @@ Please note that commit hooks will run automatically to perform some tasks;
#### Windows users
Make sure your editor and terminal that run the tests are set to `Powershell 7` or above with
`Git's Unix tools for Windows` installed. Some tests require you to be able to run `sh` and other unix commands.
`Git's Unix tools for Windows` installed. This is because some tests require you to be able to run `sh` and other
unix commands.
#### License

View File

@@ -10,8 +10,9 @@ Part of the <a href="https://game.ci">GameCI</a> open source project.
<br />
<br />
[![Actions status](https://github.com/game-ci/unity-builder/workflows/Builds/badge.svg?event=push&branch=main)](https://github.com/game-ci/unity-builder/actions?query=branch%3Amain+event%3Apush+workflow%3A%22Builds)
[![lgtm - code quality](https://img.shields.io/lgtm/grade/javascript/g/webbertakken/unity-builder.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/webbertakken/unity-builder/context:javascript)
[![Builds - Ubuntu](https://github.com/game-ci/unity-builder/actions/workflows/build-tests-ubuntu.yml/badge.svg)](https://github.com/game-ci/unity-builder/actions/workflows/build-tests-ubuntu.yml)
[![Builds - Windows](https://github.com/game-ci/unity-builder/actions/workflows/build-tests-windows.yml/badge.svg)](https://github.com/game-ci/unity-builder/actions/workflows/build-tests-windows.yml)
[![Builds - MacOS](https://github.com/game-ci/unity-builder/actions/workflows/build-tests-mac.yml/badge.svg)](https://github.com/game-ci/unity-builder/actions/workflows/build-tests-mac.yml)
[![codecov - test coverage](https://codecov.io/gh/game-ci/unity-builder/branch/master/graph/badge.svg)](https://codecov.io/gh/game-ci/unity-builder)
<br />
<br />

View File

@@ -9,7 +9,8 @@ inputs:
unityVersion:
required: false
default: 'auto'
description: 'Version of unity to use for building the project. Use "auto" to get from your ProjectSettings/ProjectVersion.txt'
description:
'Version of unity to use for building the project. Use "auto" to get from your ProjectSettings/ProjectVersion.txt'
customImage:
required: false
default: ''
@@ -17,11 +18,15 @@ inputs:
projectPath:
required: false
default: ''
description: 'Relative path to the project to be built.'
description: 'Path to the project to be built, relative to the repository root.'
buildProfile:
required: false
default: ''
description: 'Path to the build profile to activate, relative to the project root.'
buildName:
required: false
default: ''
description: 'Name of the build.'
description: 'Name of the build. Should not include a file extension.'
buildsPath:
required: false
default: ''
@@ -30,6 +35,14 @@ inputs:
required: false
default: ''
description: 'Path to a Namespace.Class.StaticMethod to run to perform the build.'
manualExit:
required: false
default: ''
description: 'Suppresses `-quit`. Exit your build method using `EditorApplication.Exit(0)` instead.'
enableGpu:
required: false
default: ''
description: 'Launches unity without specifying `-nographics`.'
customParameters:
required: false
default: ''
@@ -46,10 +59,12 @@ inputs:
required: false
default: ''
description: 'The android versionCode'
androidAppBundle:
androidExportType:
required: false
default: 'false'
description: 'Whether to build .aab instead of .apk'
default: 'androidPackage'
description:
'The android export type. Should be androidPackage for apk, androidAppBundle for aab, or androidStudioProject for
an android studio project.'
androidKeystoreName:
required: false
default: ''
@@ -74,106 +89,212 @@ inputs:
required: false
default: ''
description: 'The android target API level.'
androidSymbolType:
required: false
default: 'none'
description: 'The android symbol type to export. Should be "none", "public" or "debugging".'
sshAgent:
required: false
default: ''
description: 'SSH Agent path to forward to the container'
sshPublicKeysDirectoryPath:
required: false
default: ''
description: 'Path to a directory containing SSH public keys to forward to the container.'
gitPrivateToken:
required: false
default: ''
description: 'Github private token to pull from github'
description: '[Orchestrator] Github private token to pull from github'
githubOwner:
required: false
default: ''
description: '[Orchestrator] GitHub owner name or organization/team name'
runAsHostUser:
required: false
default: 'false'
description:
'Whether to run as a user that matches the host system or the default root container user. Only applicable to
Linux hosts and containers. This is useful for fixing permission errors on Self-Hosted runners.'
chownFilesTo:
required: false
default: ''
description: 'User and optionally group (user or user:group or uid:gid) to give ownership of the resulting build artifacts'
description:
'User and optionally group (user or user:group or uid:gid) to give ownership of the resulting build artifacts'
dockerCpuLimit:
required: false
default: ''
description: 'Number of CPU cores to assign the docker container. Defaults to all available cores on all platforms.'
dockerMemoryLimit:
required: false
default: ''
description:
'Amount of memory to assign the docker container. Defaults to 95% of total system memory rounded down to the
nearest megabyte on Linux and 80% on Windows. On unrecognized platforms, defaults to 75% of total system memory.
To manually specify a value, use the format <number><unit>, where unit is either m or g. ie: 512m = 512 megabytes'
dockerIsolationMode:
required: false
default: 'default'
description:
'Isolation mode to use for the docker container. Can be one of process, hyperv, or default. Default will pick the
default mode as described by Microsoft where server versions use process and desktop versions use hyperv. Only
applicable on Windows'
containerRegistryRepository:
required: false
default: 'unityci/editor'
description: 'Container registry and repository to pull image from. Only applicable if customImage is not set.'
containerRegistryImageVersion:
required: false
default: '3'
description: 'Container registry image version. Only applicable if customImage is not set.'
allowDirtyBuild:
required: false
default: ''
description: 'Allows the branch of the build to be dirty, and still generate the build.'
description: '[Orchestrator] Allows the branch of the build to be dirty, and still generate the build.'
postBuildSteps:
required: false
default: ''
description: 'run a post build job in yaml format with the keys image, secrets (name, value object array), command string'
description:
'[Orchestrator] run a post build job in yaml format with the keys image, secrets (name, value object array),
command string'
preBuildSteps:
required: false
default: ''
description: 'Run a pre build job after the repository setup but before the build job (in yaml format with the keys image, secrets (name, value object array), command line string)'
customJobHooks:
description:
'[Orchestrator] Run a pre build job after the repository setup but before the build job (in yaml format with the
keys image, secrets (name, value object array), command line string)'
containerHookFiles:
required: false
default: ''
description: 'Specify custom commands and trigger hooks (injects commands into jobs)'
description:
'[Orchestrator] Specify the names (by file name) of custom steps to run before or after orchestrator jobs, must
match a yaml step file inside your repo in the folder .game-ci/steps/'
customHookFiles:
required: false
default: ''
description:
'[Orchestrator] Specify the names (by file name) of custom hooks to run before or after orchestrator jobs, must
match a yaml step file inside your repo in the folder .game-ci/hooks/'
customCommandHooks:
required: false
default: ''
description: '[Orchestrator] Specify custom commands and trigger hooks (injects commands into jobs)'
customJob:
required: false
default: ''
description: 'Run a custom job instead of the standard build automation for cloud runner (in yaml format with the keys image, secrets (name, value object array), command line string)'
awsBaseStackName:
description:
'[Orchestrator] Run a custom job instead of the standard build automation for orchestrator (in yaml format with the
keys image, secrets (name, value object array), command line string)'
awsStackName:
default: 'game-ci'
required: false
description: 'The Cloud Formation stack name that must be setup before using this option.'
cloudRunnerCluster:
description: '[Orchestrator] The Cloud Formation stack name that must be setup before using this option.'
providerStrategy:
default: 'local'
required: false
description: 'Either local, k8s or aws can be used to run builds on a remote cluster. Additional parameters must be configured.'
cloudRunnerCpu:
description:
'[Orchestrator] Either local, k8s or aws can be used to run builds on a remote cluster. Additional parameters must
be configured.'
resourceTracking:
default: 'false'
required: false
description: '[Orchestrator] Enable resource tracking logs for disk usage and allocation summaries.'
containerCpu:
default: ''
required: false
description: 'Amount of CPU time to assign the remote build container'
cloudRunnerMemory:
description: '[Orchestrator] Amount of CPU time to assign the remote build container'
containerMemory:
default: ''
required: false
description: 'Amount of memory to assign the remote build container'
cachePushOverrideCommand:
default: ''
required: false
description: 'A command run every time a file is pushed to cache, formatted with input file path and remote cache path'
cachePullOverrideCommand:
default: ''
required: false
description: 'A command run every time before a file is being pulled from cache, formatted with request cache file and destination path'
description: '[Orchestrator] Amount of memory to assign the remote build container'
readInputFromOverrideList:
default: ''
required: false
description: 'Comma separated list of input value names to read from "input override command"'
description: '[Orchestrator] Comma separated list of input value names to read from "input override command"'
readInputOverrideCommand:
default: ''
required: false
description: 'Extend game ci by specifying a command to execute to pull input from external source e.g cloud provider secret managers'
description:
'[Orchestrator] Extend game ci by specifying a command to execute to pull input from external source e.g cloud
provider secret managers'
kubeConfig:
default: ''
required: false
description: 'Supply a base64 encoded kubernetes config to run builds on kubernetes and stream logs until completion.'
description:
'[Orchestrator] Supply a base64 encoded kubernetes config to run builds on kubernetes and stream logs until
completion.'
kubeVolume:
default: ''
required: false
description: 'Supply a Persistent Volume Claim name to use for the Unity build.'
description: '[Orchestrator] Supply a Persistent Volume Claim name to use for the Unity build.'
kubeStorageClass:
default: ''
required: false
description: 'Kubernetes storage class to use for cloud runner jobs, leave empty to install rook cluster.'
description:
'[Orchestrator] Kubernetes storage class to use for orchestrator jobs, leave empty to install rook cluster.'
kubeVolumeSize:
default: '5Gi'
required: false
description: 'Amount of disc space to assign the Kubernetes Persistent Volume'
description: '[Orchestrator] Amount of disc space to assign the Kubernetes Persistent Volume'
cacheKey:
default: ''
required: false
description: 'Cache key to indicate bucket for cache'
checkDependencyHealthOverride:
description: '[Orchestrator] Cache key to indicate bucket for cache'
watchToEnd:
default: 'true'
required: false
description:
'[Orchestrator] Whether or not to watch the build to the end. Can be used for especially long running jobs e.g
imports or self-hosted ephemeral runners.'
cacheUnityInstallationOnMac:
default: 'false'
required: false
description: 'Whether to cache the Unity hub and editor installation on MacOS'
unityHubVersionOnMac:
default: ''
required: false
description: 'Use to specify a way to check depdency services health to enable resilient self-starting jobs'
startDependenciesOverride:
description:
'The version of Unity Hub to install on MacOS (e.g. 3.4.0). Defaults to latest available on brew if empty string
or nothing is specified.'
unityLicensingServer:
default: ''
required: false
description: 'Use to specify a way to start depdency services health to enable resilient self-starting jobs'
description: 'The Unity licensing server address to use for activating Unity.'
dockerWorkspacePath:
default: '/github/workspace'
required: false
description:
'The path to mount the workspace inside the docker container. For windows, leave out the drive letter. For example
c:/github/workspace should be defined as /github/workspace'
skipActivation:
default: 'false'
required: false
description: 'Skip the activation/deactivation of Unity. This assumes Unity is already activated.'
cloneDepth:
default: '50'
required: false
description: '[Orchestrator] Specifies the depth of the git clone for the repository. Use 0 for full clone.'
orchestratorRepoName:
default: 'game-ci/unity-builder'
required: false
description:
'[Orchestrator] Specifies the repo for the unity builder. Useful if you forked the repo for testing, features, or
fixes.'
outputs:
volume:
description: 'The Persistent Volume (PV) where the build artifacts have been stored by Kubernetes'
buildVersion:
description: 'The generated version used for the Unity build'
androidVersionCode:
description: 'The generated versionCode used for the Android Unity build'
engineExitCode:
description:
'Returns the exit code from the build scripts. This code is 0 if the build was successful. If there was an error
during activation, the code is from the activation step. If activation is successful, the code is from the project
build step.'
branding:
icon: 'box'
color: 'gray-dark'
runs:
using: 'node12'
using: 'node20'
main: 'dist/index.js'

View File

@@ -1,10 +0,0 @@
{
"m_SettingKeys": [
"VR Device Disabled",
"VR Device User Alert"
],
"m_SettingValues": [
"False",
"False"
]
}

View File

@@ -1,709 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<LangVersion>latest</LangVersion>
<_TargetFrameworkDirectories>non_empty_path_generated_by_unity.rider.package</_TargetFrameworkDirectories>
<_FullFrameworkReferenceAssemblyPaths>non_empty_path_generated_by_unity.rider.package</_FullFrameworkReferenceAssemblyPaths>
<DisableHandlePackageFileConflicts>true</DisableHandlePackageFileConflicts>
</PropertyGroup>
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>10.0.20506</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<RootNamespace></RootNamespace>
<ProjectGuid>{B7F8614B-1EC2-9D3A-DA1C-4D279A867D74}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<AssemblyName>Assembly-CSharp-Editor</AssemblyName>
<TargetFrameworkVersion>v4.7.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<BaseDirectory>.</BaseDirectory>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>Temp\bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;UNITY_2019_2_11;UNITY_2019_2;UNITY_2019;UNITY_5_3_OR_NEWER;UNITY_5_4_OR_NEWER;UNITY_5_5_OR_NEWER;UNITY_5_6_OR_NEWER;UNITY_2017_1_OR_NEWER;UNITY_2017_2_OR_NEWER;UNITY_2017_3_OR_NEWER;UNITY_2017_4_OR_NEWER;UNITY_2018_1_OR_NEWER;UNITY_2018_2_OR_NEWER;UNITY_2018_3_OR_NEWER;UNITY_2018_4_OR_NEWER;UNITY_2019_1_OR_NEWER;UNITY_2019_2_OR_NEWER;PLATFORM_ARCH_64;UNITY_64;UNITY_INCLUDE_TESTS;ENABLE_AUDIO;ENABLE_CACHING;ENABLE_CLOTH;ENABLE_MICROPHONE;ENABLE_MULTIPLE_DISPLAYS;ENABLE_PHYSICS;ENABLE_TEXTURE_STREAMING;ENABLE_UNET;ENABLE_LZMA;ENABLE_UNITYEVENTS;ENABLE_WEBCAM;ENABLE_WWW;ENABLE_CLOUD_SERVICES_COLLAB;ENABLE_CLOUD_SERVICES_COLLAB_SOFTLOCKS;ENABLE_CLOUD_SERVICES_ADS;ENABLE_CLOUD_SERVICES_USE_WEBREQUEST;ENABLE_CLOUD_SERVICES_UNET;ENABLE_CLOUD_SERVICES_BUILD;ENABLE_CLOUD_LICENSE;ENABLE_EDITOR_HUB_LICENSE;ENABLE_WEBSOCKET_CLIENT;ENABLE_DIRECTOR_AUDIO;ENABLE_DIRECTOR_TEXTURE;ENABLE_MANAGED_JOBS;ENABLE_MANAGED_TRANSFORM_JOBS;ENABLE_MANAGED_ANIMATION_JOBS;ENABLE_MANAGED_AUDIO_JOBS;INCLUDE_DYNAMIC_GI;ENABLE_MONO_BDWGC;ENABLE_SCRIPTING_GC_WBARRIERS;PLATFORM_SUPPORTS_MONO;RENDER_SOFTWARE_CURSOR;ENABLE_VIDEO;PLATFORM_STANDALONE_WIN;PLATFORM_STANDALONE;UNITY_STANDALONE_WIN;UNITY_STANDALONE;ENABLE_RUNTIME_GI;ENABLE_MOVIES;ENABLE_NETWORK;ENABLE_CRUNCH_TEXTURE_COMPRESSION;ENABLE_UNITYWEBREQUEST;ENABLE_CLOUD_SERVICES;ENABLE_CLOUD_SERVICES_ANALYTICS;ENABLE_CLOUD_SERVICES_PURCHASING;ENABLE_CLOUD_SERVICES_CRASH_REPORTING;ENABLE_OUT_OF_PROCESS_CRASH_HANDLER;ENABLE_EVENT_QUEUE;ENABLE_CLUSTER_SYNC;ENABLE_CLUSTERINPUT;ENABLE_VR;ENABLE_AR;ENABLE_WEBSOCKET_HOST;ENABLE_MONO;NET_STANDARD_2_0;ENABLE_PROFILER;UNITY_ASSERTIONS;UNITY_EDITOR;UNITY_EDITOR_64;UNITY_EDITOR_WIN;ENABLE_UNITY_COLLECTIONS_CHECKS;ENABLE_BURST_AOT;UNITY_TEAM_LICENSE;ENABLE_CUSTOM_RENDER_TEXTURE;ENABLE_DIRECTOR;ENABLE_LOCALIZATION;ENABLE_SPRITES;ENABLE_TERRAIN;ENABLE_TILEMAP;ENABLE_TIMELINE;ENABLE_LEGACY_INPUT_MANAGER;NET_4_6;CSHARP_7_OR_LATER;CSHARP_7_3_OR_NEWER</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<NoWarn>0169</NoWarn>
<AllowUnsafeBlocks>False</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>Temp\bin\Release\</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<NoWarn>0169</NoWarn>
<AllowUnsafeBlocks>False</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup>
<NoConfig>true</NoConfig>
<NoStdLib>true</NoStdLib>
<AddAdditionalExplicitAssemblyReferences>false</AddAdditionalExplicitAssemblyReferences>
<ImplicitlyExpandNETStandardFacades>false</ImplicitlyExpandNETStandardFacades>
<ImplicitlyExpandDesignTimeFacades>false</ImplicitlyExpandDesignTimeFacades>
</PropertyGroup>
<ItemGroup>
<Reference Include="UnityEngine">
<HintPath>C:\Program Files\Unity\2019.2.11f1\Editor\Data\Managed/UnityEngine/UnityEngine.dll</HintPath>
</Reference>
<Reference Include="UnityEditor">
<HintPath>C:\Program Files\Unity\2019.2.11f1\Editor\Data\Managed/UnityEditor.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Assets\Editor\Builder.cs" />
<Compile Include="Assets\Editor\Input\ArgumentsParser.cs" />
<Compile Include="Assets\Editor\Reporting\StdOutReporter.cs" />
<Compile Include="Assets\Editor\System\ProcessExtensions.cs" />
<Compile Include="Assets\Editor\Versioning\VersionApplicator.cs" />
<Compile Include="Assets\Editor\Versioning\Git.cs" />
<Compile Include="Assets\Editor\Versioning\VersionGenerator.cs" />
<Compile Include="Assets\Editor\Versioning\GitException.cs" />
<Reference Include="UnityEditor.TestRunner">
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/ScriptAssemblies/UnityEditor.TestRunner.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.TestRunner">
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/ScriptAssemblies/UnityEngine.TestRunner.dll</HintPath>
</Reference>
<Reference Include="Unity.Timeline.Editor">
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/ScriptAssemblies/Unity.Timeline.Editor.dll</HintPath>
</Reference>
<Reference Include="com.unity.multiplayer-hlapi.Editor">
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/ScriptAssemblies/com.unity.multiplayer-hlapi.Editor.dll</HintPath>
</Reference>
<Reference Include="Unity.VSCode.Editor">
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/ScriptAssemblies/Unity.VSCode.Editor.dll</HintPath>
</Reference>
<Reference Include="Unity.TextMeshPro.Editor">
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/ScriptAssemblies/Unity.TextMeshPro.Editor.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UI">
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/ScriptAssemblies/UnityEngine.UI.dll</HintPath>
</Reference>
<Reference Include="Unity.Timeline">
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/ScriptAssemblies/Unity.Timeline.dll</HintPath>
</Reference>
<Reference Include="Unity.CollabProxy.Editor">
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/ScriptAssemblies/Unity.CollabProxy.Editor.dll</HintPath>
</Reference>
<Reference Include="com.unity.multiplayer-weaver.Editor">
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/ScriptAssemblies/com.unity.multiplayer-weaver.Editor.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.XR.LegacyInputHelpers">
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/ScriptAssemblies/UnityEngine.XR.LegacyInputHelpers.dll</HintPath>
</Reference>
<Reference Include="Unity.Rider.Editor">
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/ScriptAssemblies/Unity.Rider.Editor.dll</HintPath>
</Reference>
<Reference Include="Unity.2D.Sprite.Editor">
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/ScriptAssemblies/Unity.2D.Sprite.Editor.dll</HintPath>
</Reference>
<Reference Include="Unity.2D.Tilemap.Editor">
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/ScriptAssemblies/Unity.2D.Tilemap.Editor.dll</HintPath>
</Reference>
<Reference Include="UnityEditor.SpatialTracking">
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/ScriptAssemblies/UnityEditor.SpatialTracking.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.SpatialTracking">
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/ScriptAssemblies/UnityEngine.SpatialTracking.dll</HintPath>
</Reference>
<Reference Include="Unity.TextMeshPro">
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/ScriptAssemblies/Unity.TextMeshPro.dll</HintPath>
</Reference>
<Reference Include="Unity.Analytics.DataPrivacy">
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/ScriptAssemblies/Unity.Analytics.DataPrivacy.dll</HintPath>
</Reference>
<Reference Include="UnityEditor.XR.LegacyInputHelpers">
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/ScriptAssemblies/UnityEditor.XR.LegacyInputHelpers.dll</HintPath>
</Reference>
<Reference Include="UnityEditor.UI">
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/ScriptAssemblies/UnityEditor.UI.dll</HintPath>
</Reference>
<Reference Include="com.unity.multiplayer-hlapi.Runtime">
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/ScriptAssemblies/com.unity.multiplayer-hlapi.Runtime.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.AIModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.AIModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.ARModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.ARModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.AccessibilityModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.AccessibilityModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.AndroidJNIModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.AndroidJNIModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.AnimationModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.AnimationModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.AssetBundleModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.AssetBundleModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.AudioModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.AudioModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.ClothModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.ClothModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.ClusterInputModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.ClusterInputModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.ClusterRendererModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.ClusterRendererModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.CoreModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.CoreModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.CrashReportingModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.CrashReportingModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.DSPGraphModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.DSPGraphModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.DirectorModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.DirectorModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.FileSystemHttpModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.FileSystemHttpModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.GameCenterModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.GameCenterModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.GridModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.GridModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.HotReloadModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.HotReloadModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.IMGUIModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.IMGUIModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.ImageConversionModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.ImageConversionModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.InputModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.InputModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.InputLegacyModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.InputLegacyModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.JSONSerializeModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.JSONSerializeModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.LocalizationModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.LocalizationModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.ParticleSystemModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.ParticleSystemModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.PerformanceReportingModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.PerformanceReportingModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.PhysicsModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.PhysicsModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.Physics2DModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.Physics2DModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.ProfilerModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.ProfilerModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.ScreenCaptureModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.ScreenCaptureModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.SharedInternalsModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.SharedInternalsModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.SpriteMaskModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.SpriteMaskModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.SpriteShapeModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.SpriteShapeModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.StreamingModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.StreamingModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.SubstanceModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.SubstanceModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.TLSModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.TLSModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.TerrainModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.TerrainModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.TerrainPhysicsModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.TerrainPhysicsModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.TextCoreModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.TextCoreModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.TextRenderingModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.TextRenderingModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.TilemapModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.TilemapModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UIModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.UIModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UIElementsModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.UIElementsModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UNETModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.UNETModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UmbraModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.UmbraModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UnityAnalyticsModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.UnityAnalyticsModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UnityConnectModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.UnityConnectModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UnityTestProtocolModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.UnityTestProtocolModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UnityWebRequestModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.UnityWebRequestModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UnityWebRequestAssetBundleModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.UnityWebRequestAssetBundleModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UnityWebRequestAudioModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.UnityWebRequestAudioModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UnityWebRequestTextureModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.UnityWebRequestTextureModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.UnityWebRequestWWWModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.UnityWebRequestWWWModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.VFXModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.VFXModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.VRModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.VRModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.VehiclesModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.VehiclesModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.VideoModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.VideoModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.WindModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.WindModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.XRModule">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.XRModule.dll</HintPath>
</Reference>
<Reference Include="UnityEditor.VR">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/UnityExtensions/Unity/UnityVR/Editor/UnityEditor.VR.dll</HintPath>
</Reference>
<Reference Include="UnityEditor.Graphs">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEditor.Graphs.dll</HintPath>
</Reference>
<Reference Include="UnityEditor.WindowsStandalone.Extensions">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/PlaybackEngines/WindowsStandaloneSupport/UnityEditor.WindowsStandalone.Extensions.dll</HintPath>
</Reference>
<Reference Include="UnityEditor.WebGL.Extensions">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/PlaybackEngines/WebGLSupport/UnityEditor.WebGL.Extensions.dll</HintPath>
</Reference>
<Reference Include="UnityEditor.Android.Extensions">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/PlaybackEngines/AndroidPlayer/UnityEditor.Android.Extensions.dll</HintPath>
</Reference>
<Reference Include="UnityEditor.UWP.Extensions">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/PlaybackEngines/MetroSupport/UnityEditor.UWP.Extensions.dll</HintPath>
</Reference>
<Reference Include="UnityEditor.Advertisements">
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/PackageCache/com.unity.ads@2.0.8/Editor/UnityEditor.Advertisements.dll</HintPath>
</Reference>
<Reference Include="Unity.Analytics.Editor">
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/PackageCache/com.unity.analytics@3.3.2/Unity.Analytics.Editor.dll</HintPath>
</Reference>
<Reference Include="Unity.Analytics.StandardEvents">
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/PackageCache/com.unity.analytics@3.3.2/Unity.Analytics.StandardEvents.dll</HintPath>
</Reference>
<Reference Include="Unity.Analytics.Tracker">
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/PackageCache/com.unity.analytics@3.3.2/Unity.Analytics.Tracker.dll</HintPath>
</Reference>
<Reference Include="UnityEditor.Purchasing">
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/PackageCache/com.unity.purchasing@2.0.6/Editor/UnityEditor.Purchasing.dll</HintPath>
</Reference>
<Reference Include="nunit.framework">
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/PackageCache/com.unity.ext.nunit@1.0.0/net35/unity-custom/nunit.framework.dll</HintPath>
</Reference>
<Reference Include="mscorlib">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/mscorlib.dll</HintPath>
</Reference>
<Reference Include="System">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/System.dll</HintPath>
</Reference>
<Reference Include="System.Core">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/System.Core.dll</HintPath>
</Reference>
<Reference Include="System.Runtime.Serialization">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/System.Runtime.Serialization.dll</HintPath>
</Reference>
<Reference Include="System.Xml">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/System.Xml.dll</HintPath>
</Reference>
<Reference Include="System.Xml.Linq">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/System.Xml.Linq.dll</HintPath>
</Reference>
<Reference Include="System.Numerics">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/System.Numerics.dll</HintPath>
</Reference>
<Reference Include="System.Numerics.Vectors">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/System.Numerics.Vectors.dll</HintPath>
</Reference>
<Reference Include="System.Net.Http">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/System.Net.Http.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CSharp">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Microsoft.CSharp.dll</HintPath>
</Reference>
<Reference Include="System.Data">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/System.Data.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Win32.Primitives">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/Microsoft.Win32.Primitives.dll</HintPath>
</Reference>
<Reference Include="netstandard">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/netstandard.dll</HintPath>
</Reference>
<Reference Include="System.AppContext">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.AppContext.dll</HintPath>
</Reference>
<Reference Include="System.Collections.Concurrent">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Collections.Concurrent.dll</HintPath>
</Reference>
<Reference Include="System.Collections">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Collections.dll</HintPath>
</Reference>
<Reference Include="System.Collections.NonGeneric">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Collections.NonGeneric.dll</HintPath>
</Reference>
<Reference Include="System.Collections.Specialized">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Collections.Specialized.dll</HintPath>
</Reference>
<Reference Include="System.ComponentModel.Annotations">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.ComponentModel.Annotations.dll</HintPath>
</Reference>
<Reference Include="System.ComponentModel">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.ComponentModel.dll</HintPath>
</Reference>
<Reference Include="System.ComponentModel.EventBasedAsync">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.ComponentModel.EventBasedAsync.dll</HintPath>
</Reference>
<Reference Include="System.ComponentModel.Primitives">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.ComponentModel.Primitives.dll</HintPath>
</Reference>
<Reference Include="System.ComponentModel.TypeConverter">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.ComponentModel.TypeConverter.dll</HintPath>
</Reference>
<Reference Include="System.Console">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Console.dll</HintPath>
</Reference>
<Reference Include="System.Data.Common">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Data.Common.dll</HintPath>
</Reference>
<Reference Include="System.Diagnostics.Contracts">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Diagnostics.Contracts.dll</HintPath>
</Reference>
<Reference Include="System.Diagnostics.Debug">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Diagnostics.Debug.dll</HintPath>
</Reference>
<Reference Include="System.Diagnostics.FileVersionInfo">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Diagnostics.FileVersionInfo.dll</HintPath>
</Reference>
<Reference Include="System.Diagnostics.Process">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Diagnostics.Process.dll</HintPath>
</Reference>
<Reference Include="System.Diagnostics.StackTrace">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Diagnostics.StackTrace.dll</HintPath>
</Reference>
<Reference Include="System.Diagnostics.TextWriterTraceListener">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Diagnostics.TextWriterTraceListener.dll</HintPath>
</Reference>
<Reference Include="System.Diagnostics.Tools">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Diagnostics.Tools.dll</HintPath>
</Reference>
<Reference Include="System.Diagnostics.TraceSource">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Diagnostics.TraceSource.dll</HintPath>
</Reference>
<Reference Include="System.Drawing.Primitives">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Drawing.Primitives.dll</HintPath>
</Reference>
<Reference Include="System.Dynamic.Runtime">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Dynamic.Runtime.dll</HintPath>
</Reference>
<Reference Include="System.Globalization.Calendars">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Globalization.Calendars.dll</HintPath>
</Reference>
<Reference Include="System.Globalization">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Globalization.dll</HintPath>
</Reference>
<Reference Include="System.Globalization.Extensions">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Globalization.Extensions.dll</HintPath>
</Reference>
<Reference Include="System.IO.Compression.ZipFile">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.IO.Compression.ZipFile.dll</HintPath>
</Reference>
<Reference Include="System.IO">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.IO.dll</HintPath>
</Reference>
<Reference Include="System.IO.FileSystem">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.IO.FileSystem.dll</HintPath>
</Reference>
<Reference Include="System.IO.FileSystem.DriveInfo">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.IO.FileSystem.DriveInfo.dll</HintPath>
</Reference>
<Reference Include="System.IO.FileSystem.Primitives">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.IO.FileSystem.Primitives.dll</HintPath>
</Reference>
<Reference Include="System.IO.FileSystem.Watcher">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.IO.FileSystem.Watcher.dll</HintPath>
</Reference>
<Reference Include="System.IO.IsolatedStorage">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.IO.IsolatedStorage.dll</HintPath>
</Reference>
<Reference Include="System.IO.MemoryMappedFiles">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.IO.MemoryMappedFiles.dll</HintPath>
</Reference>
<Reference Include="System.IO.Pipes">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.IO.Pipes.dll</HintPath>
</Reference>
<Reference Include="System.IO.UnmanagedMemoryStream">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.IO.UnmanagedMemoryStream.dll</HintPath>
</Reference>
<Reference Include="System.Linq">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Linq.dll</HintPath>
</Reference>
<Reference Include="System.Linq.Expressions">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Linq.Expressions.dll</HintPath>
</Reference>
<Reference Include="System.Linq.Parallel">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Linq.Parallel.dll</HintPath>
</Reference>
<Reference Include="System.Linq.Queryable">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Linq.Queryable.dll</HintPath>
</Reference>
<Reference Include="System.Net.Http.Rtc">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Net.Http.Rtc.dll</HintPath>
</Reference>
<Reference Include="System.Net.NameResolution">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Net.NameResolution.dll</HintPath>
</Reference>
<Reference Include="System.Net.NetworkInformation">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Net.NetworkInformation.dll</HintPath>
</Reference>
<Reference Include="System.Net.Ping">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Net.Ping.dll</HintPath>
</Reference>
<Reference Include="System.Net.Primitives">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Net.Primitives.dll</HintPath>
</Reference>
<Reference Include="System.Net.Requests">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Net.Requests.dll</HintPath>
</Reference>
<Reference Include="System.Net.Security">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Net.Security.dll</HintPath>
</Reference>
<Reference Include="System.Net.Sockets">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Net.Sockets.dll</HintPath>
</Reference>
<Reference Include="System.Net.WebHeaderCollection">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Net.WebHeaderCollection.dll</HintPath>
</Reference>
<Reference Include="System.Net.WebSockets.Client">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Net.WebSockets.Client.dll</HintPath>
</Reference>
<Reference Include="System.Net.WebSockets">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Net.WebSockets.dll</HintPath>
</Reference>
<Reference Include="System.ObjectModel">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.ObjectModel.dll</HintPath>
</Reference>
<Reference Include="System.Reflection">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Reflection.dll</HintPath>
</Reference>
<Reference Include="System.Reflection.Emit">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Reflection.Emit.dll</HintPath>
</Reference>
<Reference Include="System.Reflection.Emit.ILGeneration">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Reflection.Emit.ILGeneration.dll</HintPath>
</Reference>
<Reference Include="System.Reflection.Emit.Lightweight">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Reflection.Emit.Lightweight.dll</HintPath>
</Reference>
<Reference Include="System.Reflection.Extensions">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Reflection.Extensions.dll</HintPath>
</Reference>
<Reference Include="System.Reflection.Primitives">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Reflection.Primitives.dll</HintPath>
</Reference>
<Reference Include="System.Resources.Reader">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Resources.Reader.dll</HintPath>
</Reference>
<Reference Include="System.Resources.ResourceManager">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Resources.ResourceManager.dll</HintPath>
</Reference>
<Reference Include="System.Resources.Writer">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Resources.Writer.dll</HintPath>
</Reference>
<Reference Include="System.Runtime.CompilerServices.VisualC">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Runtime.CompilerServices.VisualC.dll</HintPath>
</Reference>
<Reference Include="System.Runtime">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Runtime.dll</HintPath>
</Reference>
<Reference Include="System.Runtime.Extensions">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Runtime.Extensions.dll</HintPath>
</Reference>
<Reference Include="System.Runtime.Handles">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Runtime.Handles.dll</HintPath>
</Reference>
<Reference Include="System.Runtime.InteropServices">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Runtime.InteropServices.dll</HintPath>
</Reference>
<Reference Include="System.Runtime.InteropServices.RuntimeInformation">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Runtime.InteropServices.RuntimeInformation.dll</HintPath>
</Reference>
<Reference Include="System.Runtime.InteropServices.WindowsRuntime">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Runtime.InteropServices.WindowsRuntime.dll</HintPath>
</Reference>
<Reference Include="System.Runtime.Numerics">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Runtime.Numerics.dll</HintPath>
</Reference>
<Reference Include="System.Runtime.Serialization.Formatters">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Runtime.Serialization.Formatters.dll</HintPath>
</Reference>
<Reference Include="System.Runtime.Serialization.Json">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Runtime.Serialization.Json.dll</HintPath>
</Reference>
<Reference Include="System.Runtime.Serialization.Primitives">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Runtime.Serialization.Primitives.dll</HintPath>
</Reference>
<Reference Include="System.Runtime.Serialization.Xml">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Runtime.Serialization.Xml.dll</HintPath>
</Reference>
<Reference Include="System.Security.Claims">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Security.Claims.dll</HintPath>
</Reference>
<Reference Include="System.Security.Cryptography.Algorithms">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Security.Cryptography.Algorithms.dll</HintPath>
</Reference>
<Reference Include="System.Security.Cryptography.Csp">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Security.Cryptography.Csp.dll</HintPath>
</Reference>
<Reference Include="System.Security.Cryptography.Encoding">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Security.Cryptography.Encoding.dll</HintPath>
</Reference>
<Reference Include="System.Security.Cryptography.Primitives">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Security.Cryptography.Primitives.dll</HintPath>
</Reference>
<Reference Include="System.Security.Cryptography.X509Certificates">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Security.Cryptography.X509Certificates.dll</HintPath>
</Reference>
<Reference Include="System.Security.Principal">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Security.Principal.dll</HintPath>
</Reference>
<Reference Include="System.Security.SecureString">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Security.SecureString.dll</HintPath>
</Reference>
<Reference Include="System.ServiceModel.Duplex">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.ServiceModel.Duplex.dll</HintPath>
</Reference>
<Reference Include="System.ServiceModel.Http">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.ServiceModel.Http.dll</HintPath>
</Reference>
<Reference Include="System.ServiceModel.NetTcp">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.ServiceModel.NetTcp.dll</HintPath>
</Reference>
<Reference Include="System.ServiceModel.Primitives">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.ServiceModel.Primitives.dll</HintPath>
</Reference>
<Reference Include="System.ServiceModel.Security">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.ServiceModel.Security.dll</HintPath>
</Reference>
<Reference Include="System.Text.Encoding">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Text.Encoding.dll</HintPath>
</Reference>
<Reference Include="System.Text.Encoding.Extensions">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Text.Encoding.Extensions.dll</HintPath>
</Reference>
<Reference Include="System.Text.RegularExpressions">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Text.RegularExpressions.dll</HintPath>
</Reference>
<Reference Include="System.Threading">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Threading.dll</HintPath>
</Reference>
<Reference Include="System.Threading.Overlapped">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Threading.Overlapped.dll</HintPath>
</Reference>
<Reference Include="System.Threading.Tasks">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Threading.Tasks.dll</HintPath>
</Reference>
<Reference Include="System.Threading.Tasks.Parallel">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Threading.Tasks.Parallel.dll</HintPath>
</Reference>
<Reference Include="System.Threading.Thread">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Threading.Thread.dll</HintPath>
</Reference>
<Reference Include="System.Threading.ThreadPool">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Threading.ThreadPool.dll</HintPath>
</Reference>
<Reference Include="System.Threading.Timer">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Threading.Timer.dll</HintPath>
</Reference>
<Reference Include="System.ValueTuple">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.ValueTuple.dll</HintPath>
</Reference>
<Reference Include="System.Xml.ReaderWriter">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Xml.ReaderWriter.dll</HintPath>
</Reference>
<Reference Include="System.Xml.XDocument">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Xml.XDocument.dll</HintPath>
</Reference>
<Reference Include="System.Xml.XmlDocument">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Xml.XmlDocument.dll</HintPath>
</Reference>
<Reference Include="System.Xml.XmlSerializer">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Xml.XmlSerializer.dll</HintPath>
</Reference>
<Reference Include="System.Xml.XPath">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Xml.XPath.dll</HintPath>
</Reference>
<Reference Include="System.Xml.XPath.XDocument">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Xml.XPath.XDocument.dll</HintPath>
</Reference>
<Reference Include="UnityScript">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/unityscript/UnityScript.dll</HintPath>
</Reference>
<Reference Include="UnityScript.Lang">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/unityscript/UnityScript.Lang.dll</HintPath>
</Reference>
<Reference Include="Boo.Lang">
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/unityscript/Boo.Lang.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Content Include=".editorconfig" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@@ -6,6 +6,9 @@ using UnityBuilderAction.Reporting;
using UnityBuilderAction.Versioning;
using UnityEditor;
using UnityEditor.Build.Reporting;
#if UNITY_6000_0_OR_NEWER
using UnityEditor.Build.Profile;
#endif
using UnityEngine;
namespace UnityBuilderAction
@@ -17,36 +20,9 @@ namespace UnityBuilderAction
// Gather values from args
var options = ArgumentsParser.GetValidatedOptions();
// Gather values from project
var scenes = EditorBuildSettings.scenes.Where(scene => scene.enabled).Select(s => s.path).ToArray();
// Get all buildOptions from options
BuildOptions buildOptions = BuildOptions.None;
foreach (string buildOptionString in Enum.GetNames(typeof(BuildOptions))) {
if (options.ContainsKey(buildOptionString)) {
BuildOptions buildOptionEnum = (BuildOptions) Enum.Parse(typeof(BuildOptions), buildOptionString);
buildOptions |= buildOptionEnum;
}
}
// Define BuildPlayer Options
var buildPlayerOptions = new BuildPlayerOptions {
scenes = scenes,
locationPathName = options["customBuildPath"],
target = (BuildTarget) Enum.Parse(typeof(BuildTarget), options["buildTarget"]),
options = buildOptions
};
// Set version for this build
VersionApplicator.SetVersion(options["buildVersion"]);
// Apply Android settings
if (buildPlayerOptions.target == BuildTarget.Android)
{
VersionApplicator.SetAndroidVersionCode(options["androidVersionCode"]);
AndroidSettings.Apply(options);
}
// Execute default AddressableAsset content build, if the package is installed.
// Version defines would be the best solution here, but Unity 2018 doesn't support that,
// so we fall back to using reflection instead.
@@ -63,10 +39,85 @@ namespace UnityBuilderAction
}
catch (Exception e)
{
Debug.LogError($"Failed to run default addressables build:\n{e}");
Debug.LogError("Failed to run default addressables build:\n" + e);
}
}
// Get all buildOptions from options
BuildOptions buildOptions = BuildOptions.None;
foreach (string buildOptionString in Enum.GetNames(typeof(BuildOptions))) {
if (options.ContainsKey(buildOptionString)) {
BuildOptions buildOptionEnum = (BuildOptions) Enum.Parse(typeof(BuildOptions), buildOptionString);
buildOptions |= buildOptionEnum;
}
}
// Depending on whether the build is using a build profile, `buildPlayerOptions` will an instance
// of either `UnityEditor.BuildPlayerOptions` or `UnityEditor.BuildPlayerWithProfileOptions`
dynamic buildPlayerOptions;
if (options.TryGetValue("activeBuildProfile", out var buildProfilePath)) {
if (string.IsNullOrEmpty(buildProfilePath)) {
throw new Exception("`-activeBuildProfile` is set but with an empty value; this shouldn't happen");
}
#if UNITY_6000_0_OR_NEWER
// Load build profile from Assets folder
var buildProfile = AssetDatabase.LoadAssetAtPath<BuildProfile>(buildProfilePath)
?? throw new Exception("Build profile file not found at path: " + buildProfilePath);
// no need to set active profile, as already set by `-activeBuildProfile` CLI argument
// BuildProfile.SetActiveBuildProfile(buildProfile);
Debug.Log($"build profile: {buildProfile.name}");
// Define BuildPlayerWithProfileOptions
buildPlayerOptions = new BuildPlayerWithProfileOptions {
buildProfile = buildProfile,
locationPathName = options["customBuildPath"],
options = buildOptions,
};
#else // UNITY_6000_0_OR_NEWER
throw new Exception("Build profiles are not supported by this version of Unity (" + Application.unityVersion +")");
#endif // UNITY_6000_0_OR_NEWER
} else {
#if BUILD_PROFILE_LOADED
throw new Exception("Build profile's define symbol present; shouldn't happen");
#endif // BUILD_PROFILE_LOADED
// Gather values from project
var scenes = EditorBuildSettings.scenes.Where(scene => scene.enabled).Select(s => s.path).ToArray();
#if UNITY_2021_2_OR_NEWER
// Determine subtarget
StandaloneBuildSubtarget buildSubtarget;
if (!options.TryGetValue("standaloneBuildSubtarget", out var subtargetValue) || !Enum.TryParse(subtargetValue, out buildSubtarget)) {
buildSubtarget = default;
}
#endif
BuildTarget buildTarget = (BuildTarget) Enum.Parse(typeof(BuildTarget), options["buildTarget"]);
// Define BuildPlayerOptions
buildPlayerOptions = new BuildPlayerOptions {
scenes = scenes,
locationPathName = options["customBuildPath"],
target = buildTarget,
options = buildOptions,
#if UNITY_2021_2_OR_NEWER
subtarget = (int) buildSubtarget
#endif
};
// Apply Android settings
if (buildTarget == BuildTarget.Android) {
VersionApplicator.SetAndroidVersionCode(options["androidVersionCode"]);
AndroidSettings.Apply(options);
}
}
// Perform build
BuildReport buildReport = BuildPipeline.BuildPlayer(buildPlayerOptions);

View File

@@ -1,27 +1,150 @@
using System;
using System.Collections.Generic;
using UnityEditor;
using System.Reflection;
namespace UnityBuilderAction.Input
{
public class AndroidSettings
public static class AndroidSettings
{
public static void Apply(Dictionary<string, string> options)
{
EditorUserBuildSettings.buildAppBundle = options["customBuildPath"].EndsWith(".aab");
#if UNITY_2019_1_OR_NEWER
if (options.TryGetValue("androidKeystoreName", out string keystoreName) && !string.IsNullOrEmpty(keystoreName))
{
PlayerSettings.Android.useCustomKeystore = true;
PlayerSettings.Android.keystoreName = keystoreName;
}
if (options.TryGetValue("androidKeystorePass", out string keystorePass) && !string.IsNullOrEmpty(keystorePass))
#endif
// Can't use out variable declaration as Unity 2018 doesn't support it
string keystorePass;
if (options.TryGetValue("androidKeystorePass", out keystorePass) && !string.IsNullOrEmpty(keystorePass))
PlayerSettings.Android.keystorePass = keystorePass;
if (options.TryGetValue("androidKeyaliasName", out string keyaliasName) && !string.IsNullOrEmpty(keyaliasName))
string keyaliasName;
if (options.TryGetValue("androidKeyaliasName", out keyaliasName) && !string.IsNullOrEmpty(keyaliasName))
PlayerSettings.Android.keyaliasName = keyaliasName;
if (options.TryGetValue("androidKeyaliasPass", out string keyaliasPass) && !string.IsNullOrEmpty(keyaliasPass))
string keyaliasPass;
if (options.TryGetValue("androidKeyaliasPass", out keyaliasPass) && !string.IsNullOrEmpty(keyaliasPass))
PlayerSettings.Android.keyaliasPass = keyaliasPass;
if (options.TryGetValue("androidTargetSdkVersion", out string androidTargetSdkVersion) && !string.IsNullOrEmpty(androidTargetSdkVersion))
PlayerSettings.Android.targetSdkVersion = (AndroidSdkVersions) Enum.Parse(typeof(AndroidSdkVersions), androidTargetSdkVersion);
string androidTargetSdkVersion;
if (options.TryGetValue("androidTargetSdkVersion", out androidTargetSdkVersion) && !string.IsNullOrEmpty(androidTargetSdkVersion))
{
var targetSdkVersion = AndroidSdkVersions.AndroidApiLevelAuto;
try
{
targetSdkVersion =
(AndroidSdkVersions) Enum.Parse(typeof(AndroidSdkVersions), androidTargetSdkVersion);
}
catch
{
UnityEngine.Debug.Log("Failed to parse androidTargetSdkVersion! Fallback to AndroidApiLevelAuto");
}
PlayerSettings.Android.targetSdkVersion = targetSdkVersion;
}
string androidExportType;
if (options.TryGetValue("androidExportType", out androidExportType) && !string.IsNullOrEmpty(androidExportType))
{
// Only exists in 2018.3 and above
PropertyInfo buildAppBundle = typeof(EditorUserBuildSettings)
.GetProperty("buildAppBundle", BindingFlags.Public | BindingFlags.Static);
switch (androidExportType)
{
case "androidStudioProject":
EditorUserBuildSettings.exportAsGoogleAndroidProject = true;
if (buildAppBundle != null)
buildAppBundle.SetValue(null, false, null);
break;
case "androidAppBundle":
EditorUserBuildSettings.exportAsGoogleAndroidProject = false;
if (buildAppBundle != null)
buildAppBundle.SetValue(null, true, null);
break;
case "androidPackage":
EditorUserBuildSettings.exportAsGoogleAndroidProject = false;
if (buildAppBundle != null)
buildAppBundle.SetValue(null, false, null);
break;
}
}
string symbolType;
if (options.TryGetValue("androidSymbolType", out symbolType) && !string.IsNullOrEmpty(symbolType))
{
#if UNITY_6000_0_OR_NEWER
switch (symbolType)
{
case "public":
SetDebugSymbols("SymbolTable");
break;
case "debugging":
SetDebugSymbols("Full");
break;
case "none":
SetDebugSymbols("None");
break;
}
#elif UNITY_2021_1_OR_NEWER
switch (symbolType)
{
case "public":
EditorUserBuildSettings.androidCreateSymbols = AndroidCreateSymbols.Public;
break;
case "debugging":
EditorUserBuildSettings.androidCreateSymbols = AndroidCreateSymbols.Debugging;
break;
case "none":
EditorUserBuildSettings.androidCreateSymbols = AndroidCreateSymbols.Disabled;
break;
}
#elif UNITY_2019_2_OR_NEWER
switch (symbolType)
{
case "public":
case "debugging":
EditorUserBuildSettings.androidCreateSymbolsZip = true;
break;
case "none":
EditorUserBuildSettings.androidCreateSymbolsZip = false;
break;
}
#endif
}
}
#if UNITY_6000_0_OR_NEWER
private static void SetDebugSymbols(string enumValueName)
{
// UnityEditor.Android.UserBuildSettings and Unity.Android.Types.DebugSymbolLevel are part of the Unity Android module.
// Reflection is used here to ensure the code works even if the module is not installed.
var debugSymbolsType = Type.GetType("UnityEditor.Android.UserBuildSettings+DebugSymbols, UnityEditor.Android.Extensions");
if (debugSymbolsType == null)
{
return;
}
var levelProp = debugSymbolsType.GetProperty("level", BindingFlags.Static | BindingFlags.Public);
if (levelProp == null)
{
return;
}
var enumType = Type.GetType("Unity.Android.Types.DebugSymbolLevel, Unity.Android.Types");
if (enumType == null)
{
return;
}
if (!Enum.TryParse(enumType, enumValueName, false , out var enumValue))
{
return;
}
levelProp.SetValue(null, enumValue);
}
#endif
}
}

View File

@@ -12,33 +12,53 @@ namespace UnityBuilderAction.Input
public static Dictionary<string, string> GetValidatedOptions()
{
ParseCommandLineArguments(out var validatedOptions);
Dictionary<string, string> validatedOptions;
ParseCommandLineArguments(out validatedOptions);
if (!validatedOptions.TryGetValue("projectPath", out var projectPath)) {
string projectPath;
if (!validatedOptions.TryGetValue("projectPath", out projectPath)) {
Console.WriteLine("Missing argument -projectPath");
EditorApplication.Exit(110);
}
if (!validatedOptions.TryGetValue("buildTarget", out var buildTarget)) {
#if UNITY_6000_0_OR_NEWER
var buildProfileSupport = true;
#else
var buildProfileSupport = false;
#endif // UNITY_6000_0_OR_NEWER
string buildProfile;
if (buildProfileSupport && validatedOptions.TryGetValue("activeBuildProfile", out buildProfile)) {
if (validatedOptions.ContainsKey("buildTarget")) {
Console.WriteLine("Extra argument -buildTarget");
EditorApplication.Exit(122);
}
} else {
string buildTarget;
if (!validatedOptions.TryGetValue("buildTarget", out buildTarget)) {
Console.WriteLine("Missing argument -buildTarget");
EditorApplication.Exit(120);
}
if (!Enum.IsDefined(typeof(BuildTarget), buildTarget)) {
Console.WriteLine(buildTarget + " is not a defined " + typeof(BuildTarget).Name);
EditorApplication.Exit(121);
}
}
if (!validatedOptions.TryGetValue("customBuildPath", out var customBuildPath)) {
string customBuildPath;
if (!validatedOptions.TryGetValue("customBuildPath", out customBuildPath)) {
Console.WriteLine("Missing argument -customBuildPath");
EditorApplication.Exit(130);
}
const string defaultCustomBuildName = "TestBuild";
if (!validatedOptions.TryGetValue("customBuildName", out var customBuildName)) {
Console.WriteLine($"Missing argument -customBuildName, defaulting to {defaultCustomBuildName}.");
string customBuildName;
if (!validatedOptions.TryGetValue("customBuildName", out customBuildName)) {
Console.WriteLine("Missing argument -customBuildName, defaulting to" + defaultCustomBuildName);
validatedOptions.Add("customBuildName", defaultCustomBuildName);
} else if (customBuildName == "") {
Console.WriteLine($"Invalid argument -customBuildName, defaulting to {defaultCustomBuildName}.");
Console.WriteLine("Invalid argument -customBuildName, defaulting to" + defaultCustomBuildName);
validatedOptions.Add("customBuildName", defaultCustomBuildName);
}
@@ -51,11 +71,11 @@ namespace UnityBuilderAction.Input
string[] args = Environment.GetCommandLineArgs();
Console.WriteLine(
$"{EOL}" +
$"###########################{EOL}" +
$"# Parsing settings #{EOL}" +
$"###########################{EOL}" +
$"{EOL}"
EOL +
"###########################" + EOL +
"# Parsing settings #" + EOL +
"###########################" + EOL +
EOL
);
// Extract flags with optional values
@@ -72,7 +92,7 @@ namespace UnityBuilderAction.Input
string displayValue = secret ? "*HIDDEN*" : "\"" + value + "\"";
// Assign
Console.WriteLine($"Found flag \"{flag}\" with value {displayValue}.");
Console.WriteLine("Found flag \"" + flag + "\" with value " + displayValue);
providedArguments.Add(flag, value);
}
}

View File

@@ -0,0 +1,36 @@
using System;
using UnityEngine;
using UnityEditor;
namespace UnityBuilderAction.Reporting
{
[InitializeOnLoad]
static class CompileListener
{
static CompileListener()
{
if (Application.isBatchMode)
{
Application.logMessageReceived += Application_logMessageReceived;
}
}
private static void Application_logMessageReceived(string condition, string stackTrace, LogType type)
{
string prefix = "";
switch (type)
{
case LogType.Error:
prefix = "error";
break;
case LogType.Warning:
prefix = "warning";
break;
case LogType.Exception:
prefix = "error";
break;
}
Console.WriteLine(Environment.NewLine + "::" + prefix + "::" + condition + Environment.NewLine + stackTrace);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fad44373fb7b61a4bb584e2675795aca
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -11,16 +11,16 @@ namespace UnityBuilderAction.Reporting
public static void ReportSummary(BuildSummary summary)
{
Console.WriteLine(
$"{EOL}" +
$"###########################{EOL}" +
$"# Build results #{EOL}" +
$"###########################{EOL}" +
$"{EOL}" +
$"Duration: {summary.totalTime.ToString()}{EOL}" +
$"Warnings: {summary.totalWarnings.ToString()}{EOL}" +
$"Errors: {summary.totalErrors.ToString()}{EOL}" +
$"Size: {summary.totalSize.ToString()} bytes{EOL}" +
$"{EOL}"
EOL +
"###########################" + EOL +
"# Build results #" + EOL +
"###########################" + EOL +
EOL +
"Duration: " + summary.totalTime.ToString() + EOL +
"Warnings: " + summary.totalWarnings.ToString() + EOL +
"Errors: " + summary.totalErrors.ToString() + EOL +
"Size: " + summary.totalSize.ToString() + " bytes" + EOL +
EOL
);
}

View File

@@ -21,11 +21,11 @@ namespace UnityBuilderAction.Versioning
version = GetSemanticCommitVersion();
Console.WriteLine("Repository has a valid version tag.");
} else {
version = $"0.0.{GetTotalNumberOfCommits()}";
version = "0.0." + GetTotalNumberOfCommits();
Console.WriteLine("Repository does not have tags to base the version on.");
}
Console.WriteLine($"Version is {version}");
Console.WriteLine("Version is " + version);
return version;
}
@@ -106,7 +106,8 @@ namespace UnityBuilderAction.Versioning
using (var process = new System.Diagnostics.Process()) {
string workingDirectory = UnityEngine.Application.dataPath;
int exitCode = process.Run(application, arguments, workingDirectory, out string output, out string errors);
string output, errors;
int exitCode = process.Run(application, arguments, workingDirectory, out output, out errors);
if (exitCode != 0) { throw new GitException(exitCode, errors); }
return output;

View File

@@ -1,10 +0,0 @@
{
"m_SettingKeys": [
"VR Device Disabled",
"VR Device User Alert"
],
"m_SettingValues": [
"False",
"False"
]
}

View File

@@ -1,20 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Assembly-CSharp-Editor", "Assembly-CSharp-Editor.csproj", "{B7F8614B-1EC2-9D3A-DA1C-4D279A867D74}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B7F8614B-1EC2-9D3A-DA1C-4D279A867D74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B7F8614B-1EC2-9D3A-DA1C-4D279A867D74}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B7F8614B-1EC2-9D3A-DA1C-4D279A867D74}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B7F8614B-1EC2-9D3A-DA1C-4D279A867D74}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@@ -1,3 +0,0 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/UserDictionary/Words/=Untracked/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Versioning/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

351441
dist/index.js generated vendored

File diff suppressed because one or more lines are too long

2
dist/index.js.map generated vendored

File diff suppressed because one or more lines are too long

17094
dist/licenses.txt generated vendored

File diff suppressed because it is too large Load Diff

View File

@@ -1,28 +1,40 @@
#!/usr/bin/env bash
#
# Create directories for license activation
# Perform Activation
#
sudo mkdir /Library/Application\ Support/Unity
sudo chmod -R 777 /Library/Application\ Support/Unity
if [ "$SKIP_ACTIVATION" != "true" ]; then
UNITY_LICENSE_PATH="/Library/Application Support/Unity"
ACTIVATE_LICENSE_PATH="$ACTION_FOLDER/BlankProject"
mkdir -p "$ACTIVATE_LICENSE_PATH"
if [ ! -d "$UNITY_LICENSE_PATH" ]; then
echo "Creating Unity License Directory"
sudo mkdir -p "$UNITY_LICENSE_PATH"
sudo chmod -R 777 "$UNITY_LICENSE_PATH"
fi;
ACTIVATE_LICENSE_PATH="$ACTION_FOLDER/BlankProject"
mkdir -p "$ACTIVATE_LICENSE_PATH"
source $ACTION_FOLDER/platforms/mac/steps/activate.sh
else
echo "Skipping activation"
fi
#
# Run steps
# Run Build
#
source $ACTION_FOLDER/platforms/mac/steps/activate.sh
source $ACTION_FOLDER/platforms/mac/steps/build.sh
source $ACTION_FOLDER/platforms/mac/steps/return_license.sh
#
# Remove license activation directory
# License Cleanup
#
sudo rm -r /Library/Application\ Support/Unity
rm -r "$ACTIVATE_LICENSE_PATH"
if [ "$SKIP_ACTIVATION" != "true" ]; then
source $ACTION_FOLDER/platforms/mac/steps/return_license.sh
rm -r "$ACTIVATE_LICENSE_PATH"
fi
#
# Instructions for debugging
@@ -37,7 +49,7 @@ echo ""
echo "Please note that the exit code is not very descriptive."
echo "Most likely it will not help you solve the issue."
echo ""
echo "To find the reason for failure: please search for errors in the log above."
echo "To find the reason for failure: please search for errors in the log above and check for annotations in the summary view."
echo ""
fi;

View File

@@ -4,21 +4,69 @@
echo "Changing to \"$ACTIVATE_LICENSE_PATH\" directory."
pushd "$ACTIVATE_LICENSE_PATH"
echo "Requesting activation"
if [[ -n "$UNITY_SERIAL" && -n "$UNITY_EMAIL" && -n "$UNITY_PASSWORD" ]]; then
#
# SERIAL LICENSE MODE
#
# This will activate unity, using the serial activation process.
#
# Activate license
/Applications/Unity/Hub/Editor/$UNITY_VERSION/Unity.app/Contents/MacOS/Unity \
-logFile /dev/stdout \
-batchmode \
-nographics \
-quit \
-serial "$UNITY_SERIAL" \
-username "$UNITY_EMAIL" \
-password "$UNITY_PASSWORD" \
-projectPath "$ACTIVATE_LICENSE_PATH"
echo "Requesting activation"
# Store the exit code from the verify command
UNITY_EXIT_CODE=$?
# Activate license
/Applications/Unity/Hub/Editor/$UNITY_VERSION/Unity.app/Contents/MacOS/Unity \
-logFile - \
-batchmode \
-nographics \
-quit \
-serial "$UNITY_SERIAL" \
-username "$UNITY_EMAIL" \
-password "$UNITY_PASSWORD" \
-projectPath "$ACTIVATE_LICENSE_PATH"
# Store the exit code from the verify command
UNITY_EXIT_CODE=$?
elif [[ -n "$UNITY_LICENSING_SERVER" ]]; then
#
# Custom Unity License Server
#
echo "Adding licensing server config"
mkdir -p "$UNITY_LICENSE_PATH/config/"
cp "$ACTION_FOLDER/unity-config/services-config.json" "$UNITY_LICENSE_PATH/config/services-config.json"
/Applications/Unity/Hub/Editor/$UNITY_VERSION/Unity.app/Contents/Frameworks/UnityLicensingClient.app/Contents/MacOS/Unity.Licensing.Client \
--acquire-floating > license.txt
# Store the exit code from the verify command
UNITY_EXIT_CODE=$?
if [ $UNITY_EXIT_CODE -eq 0 ]; then
PARSEDFILE=$(grep -oE '\"[^"]*\"' < license.txt | tr -d '"')
export FLOATING_LICENSE
FLOATING_LICENSE=$(sed -n 2p <<< "$PARSEDFILE")
FLOATING_LICENSE_TIMEOUT=$(sed -n 4p <<< "$PARSEDFILE")
echo "Acquired floating license: \"$FLOATING_LICENSE\" with timeout $FLOATING_LICENSE_TIMEOUT"
fi
else
#
# NO LICENSE ACTIVATION STRATEGY MATCHED
#
# This will exit since no activation strategies could be matched.
#
echo "License activation strategy could not be determined."
echo ""
echo "Visit https://game.ci/docs/github/activation for more"
echo "details on how to set up one of the possible activation strategies."
echo "::error ::No valid license activation strategy could be determined. Make sure to provide UNITY_EMAIL, UNITY_PASSWORD, and either a UNITY_SERIAL \
or UNITY_LICENSE. Otherwise please use UNITY_LICENSING_SERVER. See more info at https://game.ci/docs/github/activation"
# Immediately exit as no UNITY_EXIT_CODE can be derived.
exit 1;
fi
#
# Display information about the result
@@ -30,6 +78,7 @@ else
# Activation failed so exit with the code from the license verification step
echo "Unclassified error occured while trying to activate license."
echo "Exit code was: $UNITY_EXIT_CODE"
echo "::error ::There was an error while trying to activate the Unity license."
exit $UNITY_EXIT_CODE
fi

View File

@@ -19,6 +19,23 @@ echo "Using build name \"$BUILD_NAME\"."
echo "Using build target \"$BUILD_TARGET\"."
#
# Display the build profile
#
if [ -z "$BUILD_PROFILE" ]; then
# User has not provided a build profile
#
echo "Doing a default \"$BUILD_TARGET\" platform build."
#
else
# User has provided a path to a build profile `.asset` file
#
echo "Using build profile \"$BUILD_PROFILE\" relative to \"$UNITY_PROJECT_PATH\"."
#
fi
#
# Display build path and file
#
@@ -76,8 +93,10 @@ fi
if [[ "$BUILD_TARGET" == "Android" && -n "$ANDROID_SDK_MANAGER_PARAMETERS" ]]; then
echo "Updating Android SDK with parameters: $ANDROID_SDK_MANAGER_PARAMETERS"
export JAVA_HOME="$(awk -F'=' '/JAVA_HOME=/{print $2}' /usr/bin/unity-editor.d/*)"
"$(awk -F'=' '/ANDROID_HOME=/{print $2}' /usr/bin/unity-editor.d/*)/tools/bin/sdkmanager" "$ANDROID_SDK_MANAGER_PARAMETERS"
ANDROID_INSTALL_LOCATION="/Applications/Unity/Hub/Editor/$UNITY_VERSION/PlaybackEngines/AndroidPlayer"
export JAVA_HOME="$ANDROID_INSTALL_LOCATION/OpenJDK"
export ANDROID_HOME="$ANDROID_INSTALL_LOCATION/SDK"
yes | "$ANDROID_HOME/tools/bin/sdkmanager" "$ANDROID_SDK_MANAGER_PARAMETERS"
echo "Updated Android SDK."
else
echo "Not updating Android SDK."
@@ -126,16 +145,17 @@ echo ""
# Reference: https://docs.unity3d.com/2019.3/Documentation/Manual/CommandLineArguments.html
/Applications/Unity/Hub/Editor/$UNITY_VERSION/Unity.app/Contents/MacOS/Unity \
-quit \
-logFile - \
$( [ "${MANUAL_EXIT}" == "true" ] || echo "-quit" ) \
-batchmode \
-nographics \
-username "$UNITY_EMAIL" \
-password "$UNITY_PASSWORD" \
$( [ "${ENABLE_GPU}" == "true" ] || echo "-nographics" ) \
-customBuildName "$BUILD_NAME" \
-projectPath "$UNITY_PROJECT_PATH" \
-buildTarget "$BUILD_TARGET" \
$( [ -z "$BUILD_PROFILE" ] && echo "-buildTarget $BUILD_TARGET") \
-customBuildTarget "$BUILD_TARGET" \
-customBuildPath "$CUSTOM_BUILD_PATH" \
-customBuildProfile "$BUILD_PROFILE" \
${BUILD_PROFILE:+-activeBuildProfile} ${BUILD_PROFILE:+"$BUILD_PROFILE"} \
-executeMethod "$BUILD_METHOD" \
-buildVersion "$VERSION" \
-androidVersionCode "$ANDROID_VERSION_CODE" \
@@ -144,8 +164,9 @@ echo ""
-androidKeyaliasName "$ANDROID_KEYALIAS_NAME" \
-androidKeyaliasPass "$ANDROID_KEYALIAS_PASS" \
-androidTargetSdkVersion "$ANDROID_TARGET_SDK_VERSION" \
$CUSTOM_PARAMETERS \
> "$UNITY_PROJECT_PATH/out.log" 2>&1
-androidExportType "$ANDROID_EXPORT_TYPE" \
-androidSymbolType "$ANDROID_SYMBOL_TYPE" \
$CUSTOM_PARAMETERS
# Catch exit code
BUILD_EXIT_CODE=$?

View File

@@ -4,15 +4,29 @@
echo "Changing to \"$ACTIVATE_LICENSE_PATH\" directory."
pushd "$ACTIVATE_LICENSE_PATH"
/Applications/Unity/Hub/Editor/$UNITY_VERSION/Unity.app/Contents/MacOS/Unity \
-logFile /dev/stdout \
-batchmode \
-nographics \
-quit \
-username "$UNITY_EMAIL" \
-password "$UNITY_PASSWORD" \
-returnlicense \
-projectPath "$ACTIVATE_LICENSE_PATH"
if [[ -n "$UNITY_LICENSING_SERVER" ]]; then
#
# Return any floating license used.
#
echo "Returning floating license: \"$FLOATING_LICENSE\""
/Applications/Unity/Hub/Editor/$UNITY_VERSION/Unity.app/Contents/Frameworks/UnityLicensingClient.app/Contents/MacOS/Unity.Licensing.Client \
--return-floating "$FLOATING_LICENSE"
elif [[ -n "$UNITY_SERIAL" ]]; then
#
# SERIAL LICENSE MODE
#
# This will return the license that is currently in use.
#
/Applications/Unity/Hub/Editor/$UNITY_VERSION/Unity.app/Contents/MacOS/Unity \
-logFile - \
-batchmode \
-nographics \
-quit \
-username "$UNITY_EMAIL" \
-password "$UNITY_PASSWORD" \
-returnlicense \
-projectPath "$ACTIVATE_LICENSE_PATH"
fi
# Return to previous working directory
popd

View File

@@ -1,45 +1,83 @@
#!/usr/bin/env bash
#
# Create directory for license activation
#
ACTIVATE_LICENSE_PATH="$GITHUB_WORKSPACE/_activate-license~"
mkdir -p "$ACTIVATE_LICENSE_PATH"
# Ensure machine ID is randomized for personal license activation
if [[ "$UNITY_SERIAL" = F* ]]; then
echo "Randomizing machine ID for personal license activation"
dbus-uuidgen > /etc/machine-id && mkdir -p /var/lib/dbus/ && ln -sf /etc/machine-id /var/lib/dbus/machine-id
fi
#
# Run steps
#
source /steps/set_gitcredential.sh
source /steps/activate.sh
source /steps/build.sh
source /steps/return_license.sh
#
# Remove license activation directory
# Prepare Android SDK, if needed
# We do this here to ensure it has root permissions
#
rm -r "$ACTIVATE_LICENSE_PATH"
fullProjectPath="$GITHUB_WORKSPACE/$PROJECT_PATH"
#
# Instructions for debugging
#
if [[ "$BUILD_TARGET" == "Android" ]]; then
export JAVA_HOME="$(awk -F'=' '/JAVA_HOME=/{print $2}' /usr/bin/unity-editor.d/*)"
ANDROID_HOME_DIRECTORY="$(awk -F'=' '/ANDROID_HOME=/{print $2}' /usr/bin/unity-editor.d/*)"
SDKMANAGER=$(find $ANDROID_HOME_DIRECTORY/cmdline-tools -name sdkmanager)
if [ -z "${SDKMANAGER}" ]
then
SDKMANAGER=$(find $ANDROID_HOME_DIRECTORY/tools/bin -name sdkmanager)
if [ -z "${SDKMANAGER}" ]
then
echo "No sdkmanager found"
exit 1
fi
fi
if [[ $BUILD_EXIT_CODE -gt 0 ]]; then
echo ""
echo "###########################"
echo "# Failure #"
echo "###########################"
echo ""
echo "Please note that the exit code is not very descriptive."
echo "Most likely it will not help you solve the issue."
echo ""
echo "To find the reason for failure: please search for errors in the log above."
echo ""
fi;
if [[ -n "$ANDROID_SDK_MANAGER_PARAMETERS" ]]; then
echo "Updating Android SDK with parameters: $ANDROID_SDK_MANAGER_PARAMETERS"
$SDKMANAGER "$ANDROID_SDK_MANAGER_PARAMETERS"
else
echo "Updating Android SDK with auto detected target API version"
# Read the line containing AndroidTargetSdkVersion from the file
targetAPILine=$(grep 'AndroidTargetSdkVersion' "$fullProjectPath/ProjectSettings/ProjectSettings.asset")
#
# Exit with code from the build step.
#
# Extract the number after the semicolon
targetAPI=$(echo "$targetAPILine" | cut -d':' -f2 | tr -d '[:space:]')
exit $BUILD_EXIT_CODE
$SDKMANAGER "platforms;android-$targetAPI"
fi
echo "Updated Android SDK."
else
echo "Not updating Android SDK."
fi
if [[ "$RUN_AS_HOST_USER" == "true" ]]; then
echo "Running as host user"
# Stop on error if we can't set up the user
set -e
# Get host user/group info so we create files with the correct ownership
USERNAME=$(stat -c '%U' "$fullProjectPath")
USERID=$(stat -c '%u' "$fullProjectPath")
GROUPNAME=$(stat -c '%G' "$fullProjectPath")
GROUPID=$(stat -c '%g' "$fullProjectPath")
groupadd -g $GROUPID $GROUPNAME
useradd -u $USERID -g $GROUPID $USERNAME
usermod -aG $GROUPNAME $USERNAME
mkdir -p "/home/$USERNAME"
chown $USERNAME:$GROUPNAME "/home/$USERNAME"
# Normally need root permissions to access when using su
chmod 777 /dev/stdout
chmod 777 /dev/stderr
# Don't stop on error when running our scripts as error handling is baked in
set +e
# Switch to the host user so we can create files with the correct ownership
su $USERNAME -c "$SHELL -c 'source /steps/runsteps.sh'"
else
echo "Running as root"
# Run as root
source /steps/runsteps.sh
fi
exit $?

View File

@@ -1,79 +1,85 @@
#!/usr/bin/env bash
# Run in ACTIVATE_LICENSE_PATH directory
echo "Changing to \"$ACTIVATE_LICENSE_PATH\" directory."
pushd "$ACTIVATE_LICENSE_PATH"
# if blankproject folder doesn't exist create it
if [ ! -d "/BlankProject" ]; then
mkdir /BlankProject
fi
# if blankproject folder doesn't exist create it
if [ ! -d "/BlankProject/Assets" ]; then
mkdir /BlankProject/Assets
fi
if [[ -n "$UNITY_LICENSE" ]] || [[ -n "$UNITY_LICENSE_FILE" ]]; then
if [[ -n "$UNITY_SERIAL" && -n "$UNITY_EMAIL" && -n "$UNITY_PASSWORD" ]]; then
#
# PERSONAL LICENSE MODE
# SERIAL LICENSE MODE
#
# This will activate Unity, using a license file
# This will activate unity, using the serial activation process.
#
# Note that this is the ONLY WAY for PERSONAL LICENSES in 2020.
# * See for more details: https://gitlab.com/gableroux/unity3d-gitlab-ci-example/issues/5#note_72815478
#
# The license file can be acquired using `webbertakken/request-manual-activation-file` action.
echo "Requesting activation (personal license)"
echo "Requesting activation"
# Set the license file path
FILE_PATH=UnityLicenseFile.ulf
# Loop the unity-editor call until the license is activated with exponential backoff and a maximum of 5 retries
retry_count=0
if [[ -n "$UNITY_LICENSE" ]]; then
# Copy license file from Github variables
echo "$UNITY_LICENSE" | tr -d '\r' > $FILE_PATH
elif [[ -n "$UNITY_LICENSE_FILE" ]]; then
# Copy license file from file system
cat "$UNITY_LICENSE_FILE" | tr -d '\r' > $FILE_PATH
fi
# Initialize delay to 15 seconds
delay=15
# Activate license
ACTIVATION_OUTPUT=$(unity-editor \
# Loop until UNITY_EXIT_CODE is 0 or retry count reaches 5
while [[ $retry_count -lt 5 ]]
do
# Activate license
unity-editor \
-logFile /dev/stdout \
-quit \
-manualLicenseFile $FILE_PATH)
-serial "$UNITY_SERIAL" \
-username "$UNITY_EMAIL" \
-password "$UNITY_PASSWORD" \
-projectPath "/BlankProject"
# Store the exit code from the verify command
UNITY_EXIT_CODE=$?
# Check if UNITY_EXIT_CODE is 0
if [[ $UNITY_EXIT_CODE -eq 0 ]]
then
echo "Activation successful"
break
else
# Increment retry count
((retry_count++))
echo "::warning ::Activation failed, attempting retry #$retry_count"
echo "Activation failed, retrying in $delay seconds..."
sleep $delay
# Double the delay for the next iteration
delay=$((delay * 2))
fi
done
if [[ $retry_count -eq 5 ]]
then
echo "Activation failed after 5 retries"
fi
elif [[ -n "$UNITY_LICENSING_SERVER" ]]; then
#
# Custom Unity License Server
#
echo "Adding licensing server config"
/opt/unity/Editor/Data/Resources/Licensing/Client/Unity.Licensing.Client --acquire-floating > license.txt #is this accessible in a env variable?
# Store the exit code from the verify command
UNITY_EXIT_CODE=$?
# The exit code for personal activation is always 1;
# Determine whether activation was successful.
#
# Successful output should include the following:
#
# "LICENSE SYSTEM [2020120 18:51:20] Next license update check is after 2019-11-25T18:23:38"
#
ACTIVATION_SUCCESSFUL=$(echo $ACTIVATION_OUTPUT | grep 'Next license update check is after' | wc -l)
# Set exit code to 0 if activation was successful
if [[ $ACTIVATION_SUCCESSFUL -eq 1 ]]; then
UNITY_EXIT_CODE=0
fi;
# Remove license file
rm -f $FILE_PATH
elif [[ -n "$UNITY_SERIAL" && -n "$UNITY_EMAIL" && -n "$UNITY_PASSWORD" ]]; then
#
# PROFESSIONAL (SERIAL) LICENSE MODE
#
# This will activate unity, using the activating process.
#
# Note: This is the preferred way for PROFESSIONAL LICENSES.
#
echo "Requesting activation (professional license)"
# Activate license
unity-editor \
-logFile /dev/stdout \
-quit \
-serial "$UNITY_SERIAL" \
-username "$UNITY_EMAIL" \
-password "$UNITY_PASSWORD"
# Store the exit code from the verify command
UNITY_EXIT_CODE=$?
if [ $UNITY_EXIT_CODE -eq 0 ]; then
PARSEDFILE=$(grep -oP '\".*?\"' < license.txt | tr -d '"')
export FLOATING_LICENSE
FLOATING_LICENSE=$(sed -n 2p <<< "$PARSEDFILE")
FLOATING_LICENSE_TIMEOUT=$(sed -n 4p <<< "$PARSEDFILE")
echo "Acquired floating license: \"$FLOATING_LICENSE\" with timeout $FLOATING_LICENSE_TIMEOUT"
fi
else
#
# NO LICENSE ACTIVATION STRATEGY MATCHED
@@ -82,10 +88,13 @@ else
#
echo "License activation strategy could not be determined."
echo ""
echo "Visit https://game.ci/docs/github/getting-started for more"
echo "Visit https://game.ci/docs/github/activation for more"
echo "details on how to set up one of the possible activation strategies."
# Immediately exit as no UNITY_EXIT_CODE can be derrived.
echo "::error ::No valid license activation strategy could be determined. Make sure to provide UNITY_EMAIL, UNITY_PASSWORD, and either a UNITY_SERIAL \
or UNITY_LICENSE. Otherwise please use UNITY_LICENSING_SERVER. See more info at https://game.ci/docs/github/activation"
# Immediately exit as no UNITY_EXIT_CODE can be derived.
exit 1;
fi
@@ -100,8 +109,6 @@ else
# Activation failed so exit with the code from the license verification step
echo "Unclassified error occured while trying to activate license."
echo "Exit code was: $UNITY_EXIT_CODE"
echo "::error ::There was an error while trying to activate the Unity license."
exit $UNITY_EXIT_CODE
fi
# Return to previous working directory
popd

View File

@@ -19,6 +19,22 @@ echo "Using build name \"$BUILD_NAME\"."
echo "Using build target \"$BUILD_TARGET\"."
#
# Display the build profile
#
if [ -z "$BUILD_PROFILE" ]; then
# User has not provided a build profile
#
echo "Doing a default \"$BUILD_TARGET\" platform build."
#
else
# User has provided a path to a build profile `.asset` file
#
echo "Using build profile \"$BUILD_PROFILE\" relative to \"$UNITY_PROJECT_PATH\"."
#
fi
#
# Display build path and file
#
@@ -62,27 +78,6 @@ else
#
fi
#
# Prepare Android keystore and SDK, if needed
#
if [[ "$BUILD_TARGET" == "Android" && -n "$ANDROID_KEYSTORE_NAME" && -n "$ANDROID_KEYSTORE_BASE64" ]]; then
echo "Creating Android keystore."
echo "$ANDROID_KEYSTORE_BASE64" | base64 --decode > "$UNITY_PROJECT_PATH/$ANDROID_KEYSTORE_NAME"
echo "Created Android keystore."
else
echo "Not creating Android keystore."
fi
if [[ "$BUILD_TARGET" == "Android" && -n "$ANDROID_SDK_MANAGER_PARAMETERS" ]]; then
echo "Updating Android SDK with parameters: $ANDROID_SDK_MANAGER_PARAMETERS"
export JAVA_HOME="$(awk -F'=' '/JAVA_HOME=/{print $2}' /usr/bin/unity-editor.d/*)"
"$(awk -F'=' '/ANDROID_HOME=/{print $2}' /usr/bin/unity-editor.d/*)/tools/bin/sdkmanager" "$ANDROID_SDK_MANAGER_PARAMETERS"
echo "Updated Android SDK."
else
echo "Not updating Android SDK."
fi
#
# Pre-build debug information
#
@@ -127,12 +122,14 @@ echo ""
unity-editor \
-logfile /dev/stdout \
-quit \
$( [ "${MANUAL_EXIT}" == "true" ] || echo "-quit" ) \
-customBuildName "$BUILD_NAME" \
-projectPath "$UNITY_PROJECT_PATH" \
-buildTarget "$BUILD_TARGET" \
$( [ -z "$BUILD_PROFILE" ] && echo "-buildTarget $BUILD_TARGET" ) \
-customBuildTarget "$BUILD_TARGET" \
-customBuildPath "$CUSTOM_BUILD_PATH" \
-customBuildProfile "$BUILD_PROFILE" \
${BUILD_PROFILE:+-activeBuildProfile} ${BUILD_PROFILE:+"$BUILD_PROFILE"} \
-executeMethod "$BUILD_METHOD" \
-buildVersion "$VERSION" \
-androidVersionCode "$ANDROID_VERSION_CODE" \
@@ -141,6 +138,8 @@ unity-editor \
-androidKeyaliasName "$ANDROID_KEYALIAS_NAME" \
-androidKeyaliasPass "$ANDROID_KEYALIAS_PASS" \
-androidTargetSdkVersion "$ANDROID_TARGET_SDK_VERSION" \
-androidExportType "$ANDROID_EXPORT_TYPE" \
-androidSymbolType "$ANDROID_SYMBOL_TYPE" \
$CUSTOM_PARAMETERS
# Catch exit code
@@ -159,6 +158,7 @@ fi
# Make a given user owner of all artifacts
if [[ -n "$CHOWN_FILES_TO" ]]; then
echo "Changing ownership of files to $CHOWN_FILES_TO for $BUILD_PATH_FULL and $UNITY_PROJECT_PATH"
chown -R "$CHOWN_FILES_TO" "$BUILD_PATH_FULL"
chown -R "$CHOWN_FILES_TO" "$UNITY_PROJECT_PATH"
fi

View File

@@ -1,20 +1,22 @@
#!/usr/bin/env bash
# Run in ACTIVATE_LICENSE_PATH directory
echo "Changing to \"$ACTIVATE_LICENSE_PATH\" directory."
pushd "$ACTIVATE_LICENSE_PATH"
if [[ -n "$UNITY_SERIAL" ]]; then
if [[ -n "$UNITY_LICENSING_SERVER" ]]; then
#
# PROFESSIONAL (SERIAL) LICENSE MODE
# Return any floating license used.
#
echo "Returning floating license: \"$FLOATING_LICENSE\""
/opt/unity/Editor/Data/Resources/Licensing/Client/Unity.Licensing.Client --return-floating "$FLOATING_LICENSE"
elif [[ -n "$UNITY_SERIAL" ]]; then
#
# SERIAL LICENSE MODE
#
# This will return the license that is currently in use.
#
unity-editor \
-logFile /dev/stdout \
-quit \
-returnlicense
-returnlicense \
-username "$UNITY_EMAIL" \
-password "$UNITY_PASSWORD" \
-projectPath "/BlankProject"
fi
# Return to previous working directory
popd

48
dist/platforms/ubuntu/steps/runsteps.sh vendored Normal file
View File

@@ -0,0 +1,48 @@
#!/usr/bin/env bash
#
# Run steps
#
source /steps/set_extra_git_configs.sh
source /steps/set_gitcredential.sh
if [ "$SKIP_ACTIVATION" != "true" ]; then
source /steps/activate.sh
# If we didn't activate successfully, exit with the exit code from the activation step.
if [[ $UNITY_EXIT_CODE -ne 0 ]]; then
exit $UNITY_EXIT_CODE
fi
else
echo "Skipping activation"
fi
source /steps/build.sh
if [ "$SKIP_ACTIVATION" != "true" ]; then
source /steps/return_license.sh
fi
#
# Instructions for debugging
#
if [[ $BUILD_EXIT_CODE -gt 0 ]]; then
echo ""
echo "###########################"
echo "# Failure #"
echo "###########################"
echo ""
echo "Please note that the exit code is not very descriptive."
echo "Most likely it will not help you solve the issue."
echo ""
echo "To find the reason for failure: please search for errors in the log above and check for annotations in the summary view."
echo ""
fi;
#
# Exit with code from the build step.
#
# Exiting su
exit $BUILD_EXIT_CODE

View File

@@ -0,0 +1,29 @@
#!/usr/bin/env bash
if [ -z "${GIT_CONFIG_EXTENSIONS}" ]
then
echo "GIT_CONFIG_EXTENSIONS unset skipping"
else
echo "GIT_CONFIG_EXTENSIONS is set configuring extra git configs"
IFS=$'\n'
for config in $(echo "${GIT_CONFIG_EXTENSIONS}" | sed 's/\(.*\)=\(.*\)/"\1" "\2"/g'); do
if [[ $config =~ \"([^\"]+)\"\ \"([^\"]+)\" ]]; then
key="${BASH_REMATCH[1]}"
value="${BASH_REMATCH[2]}"
else
echo "Error parsing config: $config"
exit 1
fi
echo "Adding extra git config: \"$key\" = \"$value\""
git config --global --add "$key" "$value"
done
unset IFS
fi
echo "---------- git config --list -------------"
git config --list
echo "---------- git config --list --show-origin -------------"
git config --list --show-origin

View File

@@ -9,8 +9,8 @@ else
git config --global credential.helper store
git config --global --replace-all url."https://token:$GIT_PRIVATE_TOKEN@github.com/".insteadOf ssh://git@github.com/
git config --global --add url."https://token:$GIT_PRIVATE_TOKEN@github.com/".insteadOf git@github.com
git config --global --add url."https://token:$GIT_PRIVATE_TOKEN@github.com/".insteadOf "https://github.com/"
git config --global url."https://ssh:$GIT_PRIVATE_TOKEN@github.com/".insteadOf "ssh://git@github.com/"
git config --global url."https://git:$GIT_PRIVATE_TOKEN@github.com/".insteadOf "git@github.com:"

View File

@@ -1,7 +1,93 @@
# Activates Unity
& "C:\Program Files\Unity\Hub\Editor\$Env:UNITY_VERSION\Editor\Unity.exe" -batchmode -quit -nographics `
-username $Env:UNITY_EMAIL `
-password $Env:UNITY_PASSWORD `
-serial $Env:UNITY_SERIAL `
-projectPath "c:/BlankProject" `
-logfile | Out-Host
Write-Output ""
Write-Output "###########################"
Write-Output "# Activating #"
Write-Output "###########################"
Write-Output ""
if ( ($null -ne ${env:UNITY_SERIAL}) -and ($null -ne ${env:UNITY_EMAIL}) -and ($null -ne ${env:UNITY_PASSWORD}) )
{
#
# SERIAL LICENSE MODE
#
# This will activate unity, using the serial activation process.
#
Write-Output "Requesting activation"
$ACTIVATION_OUTPUT = Start-Process -FilePath "$Env:UNITY_PATH/Editor/Unity.exe" `
-NoNewWindow `
-PassThru `
-ArgumentList "-batchmode `
-quit `
-nographics `
-username $Env:UNITY_EMAIL `
-password $Env:UNITY_PASSWORD `
-serial $Env:UNITY_SERIAL `
-projectPath c:/BlankProject `
-logfile -"
# Cache the handle so exit code works properly
# https://stackoverflow.com/questions/10262231/obtaining-exitcode-using-start-process-and-waitforexit-instead-of-wait
$unityHandle = $ACTIVATION_OUTPUT.Handle
while ($true) {
if ($ACTIVATION_OUTPUT.HasExited) {
$ACTIVATION_EXIT_CODE = $ACTIVATION_OUTPUT.ExitCode
# Display results
if ($ACTIVATION_EXIT_CODE -eq 0)
{
Write-Output "Activation Succeeded"
} else
{
Write-Output "Activation failed, with exit code $ACTIVATION_EXIT_CODE"
}
break
}
Start-Sleep -Seconds 3
}
}
elseif( ($null -ne ${env:UNITY_LICENSING_SERVER}))
{
#
# Custom Unity License Server
#
Write-Output "Adding licensing server config"
$ACTIVATION_OUTPUT = Start-Process -FilePath "$Env:UNITY_PATH\Editor\Data\Resources\Licensing\Client\Unity.Licensing.Client.exe" `
-ArgumentList "--acquire-floating" `
-NoNewWindow `
-PassThru `
-Wait `
-RedirectStandardOutput "license.txt"
$PARSEDFILE = (Get-Content "license.txt" | Select-String -AllMatches -Pattern '\".*?\"' | ForEach-Object { $_.Matches.Value }) -replace '"'
$env:FLOATING_LICENSE = $PARSEDFILE[1]
$FLOATING_LICENSE_TIMEOUT = $PARSEDFILE[3]
Write-Output "Acquired floating license: ""$env:FLOATING_LICENSE"" with timeout $FLOATING_LICENSE_TIMEOUT"
# Store the exit code from the verify command
$ACTIVATION_EXIT_CODE = $ACTIVATION_OUTPUT.ExitCode
}
else
{
#
# NO LICENSE ACTIVATION STRATEGY MATCHED
#
# This will exit since no activation strategies could be matched.
#
Write-Output "License activation strategy could not be determined."
Write-Output ""
Write-Output "Visit https://game.ci/docs/github/activation for more"
Write-Output "details on how to set up one of the possible activation strategies."
Write-Output "::error ::No valid license activation strategy could be determined. Make sure to provide UNITY_EMAIL, UNITY_PASSWORD, and either a UNITY_SERIAL \
or UNITY_LICENSE. See more info at https://game.ci/docs/github/activation"
$ACTIVATION_EXIT_CODE = 1;
}

View File

@@ -16,6 +16,25 @@ Write-Output "$('Using build name "')$($Env:BUILD_NAME)$('".')"
Write-Output "$('Using build target "')$($Env:BUILD_TARGET)$('".')"
#
# Display the build profile
#
if ($Env:BUILD_PROFILE)
{
# User has provided a path to a build profile `.asset` file
#
Write-Output "$('Using build profile "')$($Env:BUILD_PROFILE)$('" relative to "')$($Env:UNITY_PROJECT_PATH)$('".')"
#
}
else
{
# User has not provided a build profile
#
Write-Output "$('Doing a default "')$($Env:BUILD_TARGET)$('" platform build.')"
#
}
#
# Display build path and file
#
@@ -66,6 +85,26 @@ else
Get-ChildItem -Path $Env:UNITY_PROJECT_PATH\Assets\Editor -Recurse
}
if ( "$Env:BUILD_TARGET" -eq "Android" -and -not ([string]::IsNullOrEmpty("$Env:ANDROID_KEYSTORE_BASE64")) )
{
Write-Output "Creating Android keystore."
# Write to consistent location as Windows Unity seems to have issues with pwd and can't find the keystore
$keystorePath = "C:/android.keystore"
[System.IO.File]::WriteAllBytes($keystorePath, [System.Convert]::FromBase64String($Env:ANDROID_KEYSTORE_BASE64))
# Ensure the project settings are pointed at the correct path
$unitySettingsPath = "$Env:UNITY_PROJECT_PATH\ProjectSettings\ProjectSettings.asset"
$fileContent = Get-Content -Path "$unitySettingsPath"
$fileContent = $fileContent -replace "AndroidKeystoreName:\s+.*", "AndroidKeystoreName: $keystorePath"
$fileContent | Set-Content -Path "$unitySettingsPath"
Write-Output "Created Android keystore."
}
else {
Write-Output "Not creating Android keystore."
}
#
# Pre-build debug information
#
@@ -109,39 +148,84 @@ Write-Output "# Building project #"
Write-Output "###########################"
Write-Output ""
& "C:\Program Files\Unity\Hub\Editor\$Env:UNITY_VERSION\Editor\Unity.exe" -quit -batchmode -nographics `
-projectPath $Env:UNITY_PROJECT_PATH `
-executeMethod $Env:BUILD_METHOD `
-buildTarget $Env:BUILD_TARGET `
-customBuildTarget $Env:BUILD_TARGET `
-customBuildPath $Env:CUSTOM_BUILD_PATH `
-buildVersion $Env:VERSION `
$Env:CUSTOM_PARAMETERS `
-logfile | Out-Host
$unityGraphics = "-nographics"
# Catch exit code
$Env:BUILD_EXIT_CODE=$?
# Display results
if ($Env:BUILD_EXIT_CODE -eq 0)
if ($LLVMPIPE_INSTALLED -eq "true")
{
Write-Output "Build Succeeded!"
} else
{
Write-Output "$('Build failed, with exit code ')$($Env:BUILD_EXIT_CODE)$('"')"
$unityGraphics = "-force-opengl"
}
# TODO: Determine if we need to set permissions on any files
# If $Env:CUSTOM_PARAMETERS contains spaces and is passed directly on the command line to Unity, powershell will wrap it
# in double quotes. To avoid this, parse $Env:CUSTOM_PARAMETERS into an array, while respecting any quotations within the string.
$_, $customParametersArray = Invoke-Expression('Write-Output -- "" ' + $Env:CUSTOM_PARAMETERS)
$unityArgs = @(
"-quit",
"-batchmode",
$unityGraphics,
"-silent-crashes",
"-customBuildName", "`"$Env:BUILD_NAME`"",
"-projectPath", "`"$Env:UNITY_PROJECT_PATH`"",
"-executeMethod", "`"$Env:BUILD_METHOD`"",
"-customBuildTarget", "`"$Env:BUILD_TARGET`"",
"-customBuildPath", "`"$Env:CUSTOM_BUILD_PATH`"",
"-customBuildProfile", "`"$Env:BUILD_PROFILE`"",
"-buildVersion", "`"$Env:VERSION`"",
"-androidVersionCode", "`"$Env:ANDROID_VERSION_CODE`"",
"-androidKeystorePass", "`"$Env:ANDROID_KEYSTORE_PASS`"",
"-androidKeyaliasName", "`"$Env:ANDROID_KEYALIAS_NAME`"",
"-androidKeyaliasPass", "`"$Env:ANDROID_KEYALIAS_PASS`"",
"-androidTargetSdkVersion", "`"$Env:ANDROID_TARGET_SDK_VERSION`"",
"-androidExportType", "`"$Env:ANDROID_EXPORT_TYPE`"",
"-androidSymbolType", "`"$Env:ANDROID_SYMBOL_TYPE`"",
"-logfile", "-"
) + $customParametersArray
#
# Results
#
if (-not $Env:BUILD_PROFILE) {
$unityArgs += @("-buildTarget", "`"$Env:BUILD_TARGET`"")
}
if ($Env:BUILD_PROFILE) {
$unityArgs += @("-activeBuildProfile", "`"$Env:BUILD_PROFILE`"")
}
Write-Output ""
Write-Output "###########################"
Write-Output "# Build output #"
Write-Output "###########################"
Write-Output ""
# Remove null items as that will fail the Start-Process call
$unityArgs = $unityArgs | Where-Object { $_ -ne $null }
Get-ChildItem $Env:BUILD_PATH_FULL
Write-Output ""
$unityProcess = Start-Process -FilePath "$Env:UNITY_PATH/Editor/Unity.exe" `
-ArgumentList $unityArgs `
-PassThru `
-NoNewWindow
# Cache the handle so exit code works properly
# https://stackoverflow.com/questions/10262231/obtaining-exitcode-using-start-process-and-waitforexit-instead-of-wait
$unityHandle = $unityProcess.Handle
while ($true) {
if ($unityProcess.HasExited) {
Start-Sleep -Seconds 3
Get-Process
$BUILD_EXIT_CODE = $unityProcess.ExitCode
# Display results
if ($BUILD_EXIT_CODE -eq 0)
{
Write-Output "Build Succeeded!!"
} else
{
Write-Output "Build failed, with exit code $BUILD_EXIT_CODE"
}
Write-Output ""
Write-Output "###########################"
Write-Output "# Build output #"
Write-Output "###########################"
Write-Output ""
Get-ChildItem $Env:BUILD_PATH_FULL
Write-Output ""
break
}
Start-Sleep -Seconds 3
}

View File

@@ -1,15 +1,55 @@
# Activate Unity
& "c:\steps\activate.ps1"
Get-Process
# Copy .upmconfig.toml if it exists
if (Test-Path "C:\githubhome\.upmconfig.toml") {
Write-Host "Copying .upmconfig.toml to $Env:USERPROFILE\.upmconfig.toml"
Copy-Item -Path "C:\githubhome\.upmconfig.toml" -Destination "$Env:USERPROFILE\.upmconfig.toml" -Force
} else {
Write-Host "No .upmconfig.toml found at C:\githubhome"
}
# Import any necessary registry keys, ie: location of windows 10 sdk
# No guarantee that there will be any necessary registry keys, ie: tvOS
Get-ChildItem -Path c:\regkeys -File | Foreach {reg import $_.fullname}
Get-ChildItem -Path c:\regkeys -File | ForEach-Object { reg import $_.fullname }
# Register the Visual Studio installation so Unity can find it
regsvr32 C:\ProgramData\Microsoft\VisualStudio\Setup\x64\Microsoft.VisualStudio.Setup.Configuration.Native.dll
# Kill the regsvr process
Get-Process -Name regsvr32 | ForEach-Object { Stop-Process -Id $_.Id -Force }
# Install Visual C++ 2013 Redistributables
. "c:\steps\install_vcredist13.ps1"
# Setup Git Credentials
. "c:\steps\set_gitcredential.ps1"
if ($env:ENABLE_GPU -eq "true") {
# Install LLVMpipe software graphics driver
. "c:\steps\install_llvmpipe.ps1"
}
# Activate Unity
if ($env:SKIP_ACTIVATION -ne "true") {
. "c:\steps\activate.ps1"
# If we didn't activate successfully, exit with the exit code from the activation step.
if ($ACTIVATION_EXIT_CODE -ne 0) {
exit $ACTIVATION_EXIT_CODE
}
}
else {
Write-Host "Skipping activation"
}
# Build the project
& "c:\steps\build.ps1"
. "c:\steps\build.ps1"
# Free the seat for the activated license
& "c:\steps\return_license.ps1"
if ($env:SKIP_ACTIVATION -ne "true") {
. "c:\steps\return_license.ps1"
}
Get-Process
exit $BUILD_EXIT_CODE

View File

@@ -0,0 +1,56 @@
$Private:repo = "mmozeiko/build-mesa"
$Private:downloadPath = "$Env:TEMP\mesa.zip"
$Private:extractPath = "$Env:TEMP\mesa"
$Private:destinationPath = "$Env:UNITY_PATH\Editor\"
$Private:version = "25.1.0"
$LLVMPIPE_INSTALLED = "false"
try {
# Get the release info from GitHub API (version fixed to decrease probability of breakage)
$releaseUrl = "https://api.github.com/repos/$repo/releases/tags/$version"
$release = Invoke-RestMethod -Uri $releaseUrl -Headers @{ "User-Agent" = "PowerShell" }
# Get the download URL for the zip asset
$zipUrl = $release.assets | Where-Object { $_.name -like "mesa-llvmpipe-x64*.zip" } | Select-Object -First 1 -ExpandProperty browser_download_url
if (-not $zipUrl) {
throw "No zip file found in the latest release."
}
# Download the zip file
Write-Host "Downloading $zipUrl..."
Invoke-WebRequest -Uri $zipUrl -OutFile $downloadPath
# Create extraction directory if it doesn't exist
if (-not (Test-Path $extractPath)) {
New-Item -ItemType Directory -Path $extractPath | Out-Null
}
# Extract the zip file
Write-Host "Extracting $downloadPath to $extractPath..."
Expand-Archive -Path $downloadPath -DestinationPath $extractPath -Force
# Create destination directory if it doesn't exist
if (-not (Test-Path $destinationPath)) {
New-Item -ItemType Directory -Path $destinationPath | Out-Null
}
# Copy extracted files to destination
Write-Host "Copying files to $destinationPath..."
Copy-Item -Path "$extractPath\*" -Destination $destinationPath -Recurse -Force
Write-Host "Successfully downloaded, extracted, and copied Mesa files to $destinationPath"
$LLVMPIPE_INSTALLED = "true"
} catch {
Write-Error "An error occurred: $_"
} finally {
# Clean up temporary files
if (Test-Path $downloadPath) {
Remove-Item $downloadPath -Force
}
if (Test-Path $extractPath) {
Remove-Item $extractPath -Recurse -Force
}
}

View File

@@ -0,0 +1,11 @@
# For some reason, Unity is failing in github actions windows runners
# due to missing Visual C++ 2013 redistributables.
# This script downloads and installs the required redistributables.
Write-Output ""
Write-Output "#########################################################"
Write-Output "# Installing Visual C++ Redistributables (2013) #"
Write-Output "#########################################################"
Write-Output ""
choco install vcredist2013 -y --no-progress

View File

@@ -1,7 +1,61 @@
# Return the active Unity license
& "C:\Program Files\Unity\Hub\Editor\$Env:UNITY_VERSION\Editor\Unity.exe" -batchmode -quit -nographics `
-username $Env:UNITY_EMAIL `
-password $Env:UNITY_PASSWORD `
-returnlicense `
-projectPath "c:/BlankProject" `
-logfile | Out-Host
Write-Output ""
Write-Output "###########################"
Write-Output "# Return License #"
Write-Output "###########################"
Write-Output ""
if (($null -ne ${env:UNITY_LICENSING_SERVER}))
{
Write-Output "Returning floating license: ""$env:FLOATING_LICENSE"""
Start-Process -FilePath "$Env:UNITY_PATH\Editor\Data\Resources\Licensing\Client\Unity.Licensing.Client.exe" `
-ArgumentList "--return-floating ""$env:FLOATING_LICENSE"" " `
-NoNewWindow `
-Wait
}
elseif (($null -ne ${env:UNITY_SERIAL}) -and ($null -ne ${env:UNITY_EMAIL}) -and ($null -ne ${env:UNITY_PASSWORD}))
{
#
# SERIAL LICENSE MODE
#
# This will return the license that is currently in use.
#
$RETURN_LICENSE_OUTPUT = Start-Process -FilePath "$Env:UNITY_PATH/Editor/Unity.exe" `
-NoNewWindow `
-PassThru `
-ArgumentList "-batchmode `
-quit `
-nographics `
-username $Env:UNITY_EMAIL `
-password $Env:UNITY_PASSWORD `
-returnlicense `
-projectPath c:/BlankProject `
-logfile -"
# Cache the handle so exit code works properly
# https://stackoverflow.com/questions/10262231/obtaining-exitcode-using-start-process-and-waitforexit-instead-of-wait
$unityHandle = $RETURN_LICENSE_OUTPUT.Handle
while ($true) {
if ($RETURN_LICENSE_OUTPUT.HasExited) {
$RETURN_LICENSE_EXIT_CODE = $RETURN_LICENSE_OUTPUT.ExitCode
# Display results
if ($RETURN_LICENSE_EXIT_CODE -eq 0)
{
Write-Output "License Return Succeeded"
} else
{
Write-Output "License Return failed, with exit code $RETURN_LICENSE_EXIT_CODE"
Write-Output "::warning ::License Return failed! If this is a Pro License you might need to manually `
free the seat in your Unity admin panel or you might run out of seats to activate with."
}
break
}
Start-Sleep -Seconds 3
}
}

View File

@@ -0,0 +1,20 @@
if ($null -eq ${env:GIT_PRIVATE_TOKEN}) {
Write-Host "GIT_PRIVATE_TOKEN unset skipping"
}
else {
Write-Host "GIT_PRIVATE_TOKEN is set configuring git credentials"
git config --global credential.helper store
git config --global --replace-all url."https://token:$env:GIT_PRIVATE_TOKEN@github.com/".insteadOf "ssh://git@github.com/"
git config --global --add url."https://token:$env:GIT_PRIVATE_TOKEN@github.com/".insteadOf "git@github.com"
git config --global --add url."https://token:$env:GIT_PRIVATE_TOKEN@github.com/".insteadOf "https://github.com/"
git config --global url."https://ssh:$env:GIT_PRIVATE_TOKEN@github.com/".insteadOf "ssh://git@github.com/"
git config --global url."https://git:$env:GIT_PRIVATE_TOKEN@github.com/".insteadOf "git@github.com:"
}
Write-Host "---------- git config --list -------------"
git config --list
Write-Host "---------- git config --list --show-origin -------------"
git config --list --show-origin

2
dist/sourcemap-register.js generated vendored

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,7 @@
{
"licensingServiceBaseUrl": "%URL%",
"enableEntitlementLicensing": true,
"enableFloatingApi": true,
"clientConnectTimeoutSec": 5,
"clientHandshakeTimeoutSec": 10
}

View File

@@ -1,60 +0,0 @@
"use strict";
/* eslint-disable no-process-exit */
const util = require("util");
const { JSDOM } = require("../../../..");
const { READY_STATES } = require("./xhr-utils");
const idlUtils = require("../generated/utils");
const tough = require("tough-cookie");
const dom = new JSDOM();
const xhr = new dom.window.XMLHttpRequest();
const xhrImpl = idlUtils.implForWrapper(xhr);
const chunks = [];
process.stdin.on("data", chunk => {
chunks.push(chunk);
});
process.stdin.on("end", () => {
const buffer = Buffer.concat(chunks);
const flag = JSON.parse(buffer.toString());
if (flag.body && flag.body.type === "Buffer" && flag.body.data) {
flag.body = Buffer.from(flag.body.data);
}
if (flag.cookieJar) {
flag.cookieJar = tough.CookieJar.fromJSON(flag.cookieJar);
}
flag.synchronous = false;
Object.assign(xhrImpl.flag, flag);
const { properties } = xhrImpl;
xhrImpl.readyState = READY_STATES.OPENED;
try {
xhr.addEventListener("loadend", () => {
if (properties.error) {
properties.error = properties.error.stack || util.inspect(properties.error);
}
process.stdout.write(JSON.stringify({
responseURL: xhrImpl.responseURL,
status: xhrImpl.status,
statusText: xhrImpl.statusText,
properties
}), () => {
process.exit(0);
});
}, false);
xhr.send(flag.body);
} catch (error) {
properties.error += error.stack || util.inspect(error);
process.stdout.write(JSON.stringify({
responseURL: xhrImpl.responseURL,
status: xhrImpl.status,
statusText: xhrImpl.statusText,
properties
}), () => {
process.exit(0);
});
}
});

View File

@@ -0,0 +1,3 @@
hook: after
commands: |
echo "after-build hook test!"

View File

@@ -0,0 +1,3 @@
hook: before
commands: |
echo "before-build hook test!!"

View File

@@ -0,0 +1,3 @@
hook: after
commands: |
echo "after-build step test!"

View File

@@ -0,0 +1,3 @@
hook: before
commands: |
echo "before-build step test!"

122
install.ps1 Normal file
View File

@@ -0,0 +1,122 @@
# game-ci CLI installer for Windows
# Usage: irm https://raw.githubusercontent.com/game-ci/unity-builder/main/install.ps1 | iex
#
# Environment variables:
# GAME_CI_VERSION - Install a specific version (e.g., v2.0.0). Defaults to latest.
# GAME_CI_INSTALL - Installation directory. Defaults to $HOME\.game-ci\bin.
$ErrorActionPreference = 'Stop'
$Repo = "game-ci/unity-builder"
$InstallDir = if ($env:GAME_CI_INSTALL) { $env:GAME_CI_INSTALL } else { Join-Path $env:USERPROFILE ".game-ci\bin" }
$AssetName = "game-ci-windows-x64.exe"
$BinaryName = "game-ci.exe"
function Write-Info($Message) {
Write-Host "info: " -ForegroundColor Green -NoNewline
Write-Host $Message
}
function Write-Warn($Message) {
Write-Host "warn: " -ForegroundColor Yellow -NoNewline
Write-Host $Message
}
# Determine version
if ($env:GAME_CI_VERSION) {
$Version = $env:GAME_CI_VERSION
Write-Info "Using specified version: $Version"
} else {
Write-Info "Fetching latest release..."
try {
$Release = Invoke-RestMethod "https://api.github.com/repos/$Repo/releases/latest"
$Version = $Release.tag_name
} catch {
Write-Host "error: Could not determine latest version. Check https://github.com/$Repo/releases" -ForegroundColor Red
exit 1
}
}
$DownloadUrl = "https://github.com/$Repo/releases/download/$Version/$AssetName"
$ChecksumUrl = "https://github.com/$Repo/releases/download/$Version/checksums.txt"
$BinaryPath = Join-Path $InstallDir $BinaryName
Write-Host ""
Write-Info "Installing game-ci $Version (windows-x64)"
Write-Info " from: $DownloadUrl"
Write-Info " to: $BinaryPath"
Write-Host ""
# Create install directory
if (-not (Test-Path $InstallDir)) {
New-Item -ItemType Directory -Force -Path $InstallDir | Out-Null
}
# Download binary
try {
Invoke-WebRequest -Uri $DownloadUrl -OutFile $BinaryPath -UseBasicParsing
} catch {
if ($_.Exception.Response.StatusCode -eq 404) {
Write-Host "error: Release asset not found: $AssetName ($Version)" -ForegroundColor Red
Write-Host " Check available assets at https://github.com/$Repo/releases/tag/$Version" -ForegroundColor Red
} else {
Write-Host "error: Download failed: $_" -ForegroundColor Red
}
exit 1
}
# Verify checksum
try {
$Checksums = Invoke-WebRequest -Uri $ChecksumUrl -UseBasicParsing | Select-Object -ExpandProperty Content
$ExpectedLine = $Checksums -split "`n" | Where-Object { $_ -match $AssetName } | Select-Object -First 1
if ($ExpectedLine) {
$ExpectedHash = ($ExpectedLine -split '\s+')[0]
$ActualHash = (Get-FileHash -Path $BinaryPath -Algorithm SHA256).Hash.ToLower()
if ($ExpectedHash -eq $ActualHash) {
Write-Info "Checksum verified (SHA256)"
} else {
Write-Host "error: Checksum verification failed!" -ForegroundColor Red
Write-Host " Expected: $ExpectedHash" -ForegroundColor Red
Write-Host " Got: $ActualHash" -ForegroundColor Red
Remove-Item $BinaryPath -Force
exit 1
}
}
} catch {
# Checksums not available for this release; continue without verification
}
# Verify the binary works
try {
$VersionOutput = & $BinaryPath version 2>&1
Write-Info "Verified: $($VersionOutput | Select-Object -First 1)"
} catch {
Write-Warn "Binary downloaded but could not verify. It may still work."
}
Write-Host ""
Write-Host "game-ci installed successfully!" -ForegroundColor Green -BackgroundColor Black
Write-Host ""
# Check PATH and offer to add
$UserPath = [Environment]::GetEnvironmentVariable('PATH', 'User')
if ($UserPath -notlike "*$InstallDir*") {
Write-Warn "game-ci is not in your PATH."
Write-Host ""
Write-Host "To add it permanently, run:" -ForegroundColor Yellow
Write-Host ""
Write-Host " [Environment]::SetEnvironmentVariable('PATH', ""$InstallDir;"" + [Environment]::GetEnvironmentVariable('PATH', 'User'), 'User')"
Write-Host ""
Write-Info "Then restart your terminal."
# Offer to add automatically
Write-Host ""
$AddToPath = Read-Host "Add to PATH now? (Y/n)"
if ($AddToPath -ne 'n' -and $AddToPath -ne 'N') {
[Environment]::SetEnvironmentVariable('PATH', "$InstallDir;$UserPath", 'User')
$env:PATH = "$InstallDir;$env:PATH"
Write-Info "Added to PATH. You can now run: game-ci --help"
}
} else {
Write-Info "game-ci is already in your PATH. Run: game-ci --help"
}

196
install.sh Normal file
View File

@@ -0,0 +1,196 @@
#!/bin/sh
# game-ci CLI installer
# Usage: curl -fsSL https://raw.githubusercontent.com/game-ci/unity-builder/main/install.sh | sh
#
# Environment variables:
# GAME_CI_VERSION - Install a specific version (e.g., v2.0.0). Defaults to latest.
# GAME_CI_INSTALL - Installation directory. Defaults to ~/.game-ci/bin.
set -e
REPO="game-ci/unity-builder"
INSTALL_DIR="${GAME_CI_INSTALL:-$HOME/.game-ci/bin}"
BINARY_NAME="game-ci"
# Colors (disabled if not a terminal)
if [ -t 1 ]; then
BOLD='\033[1m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
RED='\033[0;31m'
RESET='\033[0m'
else
BOLD=''
GREEN=''
YELLOW=''
RED=''
RESET=''
fi
info() {
printf "${GREEN}info${RESET}: %s\n" "$1"
}
warn() {
printf "${YELLOW}warn${RESET}: %s\n" "$1"
}
error() {
printf "${RED}error${RESET}: %s\n" "$1" >&2
exit 1
}
# Detect OS and architecture
detect_platform() {
OS="$(uname -s)"
ARCH="$(uname -m)"
case "$OS" in
Linux*) PLATFORM="linux" ;;
Darwin*) PLATFORM="macos" ;;
MINGW*|MSYS*|CYGWIN*)
PLATFORM="windows"
warn "For Windows, consider using install.ps1 instead:"
warn " irm https://raw.githubusercontent.com/game-ci/unity-builder/main/install.ps1 | iex"
;;
*) error "Unsupported operating system: $OS" ;;
esac
case "$ARCH" in
x86_64|amd64) ARCH="x64" ;;
aarch64|arm64) ARCH="arm64" ;;
*) error "Unsupported architecture: $ARCH" ;;
esac
ASSET_NAME="game-ci-${PLATFORM}-${ARCH}"
if [ "$PLATFORM" = "windows" ]; then
ASSET_NAME="${ASSET_NAME}.exe"
BINARY_NAME="game-ci.exe"
fi
}
# Get latest release tag from GitHub API
get_latest_version() {
if [ -n "$GAME_CI_VERSION" ]; then
VERSION="$GAME_CI_VERSION"
info "Using specified version: $VERSION"
return
fi
info "Fetching latest release..."
if command -v curl > /dev/null 2>&1; then
VERSION=$(curl -fsSL "https://api.github.com/repos/${REPO}/releases/latest" | grep '"tag_name"' | sed -E 's/.*"([^"]+)".*/\1/')
elif command -v wget > /dev/null 2>&1; then
VERSION=$(wget -qO- "https://api.github.com/repos/${REPO}/releases/latest" | grep '"tag_name"' | sed -E 's/.*"([^"]+)".*/\1/')
else
error "Neither curl nor wget found. Please install one of them."
fi
if [ -z "$VERSION" ]; then
error "Could not determine latest version. Check https://github.com/${REPO}/releases"
fi
}
# Download and install the binary
install() {
DOWNLOAD_URL="https://github.com/${REPO}/releases/download/${VERSION}/${ASSET_NAME}"
printf "\n"
info "Installing game-ci ${VERSION} (${PLATFORM}-${ARCH})"
info " from: ${DOWNLOAD_URL}"
info " to: ${INSTALL_DIR}/${BINARY_NAME}"
printf "\n"
mkdir -p "$INSTALL_DIR"
# Download with progress
if command -v curl > /dev/null 2>&1; then
HTTP_CODE=$(curl -fSL "$DOWNLOAD_URL" -o "${INSTALL_DIR}/${BINARY_NAME}" -w "%{http_code}" 2>/dev/null) || true
if [ "$HTTP_CODE" = "404" ]; then
error "Release asset not found: ${ASSET_NAME} (${VERSION}). Check available assets at https://github.com/${REPO}/releases/tag/${VERSION}"
elif [ ! -f "${INSTALL_DIR}/${BINARY_NAME}" ]; then
error "Download failed. URL: ${DOWNLOAD_URL}"
fi
elif command -v wget > /dev/null 2>&1; then
wget -q "$DOWNLOAD_URL" -O "${INSTALL_DIR}/${BINARY_NAME}" || error "Download failed. URL: ${DOWNLOAD_URL}"
fi
chmod +x "${INSTALL_DIR}/${BINARY_NAME}"
# Verify the binary works
if "${INSTALL_DIR}/${BINARY_NAME}" version > /dev/null 2>&1; then
INSTALLED_VERSION=$("${INSTALL_DIR}/${BINARY_NAME}" version 2>&1 | head -1)
info "Verified: ${INSTALLED_VERSION}"
else
warn "Binary downloaded but could not verify. It may still work."
fi
printf "\n"
printf "${BOLD}game-ci installed successfully!${RESET}\n"
printf "\n"
# Check if install dir is in PATH
case ":$PATH:" in
*":${INSTALL_DIR}:"*)
info "game-ci is already in your PATH. Run: game-ci --help"
;;
*)
SHELL_NAME=$(basename "$SHELL" 2>/dev/null || echo "sh")
case "$SHELL_NAME" in
zsh) PROFILE="~/.zshrc" ;;
bash) PROFILE="~/.bashrc" ;;
fish) PROFILE="~/.config/fish/config.fish" ;;
*) PROFILE="~/.profile" ;;
esac
printf "${YELLOW}Add game-ci to your PATH by adding this to ${PROFILE}:${RESET}\n"
printf "\n"
if [ "$SHELL_NAME" = "fish" ]; then
printf " set -gx PATH \"%s\" \$PATH\n" "$INSTALL_DIR"
else
printf " export PATH=\"%s:\$PATH\"\n" "$INSTALL_DIR"
fi
printf "\n"
info "Then restart your shell or run: source ${PROFILE}"
;;
esac
}
# Verify checksum if checksums.txt is available
verify_checksum() {
if ! command -v sha256sum > /dev/null 2>&1; then
return 0
fi
CHECKSUM_URL="https://github.com/${REPO}/releases/download/${VERSION}/checksums.txt"
CHECKSUMS=""
if command -v curl > /dev/null 2>&1; then
CHECKSUMS=$(curl -fsSL "$CHECKSUM_URL" 2>/dev/null) || return 0
elif command -v wget > /dev/null 2>&1; then
CHECKSUMS=$(wget -qO- "$CHECKSUM_URL" 2>/dev/null) || return 0
fi
if [ -z "$CHECKSUMS" ]; then
return 0
fi
EXPECTED=$(echo "$CHECKSUMS" | grep "$ASSET_NAME" | awk '{print $1}')
if [ -z "$EXPECTED" ]; then
return 0
fi
ACTUAL=$(sha256sum "${INSTALL_DIR}/${BINARY_NAME}" | awk '{print $1}')
if [ "$EXPECTED" != "$ACTUAL" ]; then
error "Checksum verification failed!\n Expected: ${EXPECTED}\n Got: ${ACTUAL}"
fi
info "Checksum verified (SHA256)"
}
# Main
detect_platform
get_latest_version
install
verify_checksum

11
jest.ci.config.js Normal file
View File

@@ -0,0 +1,11 @@
const base = require('./jest.config.js');
module.exports = {
...base,
forceExit: true,
detectOpenHandles: true,
testTimeout: 120000,
maxWorkers: 1,
};

View File

@@ -25,6 +25,6 @@ module.exports = {
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
modulePathIgnorePatterns: ['<rootDir>/lib/', '<rootDir>/dist/'],
// A list of paths to modules that run some code to configure or set up the testing framework before each test
setupFilesAfterEnv: ['<rootDir>/src/jest.setup.ts'],
// Use jest.setup.js to polyfill fetch for all tests
setupFiles: ['<rootDir>/jest.setup.js'],
};

2
jest.setup.js Normal file
View File

@@ -0,0 +1,2 @@
const fetch = require('node-fetch');
global.fetch = fetch;

View File

@@ -1,57 +1,92 @@
{
"name": "unity-builder",
"version": "2.0.0",
"version": "3.0.0",
"description": "Build Unity projects for different platforms.",
"main": "dist/index.js",
"bin": {
"game-ci": "./lib/cli.js"
},
"pkg": {
"scripts": "lib/**/*.js",
"assets": [
"lib/**/*.json",
"package.json"
],
"targets": [
"node20-linux-x64",
"node20-linux-arm64",
"node20-macos-x64",
"node20-macos-arm64",
"node20-win-x64"
],
"outputPath": "dist-binaries"
},
"repository": "git@github.com:game-ci/unity-builder.git",
"author": "Webber <webber@takken.io>",
"license": "MIT",
"scripts": {
"prepare": "lefthook install && npx husky uninstall -y",
"prepare": "lefthook install",
"build": "yarn && tsc && ncc build lib --source-map --license licenses.txt",
"lint": "prettier --check \"src/**/*.{js,ts}\" && eslint src/**/*.ts",
"format": "prettier --write \"src/**/*.{js,ts}\"",
"cli": "yarn ts-node src/index.ts -m cli",
"gcp-secrets-tests": "cross-env cloudRunnerCluster=aws cloudRunnerTests=true readInputOverrideCommand=\"gcloud secrets versions access 1 --secret=\"{0}\"\" populateOverride=true readInputFromOverrideList=UNITY_EMAIL,UNITY_SERIAL,UNITY_PASSWORD yarn test -i -t \"cloud runner\"",
"gcp-secrets-cli": "cross-env cloudRunnerTests=true readInputOverrideCommand=\"gcloud secrets versions access 1 --secret=\"{0}\"\" yarn ts-node src/index.ts -m cli --populateOverride true --readInputFromOverrideList UNITY_EMAIL,UNITY_SERIAL,UNITY_PASSWORD",
"aws-secrets-cli": "cross-env cloudRunnerTests=true readInputOverrideCommand=\"aws secretsmanager get-secret-value --secret-id {0}\" yarn ts-node src/index.ts -m cli --populateOverride true --readInputFromOverrideList UNITY_EMAIL,UNITY_SERIAL,UNITY_PASSWORD",
"cli-aws": "cross-env cloudRunnerCluster=aws yarn run test-cli",
"cli-k8s": "cross-env cloudRunnerCluster=k8s yarn run test-cli",
"test-cli": "cross-env cloudRunnerTests=true yarn ts-node src/index.ts -m cli --projectPath test-project",
"game-ci": "ts-node src/cli.ts",
"gcp-secrets-tests": "cross-env providerStrategy=aws orchestratorTests=true inputPullCommand=\"gcp-secret-manager\" populateOverride=true pullInputList=UNITY_EMAIL,UNITY_SERIAL,UNITY_PASSWORD yarn test -i -t \"orchestrator\"",
"gcp-secrets-cli": "cross-env orchestratorTests=true USE_IL2CPP=false inputPullCommand=\"gcp-secret-manager\" yarn ts-node src/index.ts -m cli --populateOverride true --pullInputList UNITY_EMAIL,UNITY_SERIAL,UNITY_PASSWORD",
"aws-secrets-cli": "cross-env orchestratorTests=true inputPullCommand=\"aws-secret-manager\" yarn ts-node src/index.ts -m cli --populateOverride true --pullInputList UNITY_EMAIL,UNITY_SERIAL,UNITY_PASSWORD",
"cli-aws": "cross-env providerStrategy=aws yarn run test-cli",
"cli-k8s": "cross-env providerStrategy=k8s yarn run test-cli",
"test-cli": "cross-env orchestratorTests=true yarn ts-node src/index.ts -m cli --projectPath test-project",
"test": "jest",
"test-i": "yarn run test-i-aws && yarn run test-i-k8s",
"test-i-f": "yarn run test-i-aws && yarn run test-i-k8s && yarn run cli-k8s && yarn run cli-aws",
"test-i-aws": "cross-env cloudRunnerTests=true cloudRunnerCluster=aws yarn test -i -t \"cloud runner\"",
"test-i-k8s": "cross-env cloudRunnerTests=true cloudRunnerCluster=k8s yarn test -i -t \"cloud runner\""
"test:ci": "jest --config=jest.ci.config.js --runInBand",
"test-i": "cross-env orchestratorTests=true yarn test -i -t \"orchestrator\"",
"test-i-*": "yarn run test-i-aws && yarn run test-i-k8s",
"test-i-aws": "cross-env orchestratorTests=true providerStrategy=aws yarn test -i -t \"orchestrator\"",
"test-i-k8s": "cross-env orchestratorTests=true providerStrategy=k8s yarn test -i -t \"orchestrator\""
},
"engines": {
"node": ">=18.x"
},
"dependencies": {
"@actions/core": "^1.6.0",
"@actions/exec": "^1.1.0",
"@actions/github": "^5.0.0",
"@actions/cache": "^4.0.0",
"@actions/core": "^1.11.1",
"@actions/exec": "^1.1.1",
"@actions/github": "^6.0.0",
"@aws-sdk/client-cloudformation": "^3.777.0",
"@aws-sdk/client-cloudwatch-logs": "^3.777.0",
"@aws-sdk/client-ecs": "^3.778.0",
"@aws-sdk/client-kinesis": "^3.777.0",
"@aws-sdk/client-s3": "^3.779.0",
"@kubernetes/client-node": "^0.16.3",
"@octokit/core": "^3.5.1",
"@octokit/core": "^5.1.0",
"async-wait-until": "^2.0.12",
"aws-sdk": "^2.1081.0",
"base-64": "^1.0.0",
"commander": "^9.0.0",
"commander-ts": "^0.2.0",
"kubernetes-client": "^9.0.0",
"md5": "^2.3.0",
"nanoid": "^3.3.1",
"reflect-metadata": "^0.1.13",
"semver": "^7.3.5",
"unity-changeset": "^1.6.0",
"uuid": "^8.3.2",
"yaml": "^1.10.2"
"semver": "^7.5.2",
"shell-quote": "^1.8.3",
"ts-md5": "^1.3.1",
"unity-changeset": "^3.1.0",
"uuid": "^9.0.0",
"yaml": "^2.2.2",
"yargs": "^17.7.2"
},
"devDependencies": {
"@arkweid/lefthook": "^0.7.7",
"@types/base-64": "^1.0.0",
"@types/jest": "^27.4.1",
"@types/node": "^17.0.23",
"@types/semver": "^7.3.9",
"@types/uuid": "^9.0.0",
"@types/yargs": "^17.0.35",
"@typescript-eslint/parser": "4.8.1",
"@vercel/ncc": "^0.33.3",
"@vercel/ncc": "^0.36.1",
"cross-env": "^7.0.3",
"eslint": "7.17.0",
"eslint": "^7.23.0",
"eslint-config-prettier": "8.1.0",
"eslint-plugin-github": "^4.1.1",
"eslint-plugin-jest": "24.1.3",
@@ -59,11 +94,19 @@
"eslint-plugin-unicorn": "28.0.2",
"jest": "^27.5.1",
"jest-circus": "^27.5.1",
"jest-fail-on-console": "^2.3.0",
"jest-fail-on-console": "^3.0.2",
"js-yaml": "^4.1.0",
"lefthook": "^1.6.1",
"node-fetch": "2",
"pkg": "^5.8.1",
"prettier": "^2.5.1",
"ts-jest": "^27.1.3",
"ts-node": "10.4.0",
"typescript": "4.1.3"
"ts-node": "10.8.1",
"typescript": "4.7.4",
"yarn-audit-fix": "^9.3.8"
},
"volta": {
"node": "20.5.1",
"yarn": "1.22.19"
}
}

15
scripts/game-ci.bat Normal file
View File

@@ -0,0 +1,15 @@
echo "installing game-ci cli"
if exist %UserProfile%\AppData\LocalLow\game-ci\ (
echo Installed Updating
git -C %UserProfile%\AppData\LocalLow\game-ci\ fetch
git -C %UserProfile%\AppData\LocalLow\game-ci\ reset --hard
git -C %UserProfile%\AppData\LocalLow\game-ci\ pull
git -C %UserProfile%\AppData\LocalLow\game-ci\ branch
) else (
echo Not Installed Downloading...
mkdir %UserProfile%\AppData\LocalLow\game-ci\
git clone https://github.com/game-ci/unity-builder %UserProfile%\AppData\LocalLow\game-ci\
)
call yarn --cwd %UserProfile%\AppData\LocalLow\game-ci\ install
call yarn --cwd %UserProfile%\AppData\LocalLow\game-ci\ run gcp-secrets-cli %* --projectPath %cd% --awsStackName game-ci-cli

42
src/cli.ts Normal file
View File

@@ -0,0 +1,42 @@
#!/usr/bin/env node
import yargs from 'yargs';
// eslint-disable-next-line import/no-unresolved
import { hideBin } from 'yargs/helpers';
import buildCommand from './cli/commands/build';
import testCommand from './cli/commands/test';
import orchestrateCommand from './cli/commands/orchestrate';
import activateCommand from './cli/commands/activate';
import statusCommand from './cli/commands/status';
import versionCommand from './cli/commands/version';
import updateCommand from './cli/commands/update';
import * as core from '@actions/core';
const cli = yargs(hideBin(process.argv))
.scriptName('game-ci')
.usage('$0 <command> [options]')
.command(buildCommand)
.command(testCommand)
.command(orchestrateCommand)
.command(activateCommand)
.command(statusCommand)
.command(versionCommand)
.command(updateCommand)
.demandCommand(1, 'You must specify a command. Run game-ci --help for available commands.')
.strict()
.alias('h', 'help')
.epilogue('For more information, visit https://game.ci')
.wrap(Math.min(120, process.stdout.columns || 80));
async function main() {
try {
await cli.parse();
} catch (error: any) {
if (error.name !== 'YError') {
core.error(`Error: ${error.message}`);
process.exit(1);
}
}
}
main();

View File

@@ -0,0 +1,135 @@
import { execFile } from 'node:child_process';
import path from 'node:path';
/**
* Integration tests that spawn the CLI as a child process and verify
* exit codes and output. Uses node with --require ts-node/register to
* run the TypeScript entry point directly so no build step is required.
*/
const CLI_ENTRY = path.resolve(__dirname, '..', '..', 'cli.ts');
function runCli(cliArguments: string[]): Promise<{ code: number | null; stdout: string; stderr: string }> {
return new Promise((resolve) => {
execFile(
process.execPath,
['--require', 'ts-node/register/transpile-only', CLI_ENTRY, ...cliArguments],
{ timeout: 30_000, cwd: path.resolve(__dirname, '..', '..', '..') },
(error, stdout, stderr) => {
resolve({
code: error ? error.code ?? 1 : 0,
stdout: stdout.toString(),
stderr: stderr.toString(),
});
},
);
});
}
// Integration tests spawn child processes which need more time than the default 5s
jest.setTimeout(30_000);
describe('CLI integration', () => {
it('exits 0 and shows all commands for --help', async () => {
const result = await runCli(['--help']);
expect(result.code).toStrictEqual(0);
expect(result.stdout).toContain('game-ci');
expect(result.stdout).toContain('build');
expect(result.stdout).toContain('test');
expect(result.stdout).toContain('orchestrate');
expect(result.stdout).toContain('activate');
expect(result.stdout).toContain('status');
expect(result.stdout).toContain('version');
expect(result.stdout).toContain('update');
});
it('exits 0 and shows version info for version command', async () => {
const result = await runCli(['version']);
expect(result.code).toStrictEqual(0);
expect(result.stdout).toContain('unity-builder');
});
it('exits 0 and shows build flags for build --help', async () => {
const result = await runCli(['build', '--help']);
expect(result.code).toStrictEqual(0);
expect(result.stdout).toContain('--target-platform');
expect(result.stdout).toContain('--unity-version');
expect(result.stdout).toContain('--project-path');
expect(result.stdout).toContain('--build-name');
expect(result.stdout).toContain('--builds-path');
expect(result.stdout).toContain('--build-method');
expect(result.stdout).toContain('--custom-parameters');
expect(result.stdout).toContain('--versioning');
});
it('exits 0 and shows test flags for test --help', async () => {
const result = await runCli(['test', '--help']);
expect(result.code).toStrictEqual(0);
expect(result.stdout).toContain('--target-platform');
expect(result.stdout).toContain('--test-mode');
expect(result.stdout).toContain('--test-results-path');
expect(result.stdout).toContain('--enable-code-coverage');
});
it('exits 0 for test alias t --help', async () => {
const result = await runCli(['t', '--help']);
expect(result.code).toStrictEqual(0);
expect(result.stdout).toContain('--test-mode');
});
it('exits non-zero for an unknown command', async () => {
const result = await runCli(['nonexistent']);
expect(result.code).not.toStrictEqual(0);
});
it('exits non-zero when no command is provided', async () => {
const result = await runCli([]);
expect(result.code).not.toStrictEqual(0);
});
it('exits 0 for orchestrate --help', async () => {
const result = await runCli(['orchestrate', '--help']);
expect(result.code).toStrictEqual(0);
expect(result.stdout).toContain('--target-platform');
expect(result.stdout).toContain('--provider-strategy');
});
it('exits 0 for orchestrate alias o --help', async () => {
const result = await runCli(['o', '--help']);
expect(result.code).toStrictEqual(0);
expect(result.stdout).toContain('--provider-strategy');
});
it('exits 0 for activate --help', async () => {
const result = await runCli(['activate', '--help']);
expect(result.code).toStrictEqual(0);
expect(result.stdout).toContain('activate');
});
it('exits 0 for status --help', async () => {
const result = await runCli(['status', '--help']);
expect(result.code).toStrictEqual(0);
expect(result.stdout).toContain('status');
expect(result.stdout).toContain('--cache-dir');
});
it('exits 0 for update --help', async () => {
const result = await runCli(['update', '--help']);
expect(result.code).toStrictEqual(0);
expect(result.stdout).toContain('update');
expect(result.stdout).toContain('--force');
expect(result.stdout).toContain('--version');
});
});

View File

@@ -0,0 +1,312 @@
import buildCommand from '../commands/build';
import testCommand from '../commands/test';
import activateCommand from '../commands/activate';
import orchestrateCommand from '../commands/orchestrate';
import statusCommand from '../commands/status';
import versionCommand from '../commands/version';
import updateCommand from '../commands/update';
function createFakeYargs(): { yargs: any; options: Record<string, any> } {
const options: Record<string, any> = {};
const yargs: any = {
option: jest.fn(),
positional: jest.fn(),
example: jest.fn(),
env: jest.fn(),
};
yargs.option.mockImplementation((name: string, config: any) => {
options[name] = config;
return yargs;
});
yargs.positional.mockImplementation((name: string, config: any) => {
options[name] = config;
return yargs;
});
yargs.example.mockReturnValue(yargs);
yargs.env.mockReturnValue(yargs);
return { yargs, options };
}
describe('CLI commands', () => {
describe('build command', () => {
it('exports the correct command name', () => {
expect(buildCommand.command).toStrictEqual('build');
});
it('has a description', () => {
expect(buildCommand.describe).toBeTruthy();
});
it('has a builder function', () => {
expect(typeof buildCommand.builder).toStrictEqual('function');
});
it('has a handler function', () => {
expect(typeof buildCommand.handler).toStrictEqual('function');
});
it('defines all expected build flags via builder', () => {
const { yargs, options } = createFakeYargs();
(buildCommand.builder as Function)(yargs);
// Core build flags (from shared + build-specific)
expect(options['target-platform']).toBeDefined();
expect(options['target-platform'].demandOption).toStrictEqual(true);
expect(options['unity-version']).toBeDefined();
expect(options['project-path']).toBeDefined();
expect(options['build-profile']).toBeDefined();
expect(options['build-name']).toBeDefined();
expect(options['builds-path']).toBeDefined();
expect(options['build-method']).toBeDefined();
expect(options['custom-parameters']).toBeDefined();
expect(options['versioning']).toBeDefined();
expect(options['version']).toBeDefined();
expect(options['custom-image']).toBeDefined();
expect(options['manual-exit']).toBeDefined();
expect(options['enable-gpu']).toBeDefined();
// Android flags
expect(options['android-version-code']).toBeDefined();
expect(options['android-export-type']).toBeDefined();
expect(options['android-keystore-name']).toBeDefined();
expect(options['android-keystore-base64']).toBeDefined();
expect(options['android-keystore-pass']).toBeDefined();
expect(options['android-keyalias-name']).toBeDefined();
expect(options['android-keyalias-pass']).toBeDefined();
expect(options['android-target-sdk-version']).toBeDefined();
expect(options['android-symbol-type']).toBeDefined();
// Docker flags
expect(options['docker-cpu-limit']).toBeDefined();
expect(options['docker-memory-limit']).toBeDefined();
expect(options['docker-workspace-path']).toBeDefined();
expect(options['run-as-host-user']).toBeDefined();
expect(options['chown-files-to']).toBeDefined();
// Build should NOT have orchestrator-specific flags
expect(options['provider-strategy']).toBeUndefined();
expect(options['aws-stack-name']).toBeUndefined();
expect(options['kube-config']).toBeUndefined();
});
it('sets correct default values', () => {
const { yargs, options } = createFakeYargs();
(buildCommand.builder as Function)(yargs);
expect(options['unity-version'].default).toStrictEqual('auto');
expect(options['project-path'].default).toStrictEqual('.');
expect(options['builds-path'].default).toStrictEqual('build');
expect(options['versioning'].default).toStrictEqual('Semantic');
expect(options['manual-exit'].default).toStrictEqual(false);
expect(options['enable-gpu'].default).toStrictEqual(false);
expect(options['android-export-type'].default).toStrictEqual('androidPackage');
expect(options['android-symbol-type'].default).toStrictEqual('none');
});
it('provides camelCase aliases for kebab-case options', () => {
const { yargs, options } = createFakeYargs();
(buildCommand.builder as Function)(yargs);
expect(options['target-platform'].alias).toStrictEqual('targetPlatform');
expect(options['unity-version'].alias).toStrictEqual('unityVersion');
expect(options['project-path'].alias).toStrictEqual('projectPath');
expect(options['build-name'].alias).toStrictEqual('buildName');
expect(options['builds-path'].alias).toStrictEqual('buildsPath');
expect(options['build-method'].alias).toStrictEqual('buildMethod');
});
});
describe('test command', () => {
it('exports command with alias', () => {
expect(testCommand.command).toStrictEqual(['test', 't']);
});
it('has a description', () => {
expect(testCommand.describe).toBeTruthy();
});
it('has a builder function', () => {
expect(typeof testCommand.builder).toStrictEqual('function');
});
it('has a handler function', () => {
expect(typeof testCommand.handler).toStrictEqual('function');
});
it('defines test-specific flags', () => {
const { yargs, options } = createFakeYargs();
(testCommand.builder as Function)(yargs);
expect(options['test-mode']).toBeDefined();
expect(options['test-mode'].default).toStrictEqual('All');
expect(options['test-mode'].choices).toStrictEqual(['EditMode', 'PlayMode', 'All']);
expect(options['test-results-path']).toBeDefined();
expect(options['test-category']).toBeDefined();
expect(options['test-filter']).toBeDefined();
expect(options['enable-code-coverage']).toBeDefined();
expect(options['coverage-options']).toBeDefined();
});
it('includes shared project options', () => {
const { yargs, options } = createFakeYargs();
(testCommand.builder as Function)(yargs);
expect(options['target-platform']).toBeDefined();
expect(options['target-platform'].demandOption).toStrictEqual(true);
expect(options['unity-version']).toBeDefined();
expect(options['project-path']).toBeDefined();
expect(options['custom-image']).toBeDefined();
});
it('includes docker options but not orchestrator options', () => {
const { yargs, options } = createFakeYargs();
(testCommand.builder as Function)(yargs);
expect(options['docker-cpu-limit']).toBeDefined();
expect(options['docker-memory-limit']).toBeDefined();
expect(options['provider-strategy']).toBeUndefined();
expect(options['aws-stack-name']).toBeUndefined();
});
});
describe('activate command', () => {
it('exports the correct command name', () => {
expect(activateCommand.command).toStrictEqual('activate');
});
it('has a description', () => {
expect(activateCommand.describe).toBeTruthy();
});
it('has a builder function', () => {
expect(typeof activateCommand.builder).toStrictEqual('function');
});
it('has a handler function', () => {
expect(typeof activateCommand.handler).toStrictEqual('function');
});
});
describe('orchestrate command', () => {
it('exports command with alias', () => {
expect(orchestrateCommand.command).toStrictEqual(['orchestrate', 'o']);
});
it('has a description', () => {
expect(orchestrateCommand.describe).toBeTruthy();
});
it('has a builder function', () => {
expect(typeof orchestrateCommand.builder).toStrictEqual('function');
});
it('has a handler function', () => {
expect(typeof orchestrateCommand.handler).toStrictEqual('function');
});
it('defines key orchestrator flags', () => {
const { yargs, options } = createFakeYargs();
(orchestrateCommand.builder as Function)(yargs);
expect(options['target-platform']).toBeDefined();
expect(options['target-platform'].demandOption).toStrictEqual(true);
expect(options['provider-strategy']).toBeDefined();
expect(options['provider-strategy'].default).toStrictEqual('aws');
expect(options['aws-stack-name']).toBeDefined();
expect(options['kube-config']).toBeDefined();
expect(options['kube-volume']).toBeDefined();
expect(options['cache-key']).toBeDefined();
expect(options['watch-to-end']).toBeDefined();
expect(options['clone-depth']).toBeDefined();
});
it('does not include build-only options', () => {
const { yargs, options } = createFakeYargs();
(orchestrateCommand.builder as Function)(yargs);
expect(options['build-profile']).toBeUndefined();
expect(options['manual-exit']).toBeUndefined();
expect(options['enable-gpu']).toBeUndefined();
expect(options['android-version-code']).toBeUndefined();
});
});
describe('status command', () => {
it('exports the correct command name', () => {
expect(statusCommand.command).toStrictEqual('status');
});
it('has a description', () => {
expect(statusCommand.describe).toBeTruthy();
});
it('has a handler function', () => {
expect(typeof statusCommand.handler).toStrictEqual('function');
});
it('includes cache-dir option', () => {
const { yargs, options } = createFakeYargs();
(statusCommand.builder as Function)(yargs);
expect(options['cache-dir']).toBeDefined();
expect(options['project-path']).toBeDefined();
});
});
describe('version command', () => {
it('exports the correct command name', () => {
expect(versionCommand.command).toStrictEqual('version');
});
it('has a description', () => {
expect(versionCommand.describe).toBeTruthy();
});
it('has a handler function', () => {
expect(typeof versionCommand.handler).toStrictEqual('function');
});
});
describe('update command', () => {
it('exports the correct command name', () => {
expect(updateCommand.command).toStrictEqual('update');
});
it('has a description', () => {
expect(updateCommand.describe).toBeTruthy();
});
it('has a builder function', () => {
expect(typeof updateCommand.builder).toStrictEqual('function');
});
it('has a handler function', () => {
expect(typeof updateCommand.handler).toStrictEqual('function');
});
it('defines force and version flags', () => {
const { yargs, options } = createFakeYargs();
(updateCommand.builder as Function)(yargs);
expect(options['force']).toBeDefined();
expect(options['force'].type).toStrictEqual('boolean');
expect(options['force'].default).toStrictEqual(false);
expect(options['version']).toBeDefined();
expect(options['version'].type).toStrictEqual('string');
});
});
});

View File

@@ -0,0 +1,221 @@
import { mapCliArgumentsToInput, CliArguments } from '../input-mapper';
import { Cli } from '../../model/cli/cli';
import GitHub from '../../model/github';
afterEach(() => {
jest.restoreAllMocks();
Cli.options = undefined;
});
describe('mapCliArgumentsToInput', () => {
describe('basic mapping', () => {
it('populates Cli.options from CLI arguments', () => {
const cliArguments: CliArguments = {
targetPlatform: 'StandaloneLinux64',
unityVersion: '2022.3.56f1',
projectPath: './my-project',
};
mapCliArgumentsToInput(cliArguments);
expect(Cli.options).toBeDefined();
expect(Cli.options!['targetPlatform']).toStrictEqual('StandaloneLinux64');
expect(Cli.options!['unityVersion']).toStrictEqual('2022.3.56f1');
expect(Cli.options!['projectPath']).toStrictEqual('./my-project');
});
it('disables GitHub Actions input reading', () => {
const cliArguments: CliArguments = { targetPlatform: 'WebGL' };
mapCliArgumentsToInput(cliArguments);
expect(GitHub.githubInputEnabled).toStrictEqual(false);
});
it('sets mode to cli by default when not provided', () => {
const cliArguments: CliArguments = { targetPlatform: 'Android' };
mapCliArgumentsToInput(cliArguments);
expect(Cli.options!['mode']).toStrictEqual('cli');
});
it('preserves an explicitly provided mode', () => {
const cliArguments: CliArguments = { targetPlatform: 'Android', mode: 'custom-mode' };
mapCliArgumentsToInput(cliArguments);
expect(Cli.options!['mode']).toStrictEqual('custom-mode');
});
});
describe('default values', () => {
it('omits undefined values from Cli.options', () => {
const cliArguments: CliArguments = {
targetPlatform: 'StandaloneLinux64',
unityVersion: undefined,
buildName: undefined,
};
mapCliArgumentsToInput(cliArguments);
expect(Cli.options!['targetPlatform']).toStrictEqual('StandaloneLinux64');
expect(Cli.options!).not.toHaveProperty('unityVersion');
expect(Cli.options!).not.toHaveProperty('buildName');
});
});
describe('boolean conversion', () => {
it('converts boolean true to string "true"', () => {
const cliArguments: CliArguments = { manualExit: true };
mapCliArgumentsToInput(cliArguments);
expect(Cli.options!['manualExit']).toStrictEqual('true');
});
it('converts boolean false to string "false"', () => {
const cliArguments: CliArguments = { enableGpu: false };
mapCliArgumentsToInput(cliArguments);
expect(Cli.options!['enableGpu']).toStrictEqual('false');
});
it('converts allowDirtyBuild boolean to string', () => {
const cliArguments: CliArguments = { allowDirtyBuild: true };
mapCliArgumentsToInput(cliArguments);
expect(Cli.options!['allowDirtyBuild']).toStrictEqual('true');
});
});
describe('yargs internal properties', () => {
it('filters out yargs _ property', () => {
const cliArguments: CliArguments = {
targetPlatform: 'iOS',
_: ['build'] as any,
};
mapCliArgumentsToInput(cliArguments);
expect(Cli.options!).not.toHaveProperty('_');
});
it('filters out yargs $0 property', () => {
const cliArguments: CliArguments = {
targetPlatform: 'iOS',
$0: 'game-ci' as any,
};
mapCliArgumentsToInput(cliArguments);
expect(Cli.options!).not.toHaveProperty('$0');
});
});
describe('flag name conversion', () => {
it('passes camelCase keys through directly', () => {
const cliArguments: CliArguments = {
androidKeystoreName: 'my.keystore',
androidKeystorePass: 'secret',
dockerCpuLimit: '4',
dockerMemoryLimit: '8g',
};
mapCliArgumentsToInput(cliArguments);
expect(Cli.options!['androidKeystoreName']).toStrictEqual('my.keystore');
expect(Cli.options!['androidKeystorePass']).toStrictEqual('secret');
expect(Cli.options!['dockerCpuLimit']).toStrictEqual('4');
expect(Cli.options!['dockerMemoryLimit']).toStrictEqual('8g');
});
it('maps all android-related arguments', () => {
const cliArguments: CliArguments = {
androidVersionCode: '42',
androidExportType: 'androidAppBundle',
androidKeystoreBase64: 'base64data',
androidKeyaliasName: 'myalias',
androidKeyaliasPass: 'aliaspass',
androidTargetSdkVersion: '33',
androidSymbolType: 'public',
};
mapCliArgumentsToInput(cliArguments);
expect(Cli.options!['androidVersionCode']).toStrictEqual('42');
expect(Cli.options!['androidExportType']).toStrictEqual('androidAppBundle');
expect(Cli.options!['androidKeystoreBase64']).toStrictEqual('base64data');
expect(Cli.options!['androidKeyaliasName']).toStrictEqual('myalias');
expect(Cli.options!['androidKeyaliasPass']).toStrictEqual('aliaspass');
expect(Cli.options!['androidTargetSdkVersion']).toStrictEqual('33');
expect(Cli.options!['androidSymbolType']).toStrictEqual('public');
});
it('maps docker and container arguments', () => {
const cliArguments: CliArguments = {
dockerIsolationMode: 'hyperv',
dockerWorkspacePath: '/custom/workspace',
containerRegistryRepository: 'custom/editor',
containerRegistryImageVersion: '5',
runAsHostUser: 'true',
chownFilesTo: 'root:root',
};
mapCliArgumentsToInput(cliArguments);
expect(Cli.options!['dockerIsolationMode']).toStrictEqual('hyperv');
expect(Cli.options!['dockerWorkspacePath']).toStrictEqual('/custom/workspace');
expect(Cli.options!['containerRegistryRepository']).toStrictEqual('custom/editor');
expect(Cli.options!['containerRegistryImageVersion']).toStrictEqual('5');
expect(Cli.options!['runAsHostUser']).toStrictEqual('true');
expect(Cli.options!['chownFilesTo']).toStrictEqual('root:root');
});
it('maps orchestrator-related arguments', () => {
const cliArguments: CliArguments = {
providerStrategy: 'k8s',
awsStackName: 'my-stack',
kubeConfig: 'base64config',
kubeVolume: 'my-pvc',
kubeVolumeSize: '10Gi',
kubeStorageClass: 'gp3',
containerCpu: '2048',
containerMemory: '4096',
cacheKey: 'my-cache',
watchToEnd: 'false',
cloneDepth: '100',
};
mapCliArgumentsToInput(cliArguments);
expect(Cli.options!['providerStrategy']).toStrictEqual('k8s');
expect(Cli.options!['awsStackName']).toStrictEqual('my-stack');
expect(Cli.options!['kubeConfig']).toStrictEqual('base64config');
expect(Cli.options!['kubeVolume']).toStrictEqual('my-pvc');
expect(Cli.options!['kubeVolumeSize']).toStrictEqual('10Gi');
expect(Cli.options!['kubeStorageClass']).toStrictEqual('gp3');
expect(Cli.options!['containerCpu']).toStrictEqual('2048');
expect(Cli.options!['containerMemory']).toStrictEqual('4096');
expect(Cli.options!['cacheKey']).toStrictEqual('my-cache');
expect(Cli.options!['watchToEnd']).toStrictEqual('false');
expect(Cli.options!['cloneDepth']).toStrictEqual('100');
});
});
describe('Cli.isCliMode integration', () => {
it('enables CLI mode after mapping', () => {
const cliArguments: CliArguments = { targetPlatform: 'WebGL' };
mapCliArgumentsToInput(cliArguments);
expect(Cli.isCliMode).toStrictEqual(true);
});
it('is not in CLI mode before mapping', () => {
expect(Cli.isCliMode).toStrictEqual(false);
});
});
});

View File

@@ -0,0 +1,83 @@
import type { CommandModule } from 'yargs';
import * as core from '@actions/core';
import { mapCliArgumentsToInput, CliArguments } from '../input-mapper';
interface ActivateArguments extends CliArguments {
unityVersion?: string;
unitySerial?: string;
unityLicensingServer?: string;
}
const activateCommand: CommandModule<object, ActivateArguments> = {
command: 'activate',
describe: 'Verify Unity license configuration',
builder: (yargs) => {
return yargs
.option('unity-version', {
alias: 'unityVersion',
type: 'string',
description: 'Version of Unity to activate',
default: 'auto',
})
.option('unity-licensing-server', {
alias: 'unityLicensingServer',
type: 'string',
description: 'The Unity licensing server address for floating licenses',
default: '',
})
.env('UNITY')
.example(
'UNITY_SERIAL=XXXX-XXXX-XXXX-XXXX game-ci activate',
'Activate Unity using a serial from environment variable',
)
.example(
'game-ci activate --unity-licensing-server http://license-server:8080',
'Activate Unity using a floating license server',
) as any;
},
handler: async (cliArguments) => {
try {
mapCliArgumentsToInput(cliArguments);
const unitySerial = process.env.UNITY_SERIAL;
const unityLicense = process.env.UNITY_LICENSE;
const licensingServer = cliArguments.unityLicensingServer || process.env.UNITY_LICENSING_SERVER || '';
if (licensingServer) {
core.info(`Activating Unity via licensing server: ${licensingServer}`);
core.info('Floating license activation is handled automatically during builds.');
core.info('No manual activation step is needed when using a licensing server.');
return;
}
if (!unitySerial && !unityLicense) {
throw new Error(
'No Unity license found.\n\n' +
'Provide one of the following:\n' +
' - UNITY_SERIAL environment variable (professional license)\n' +
' - UNITY_LICENSE environment variable (personal license file content)\n' +
' - --unity-licensing-server flag (floating license)\n\n' +
'For more information, visit: https://game.ci/docs/github/activation',
);
}
if (unitySerial) {
const maskedSerial = unitySerial.length > 8 ? `${unitySerial.slice(0, 4)}...${unitySerial.slice(-4)}` : '****';
core.info(`Unity serial detected: ${maskedSerial}`);
core.info('License will be activated automatically when running a build.');
} else if (unityLicense) {
core.info('Unity license file detected from UNITY_LICENSE environment variable.');
core.info('License will be activated automatically when running a build.');
}
core.info('\nActivation verified. You can now run: game-ci build --target-platform <platform>');
} catch (error: any) {
core.setFailed(`Activation failed: ${error.message}`);
throw error;
}
},
};
export default activateCommand;

108
src/cli/commands/build.ts Normal file
View File

@@ -0,0 +1,108 @@
import type { CommandModule } from 'yargs';
import * as core from '@actions/core';
import { BuildParameters, ImageTag } from '../../model';
import { mapCliArgumentsToInput, CliArguments } from '../input-mapper';
import MacBuilder from '../../model/mac-builder';
import Docker from '../../model/docker';
import Action from '../../model/action';
import PlatformSetup from '../../model/platform-setup';
import { withProjectOptions, withDockerOptions, withAndroidOptions } from './shared-options';
interface BuildArguments extends CliArguments {
targetPlatform: string;
}
const buildCommand: CommandModule<object, BuildArguments> = {
command: 'build',
describe: 'Build a Unity project locally via Docker or native runner',
builder: (yargs) => {
let y = withProjectOptions(yargs);
y = withAndroidOptions(y);
y = withDockerOptions(y);
return y
.option('build-profile', {
alias: 'buildProfile',
type: 'string',
description: 'Path to the build profile to activate, relative to the project root',
default: '',
})
.option('versioning', {
type: 'string',
description: 'The versioning scheme to use when building the project',
default: 'Semantic',
})
.option('version', {
type: 'string',
description: 'The version, when used with the "Custom" versioning scheme',
default: '',
})
.option('manual-exit', {
alias: 'manualExit',
type: 'boolean',
description: 'Suppresses -quit. Exit your build method using EditorApplication.Exit(0) instead.',
default: false,
})
.option('enable-gpu', {
alias: 'enableGpu',
type: 'boolean',
description: 'Launches unity without specifying -nographics',
default: false,
})
.option('cache-unity-installation-on-mac', {
alias: 'cacheUnityInstallationOnMac',
type: 'boolean',
description: 'Whether to cache the Unity hub and editor installation on MacOS',
default: false,
})
.option('unity-hub-version-on-mac', {
alias: 'unityHubVersionOnMac',
type: 'string',
description: 'The version of Unity Hub to install on MacOS (e.g. 3.4.0). Defaults to latest available on brew.',
default: '',
})
.example('game-ci build --target-platform StandaloneLinux64', 'Build for Linux using auto-detected Unity version')
.example(
'game-ci build --target-platform Android --unity-version 2022.3.56f1 --build-method MyBuild.Run',
'Build for Android with a specific Unity version and build method',
) as any;
},
handler: async (cliArguments) => {
try {
mapCliArgumentsToInput(cliArguments);
const buildParameters = await BuildParameters.create();
const baseImage = new ImageTag(buildParameters);
core.info(`Building locally for ${buildParameters.targetPlatform}...`);
core.info(`Unity version: ${buildParameters.editorVersion}`);
core.info(`Project path: ${buildParameters.projectPath}`);
const actionFolder = Action.actionFolder;
await PlatformSetup.setup(buildParameters, actionFolder);
const exitCode =
process.platform === 'darwin'
? await MacBuilder.run(actionFolder)
: await Docker.run(baseImage.toString(), {
workspace: process.cwd(),
actionFolder,
...buildParameters,
});
core.info(`\nBuild completed with exit code: ${exitCode}`);
core.info(`Build version: ${buildParameters.buildVersion}`);
core.info(`Build path: ${buildParameters.buildPath}`);
if (exitCode !== 0) {
throw new Error(`Build failed with exit code ${exitCode}`);
}
} catch (error: any) {
core.setFailed(`Build failed: ${error.message}`);
throw error;
}
},
};
export default buildCommand;

View File

@@ -0,0 +1,62 @@
import type { CommandModule } from 'yargs';
import * as core from '@actions/core';
import { BuildParameters, ImageTag, Orchestrator } from '../../model';
import { mapCliArgumentsToInput, CliArguments } from '../input-mapper';
import { withProjectOptions, withOrchestratorOptions } from './shared-options';
interface OrchestrateArguments extends CliArguments {
targetPlatform: string;
providerStrategy?: string;
}
const orchestrateCommand: CommandModule<object, OrchestrateArguments> = {
command: ['orchestrate', 'o'],
describe: 'Run a build via orchestrator providers (AWS, Kubernetes, etc.)',
builder: (yargs) => {
let y = withProjectOptions(yargs);
y = withOrchestratorOptions(y);
return y
.option('versioning', {
type: 'string',
description: 'The versioning scheme to use',
default: 'None',
})
.example(
'game-ci orchestrate --target-platform StandaloneLinux64 --provider-strategy aws',
'Build on AWS using the orchestrator',
)
.example(
'game-ci o --target-platform StandaloneLinux64 --provider-strategy k8s --kube-config <base64>',
'Build on Kubernetes (short alias)',
) as any;
},
handler: async (cliArguments) => {
try {
mapCliArgumentsToInput(cliArguments);
const buildParameters = await BuildParameters.create();
const baseImage = new ImageTag(buildParameters);
core.info(`Orchestrating build via ${buildParameters.providerStrategy}...`);
core.info(`Target platform: ${buildParameters.targetPlatform}`);
core.info(`Unity version: ${buildParameters.editorVersion}`);
core.info(`Build GUID: ${buildParameters.buildGuid}`);
const result = await Orchestrator.run(buildParameters, baseImage.toString());
core.info(`\nOrchestrated build completed.`);
if (result?.BuildResults) {
core.info(`Results: ${result.BuildResults}`);
} else {
core.warning('Build completed but no build results were returned.');
}
} catch (error: any) {
core.setFailed(`Orchestrated build failed: ${error.message}`);
throw error;
}
},
};
export default orchestrateCommand;

View File

@@ -0,0 +1,308 @@
import type { Argv } from 'yargs';
/**
* Shared option groups for CLI commands. Avoids duplicating option
* definitions across build, test, and orchestrate commands.
*/
export function withProjectOptions<T>(yargs: Argv<T>) {
return yargs
.option('target-platform', {
alias: 'targetPlatform',
type: 'string' as const,
description: 'Platform that the build should target',
demandOption: true,
})
.option('unity-version', {
alias: 'unityVersion',
type: 'string' as const,
description: 'Version of Unity to use. Use "auto" to detect from ProjectVersion.txt.',
default: 'auto',
})
.option('project-path', {
alias: 'projectPath',
type: 'string' as const,
description: 'Path to the Unity project',
default: '.',
})
.option('build-name', {
alias: 'buildName',
type: 'string' as const,
description: 'Name of the build',
default: '',
})
.option('builds-path', {
alias: 'buildsPath',
type: 'string' as const,
description: 'Path where the builds should be stored',
default: 'build',
})
.option('build-method', {
alias: 'buildMethod',
type: 'string' as const,
description: 'Path to a Namespace.Class.StaticMethod to run to perform the build',
default: '',
})
.option('custom-parameters', {
alias: 'customParameters',
type: 'string' as const,
description: 'Custom parameters to configure the build',
default: '',
})
.option('custom-image', {
alias: 'customImage',
type: 'string' as const,
description: 'Specific docker image that should be used for building the project',
default: '',
})
.option('git-private-token', {
alias: 'gitPrivateToken',
type: 'string' as const,
description: 'GitHub private token for repository access',
default: '',
})
.option('skip-activation', {
alias: 'skipActivation',
type: 'string' as const,
description: 'Skip Unity activation/deactivation',
default: 'false',
})
.option('unity-licensing-server', {
alias: 'unityLicensingServer',
type: 'string' as const,
description: 'The Unity licensing server address',
default: '',
})
.option('container-registry-repository', {
alias: 'containerRegistryRepository',
type: 'string' as const,
description: 'Container registry and repository to pull image from. Only applicable if customImage is not set.',
default: 'unityci/editor',
})
.option('container-registry-image-version', {
alias: 'containerRegistryImageVersion',
type: 'string' as const,
description: 'Container registry image version. Only applicable if customImage is not set.',
default: '3',
});
}
export function withDockerOptions<T>(yargs: Argv<T>) {
return yargs
.option('docker-cpu-limit', {
alias: 'dockerCpuLimit',
type: 'string' as const,
description: 'Number of CPU cores to assign the docker container',
default: '',
})
.option('docker-memory-limit', {
alias: 'dockerMemoryLimit',
type: 'string' as const,
description: 'Amount of memory to assign the docker container (e.g. 512m, 4g)',
default: '',
})
.option('docker-workspace-path', {
alias: 'dockerWorkspacePath',
type: 'string' as const,
description: 'The path to mount the workspace inside the docker container',
default: '/github/workspace',
})
.option('docker-isolation-mode', {
alias: 'dockerIsolationMode',
type: 'string' as const,
description:
'Isolation mode to use for the docker container (process, hyperv, or default). Only applicable on Windows.',
default: 'default',
})
.option('run-as-host-user', {
alias: 'runAsHostUser',
type: 'string' as const,
description: 'Whether to run as a user that matches the host system',
default: 'false',
})
.option('chown-files-to', {
alias: 'chownFilesTo',
type: 'string' as const,
description: 'User and optionally group to give ownership of build artifacts',
default: '',
})
.option('ssh-agent', {
alias: 'sshAgent',
type: 'string' as const,
description: 'SSH Agent path to forward to the container',
default: '',
})
.option('ssh-public-keys-directory-path', {
alias: 'sshPublicKeysDirectoryPath',
type: 'string' as const,
description: 'Path to a directory containing SSH public keys to forward to the container',
default: '',
});
}
export function withAndroidOptions<T>(yargs: Argv<T>) {
return yargs
.option('android-version-code', {
alias: 'androidVersionCode',
type: 'string' as const,
description: 'The android versionCode',
default: '',
})
.option('android-export-type', {
alias: 'androidExportType',
type: 'string' as const,
description: 'The android export type (androidPackage, androidAppBundle, androidStudioProject)',
default: 'androidPackage',
})
.option('android-keystore-name', {
alias: 'androidKeystoreName',
type: 'string' as const,
description: 'The android keystoreName',
default: '',
})
.option('android-keystore-base64', {
alias: 'androidKeystoreBase64',
type: 'string' as const,
description: 'The base64 contents of the android keystore file',
default: '',
})
.option('android-keystore-pass', {
alias: 'androidKeystorePass',
type: 'string' as const,
description: 'The android keystorePass',
default: '',
})
.option('android-keyalias-name', {
alias: 'androidKeyaliasName',
type: 'string' as const,
description: 'The android keyaliasName',
default: '',
})
.option('android-keyalias-pass', {
alias: 'androidKeyaliasPass',
type: 'string' as const,
description: 'The android keyaliasPass',
default: '',
})
.option('android-target-sdk-version', {
alias: 'androidTargetSdkVersion',
type: 'string' as const,
description: 'The android target API level',
default: '',
})
.option('android-symbol-type', {
alias: 'androidSymbolType',
type: 'string' as const,
description: 'The android symbol type to export (none, public, debugging)',
default: 'none',
});
}
export function withOrchestratorOptions<T>(yargs: Argv<T>) {
return yargs
.option('provider-strategy', {
alias: 'providerStrategy',
type: 'string' as const,
description: 'Orchestrator provider: aws, k8s, local-docker, local-system',
default: 'aws',
})
.option('aws-stack-name', {
alias: 'awsStackName',
type: 'string' as const,
description: 'The Cloud Formation stack name (AWS provider)',
default: 'game-ci',
})
.option('kube-config', {
alias: 'kubeConfig',
type: 'string' as const,
description: 'Base64 encoded Kubernetes config (K8s provider)',
default: '',
})
.option('kube-volume', {
alias: 'kubeVolume',
type: 'string' as const,
description: 'Persistent Volume Claim name for Unity build (K8s provider)',
default: '',
})
.option('kube-volume-size', {
alias: 'kubeVolumeSize',
type: 'string' as const,
description: 'Disc space for Kubernetes Persistent Volume',
default: '5Gi',
})
.option('kube-storage-class', {
alias: 'kubeStorageClass',
type: 'string' as const,
description: 'Kubernetes storage class to use for orchestrator jobs. Leave empty to install rook cluster.',
default: '',
})
.option('container-cpu', {
alias: 'containerCpu',
type: 'string' as const,
description: 'CPU allocation for remote build container',
default: '1024',
})
.option('container-memory', {
alias: 'containerMemory',
type: 'string' as const,
description: 'Memory allocation for remote build container',
default: '3072',
})
.option('cache-key', {
alias: 'cacheKey',
type: 'string' as const,
description: 'Cache key to indicate bucket for cache',
default: '',
})
.option('allow-dirty-build', {
alias: 'allowDirtyBuild',
type: 'boolean' as const,
description: 'Allow builds from dirty branches',
default: false,
})
.option('watch-to-end', {
alias: 'watchToEnd',
type: 'string' as const,
description: 'Whether to watch the build to completion',
default: 'true',
})
.option('clone-depth', {
alias: 'cloneDepth',
type: 'string' as const,
description: 'Git clone depth (0 for full clone)',
default: '50',
})
.option('read-input-from-override-list', {
alias: 'readInputFromOverrideList',
type: 'string' as const,
description: 'Comma separated list of input value names to read from the input override command',
default: '',
})
.option('read-input-override-command', {
alias: 'readInputOverrideCommand',
type: 'string' as const,
description: 'Command to execute to pull input from an external source (e.g. cloud provider secret managers)',
default: '',
})
.option('post-build-steps', {
alias: 'postBuildSteps',
type: 'string' as const,
description:
'Post build job in yaml format with the keys image, secrets (name, value object array), command string',
default: '',
})
.option('pre-build-steps', {
alias: 'preBuildSteps',
type: 'string' as const,
description:
'Pre build job after repository setup but before the build job (yaml format with keys image, secrets, command)',
default: '',
})
.option('custom-job', {
alias: 'customJob',
type: 'string' as const,
description:
'Custom job instead of the standard build automation (yaml format with keys image, secrets, command)',
default: '',
});
}

117
src/cli/commands/status.ts Normal file
View File

@@ -0,0 +1,117 @@
import type { CommandModule } from 'yargs';
import * as core from '@actions/core';
import fs from 'node:fs';
import path from 'node:path';
import UnityVersioning from '../../model/unity-versioning';
const statusCommand: CommandModule = {
command: 'status',
describe: 'Show project info, environment, and cache status',
builder: (yargs) => {
return yargs
.option('project-path', {
alias: 'projectPath',
type: 'string',
description: 'Path to the Unity project',
default: '.',
})
.option('cache-dir', {
alias: 'cacheDir',
type: 'string',
description: 'Path to an additional cache directory to inspect',
default: '',
});
},
handler: async (cliArguments) => {
const projectPath = (cliArguments.projectPath as string) || '.';
const cacheDirectory = cliArguments.cacheDir as string;
core.info('game-ci Workspace Status');
core.info('========================\n');
// Project detection
const projectVersionPath = path.join(projectPath, 'ProjectSettings', 'ProjectVersion.txt');
const hasProject = fs.existsSync(projectVersionPath);
core.info(`Project Path: ${path.resolve(projectPath)}`);
core.info(`Unity Project Found: ${hasProject ? 'Yes' : 'No'}`);
if (hasProject) {
try {
const unityVersion = UnityVersioning.determineUnityVersion(projectPath, 'auto');
core.info(`Unity Version: ${unityVersion}`);
} catch {
core.info(`Unity Version: Unable to detect`);
}
// Library cache status
const libraryPath = path.join(projectPath, 'Library');
if (fs.existsSync(libraryPath)) {
const stats = fs.statSync(libraryPath);
core.info(`Library Cache: Present (modified ${stats.mtime.toISOString()})`);
const keyDirectories = ['PackageCache', 'ScriptAssemblies', 'ShaderCache', 'Bee'];
for (const directory of keyDirectories) {
const directoryPath = path.join(libraryPath, directory);
if (fs.existsSync(directoryPath)) {
const directoryStats = fs.statSync(directoryPath);
core.info(` ${directory}/: exists (modified ${directoryStats.mtime.toISOString()})`);
}
}
} else {
core.info(`Library Cache: Not present (clean build required)`);
}
// Cache archive detection
if (cacheDirectory && fs.existsSync(cacheDirectory)) {
core.info(`\nCache Archives (${cacheDirectory}):`);
const cacheFiles = fs.readdirSync(cacheDirectory).filter((f) => f.endsWith('.tar') || f.endsWith('.tar.lz4'));
if (cacheFiles.length > 0) {
for (const file of cacheFiles) {
const filePath = path.join(cacheDirectory, file);
const fileStats = fs.statSync(filePath);
const sizeMegabytes = (fileStats.size / (1024 * 1024)).toFixed(1);
core.info(` - ${file} (${sizeMegabytes} MB, ${fileStats.mtime.toISOString()})`);
}
} else {
core.info(' No cache archives found.');
}
}
// Build output detection
const buildsPath = path.join(projectPath, '..', 'build');
if (fs.existsSync(buildsPath)) {
const builds = fs.readdirSync(buildsPath);
if (builds.length > 0) {
core.info(`\nBuild Outputs (${buildsPath}):`);
for (const build of builds) {
const buildPath = path.join(buildsPath, build);
const buildStats = fs.statSync(buildPath);
core.info(` - ${build} (${buildStats.isDirectory() ? 'dir' : 'file'}, ${buildStats.mtime.toISOString()})`);
}
}
}
}
// Environment
core.info('\nEnvironment:');
core.info(` Platform: ${process.platform}`);
core.info(` Node.js: ${process.version}`);
core.info(` UNITY_SERIAL: ${process.env.UNITY_SERIAL ? 'Set' : 'Not set'}`);
core.info(` UNITY_LICENSE: ${process.env.UNITY_LICENSE ? 'Set' : 'Not set'}`);
core.info(` UNITY_EMAIL: ${process.env.UNITY_EMAIL ? 'Set' : 'Not set'}`);
core.info(` UNITY_PASSWORD: ${process.env.UNITY_PASSWORD ? 'Set' : 'Not set'}`);
// Docker availability
core.info(`\nDocker: Checking...`);
try {
const { execSync } = await import('node:child_process');
const dockerVersion = execSync('docker --version', { encoding: 'utf8' }).trim();
core.info(` ${dockerVersion}`);
} catch {
core.info(` Docker not found or not accessible`);
}
},
};
export default statusCommand;

126
src/cli/commands/test.ts Normal file
View File

@@ -0,0 +1,126 @@
import type { CommandModule } from 'yargs';
import * as core from '@actions/core';
import { BuildParameters, ImageTag } from '../../model';
import { mapCliArgumentsToInput, CliArguments } from '../input-mapper';
import Docker from '../../model/docker';
import Action from '../../model/action';
import PlatformSetup from '../../model/platform-setup';
import { withProjectOptions, withDockerOptions } from './shared-options';
interface TestArguments extends CliArguments {
targetPlatform: string;
testMode?: string;
}
const testCommand: CommandModule<object, TestArguments> = {
command: ['test', 't'],
describe: 'Run tests for a Unity project',
builder: (yargs) => {
let y = withProjectOptions(yargs);
y = withDockerOptions(y);
return y
.option('test-mode', {
alias: 'testMode',
type: 'string',
description: 'The mode to run tests in (EditMode, PlayMode, or All)',
default: 'All',
choices: ['EditMode', 'PlayMode', 'All'],
})
.option('test-results-path', {
alias: 'testResultsPath',
type: 'string',
description: 'Path where test results XML should be stored',
default: 'test-results',
})
.option('test-category', {
alias: 'testCategory',
type: 'string',
description: 'Only run tests in the given category (semicolon-separated)',
default: '',
})
.option('test-filter', {
alias: 'testFilter',
type: 'string',
description: 'Only run tests that match the filter (semicolon-separated)',
default: '',
})
.option('coverage-options', {
alias: 'coverageOptions',
type: 'string',
description: 'Options for code coverage (e.g. assemblyFilters, pathFilters)',
default: '',
})
.option('enable-code-coverage', {
alias: 'enableCodeCoverage',
type: 'boolean',
description: 'Enable code coverage when running tests',
default: false,
})
.option('versioning', {
type: 'string',
description: 'The versioning scheme to use',
default: 'None',
})
.option('cache-unity-installation-on-mac', {
alias: 'cacheUnityInstallationOnMac',
type: 'boolean',
description: 'Whether to cache the Unity hub and editor installation on MacOS',
default: false,
})
.option('unity-hub-version-on-mac', {
alias: 'unityHubVersionOnMac',
type: 'string',
description: 'The version of Unity Hub to install on MacOS (e.g. 3.4.0). Defaults to latest available on brew.',
default: '',
})
.example('game-ci test --target-platform StandaloneLinux64', 'Run all tests for Linux platform')
.example(
'game-ci t --target-platform StandaloneLinux64 --test-mode EditMode',
'Run only EditMode tests (short alias)',
)
.example(
'game-ci test --target-platform StandaloneLinux64 --enable-code-coverage',
'Run tests with code coverage',
) as any;
},
handler: async (cliArguments) => {
try {
// Map test-specific flags into the input system
mapCliArgumentsToInput(cliArguments);
const buildParameters = await BuildParameters.create();
const baseImage = new ImageTag(buildParameters);
const testMode = cliArguments.testMode || 'All';
core.info(`Running Unity tests (${testMode})...`);
core.info(`Target platform: ${buildParameters.targetPlatform}`);
core.info(`Unity version: ${buildParameters.editorVersion}`);
core.info(`Project path: ${buildParameters.projectPath}`);
const actionFolder = Action.actionFolder;
await PlatformSetup.setup(buildParameters, actionFolder);
const exitCode = await Docker.run(baseImage.toString(), {
workspace: process.cwd(),
actionFolder,
...buildParameters,
});
const resultsPath = cliArguments.testResultsPath || 'test-results';
core.info(`\nTests completed with exit code: ${exitCode}`);
core.info(`Test results: ${resultsPath}`);
if (exitCode !== 0) {
throw new Error(`Tests failed with exit code ${exitCode}`);
}
} catch (error: any) {
core.setFailed(`Tests failed: ${error.message}`);
throw error;
}
},
};
export default testCommand;

387
src/cli/commands/update.ts Normal file
View File

@@ -0,0 +1,387 @@
import type { CommandModule } from 'yargs';
import * as core from '@actions/core';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import https from 'node:https';
import http from 'node:http';
import { execFileSync } from 'node:child_process';
const REPO = 'game-ci/unity-builder';
interface GitHubRelease {
// eslint-disable-next-line camelcase
tag_name: string;
assets: Array<{
name: string;
// eslint-disable-next-line camelcase
browser_download_url: string;
size: number;
}>;
}
interface UpdateArguments {
force?: boolean;
version?: string;
}
/**
* Fetches JSON from a URL via HTTPS, following redirects.
*/
function fetchJson(url: string): Promise<any> {
return new Promise((resolve, reject) => {
const get = (targetUrl: string, redirectCount: number) => {
if (redirectCount > 5) {
reject(new Error('Too many redirects'));
return;
}
https
.get(
targetUrl,
{
headers: { 'User-Agent': 'game-ci-cli', Accept: 'application/json' },
},
(response) => {
if (
response.statusCode &&
response.statusCode >= 300 &&
response.statusCode < 400 &&
response.headers.location
) {
get(response.headers.location, redirectCount + 1);
return;
}
if (response.statusCode !== 200) {
reject(new Error(`HTTP ${response.statusCode} from ${targetUrl}`));
return;
}
let data = '';
response.on('data', (chunk) => (data += chunk));
response.on('end', () => {
try {
resolve(JSON.parse(data));
} catch {
reject(new Error('Invalid JSON response'));
}
});
},
)
.on('error', reject);
};
get(url, 0);
});
}
/**
* Downloads a file from a URL, following redirects. Returns the file content as a Buffer.
*/
function downloadFile(url: string): Promise<Buffer> {
return new Promise((resolve, reject) => {
const get = (targetUrl: string, redirectCount: number) => {
if (redirectCount > 10) {
reject(new Error('Too many redirects'));
return;
}
const protocol = targetUrl.startsWith('https') ? https : http;
protocol
.get(targetUrl, { headers: { 'User-Agent': 'game-ci-cli' } }, (response) => {
if (
response.statusCode &&
response.statusCode >= 300 &&
response.statusCode < 400 &&
response.headers.location
) {
get(response.headers.location, redirectCount + 1);
return;
}
if (response.statusCode !== 200) {
reject(new Error(`HTTP ${response.statusCode} downloading ${targetUrl}`));
return;
}
const chunks: Buffer[] = [];
response.on('data', (chunk: Buffer) => chunks.push(chunk));
response.on('end', () => resolve(Buffer.concat(chunks)));
})
.on('error', reject);
};
get(url, 0);
});
}
/**
* Gets the current version from package.json or the compiled binary.
*/
function getCurrentVersion(): string {
// Try reading from package.json at various relative locations
const candidates = [
path.join(__dirname, '..', '..', '..', 'package.json'),
path.join(__dirname, '..', '..', 'package.json'),
path.join(process.cwd(), 'package.json'),
];
for (const candidate of candidates) {
if (fs.existsSync(candidate)) {
try {
const packageData = JSON.parse(fs.readFileSync(candidate, 'utf8'));
if (packageData.version) {
return packageData.version;
}
} catch {
// Continue to next candidate
}
}
}
return 'unknown';
}
/**
* Determines the correct asset name for the current platform/architecture.
*/
function getAssetName(): string {
const platform = process.platform;
const arch = process.arch;
let osPart: string;
switch (platform) {
case 'linux':
osPart = 'linux';
break;
case 'darwin':
osPart = 'macos';
break;
case 'win32':
osPart = 'windows';
break;
default:
throw new Error(`Unsupported platform: ${platform}`);
}
let archPart: string;
switch (arch) {
case 'x64':
archPart = 'x64';
break;
case 'arm64':
archPart = 'arm64';
break;
default:
throw new Error(`Unsupported architecture: ${arch}`);
}
const assetBaseName = `game-ci-${osPart}-${archPart}`;
return osPart === 'windows' ? `${assetBaseName}.exe` : assetBaseName;
}
/**
* Determines the path to the currently running executable.
* For standalone binaries (pkg), process.execPath points to the binary itself.
* For Node.js execution, we return undefined since self-update does not apply.
*/
function getExecutablePath(): string | undefined {
// When running as a pkg binary, process.pkg is defined
if ((process as any).pkg) {
return process.execPath;
}
// When running via Node.js, check if there is a standalone binary in the typical install location
const installDirectory = process.env.GAME_CI_INSTALL || path.join(os.homedir(), '.game-ci', 'bin');
const binaryName = process.platform === 'win32' ? 'game-ci.exe' : 'game-ci';
const installedPath = path.join(installDirectory, binaryName);
if (fs.existsSync(installedPath)) {
return installedPath;
}
return;
}
/**
* Strips leading 'v' from a version string and splits into numeric parts.
*/
function parseVersionParts(version: string): number[] {
return version
.replace(/^v/, '')
.split('.')
.map((part) => Number(part));
}
/**
* Compares two semver strings. Returns:
* -1 if a < b
* 0 if a == b
* 1 if a > b
*/
function compareSemver(a: string, b: string): number {
const partsA = parseVersionParts(a);
const partsB = parseVersionParts(b);
for (let index = 0; index < 3; index++) {
const x = partsA[index] || 0;
const y = partsB[index] || 0;
if (x < y) return -1;
if (x > y) return 1;
}
return 0;
}
const updateCommand: CommandModule<object, UpdateArguments> = {
command: 'update',
describe: 'Update game-ci to the latest version',
builder: (yargs) => {
return yargs
.option('force', {
alias: 'f',
type: 'boolean',
description: 'Force update even if already on latest version',
default: false,
})
.option('version', {
type: 'string',
description: 'Update to a specific version (e.g., v2.0.0)',
default: '',
})
.example('game-ci update', 'Update to the latest version')
.example('game-ci update --version v2.1.0', 'Update to a specific version')
.example('game-ci update --force', 'Force reinstall of the current version') as any;
},
handler: async (cliArguments) => {
try {
const currentVersion = getCurrentVersion();
core.info(`Current version: v${currentVersion}`);
core.info(`Platform: ${process.platform} ${process.arch}`);
core.info('');
// Fetch release info
let release: GitHubRelease;
const targetVersion = cliArguments.version as string;
if (targetVersion) {
const tag = targetVersion.startsWith('v') ? targetVersion : `v${targetVersion}`;
core.info(`Fetching release ${tag}...`);
release = await fetchJson(`https://api.github.com/repos/${REPO}/releases/tags/${tag}`);
} else {
core.info('Checking for updates...');
release = await fetchJson(`https://api.github.com/repos/${REPO}/releases/latest`);
}
const latestVersion = release.tag_name;
core.info(`Latest version: ${latestVersion}`);
core.info('');
// Compare versions
const comparison = compareSemver(currentVersion, latestVersion);
if (comparison >= 0 && !cliArguments.force) {
core.info('You are already on the latest version. Use --force to reinstall.');
return;
}
if (comparison > 0 && !targetVersion) {
core.info(`Current version (v${currentVersion}) is newer than latest release (${latestVersion}).`);
core.info('Use --force to downgrade, or --version to target a specific release.');
return;
}
// Find the correct asset
const assetName = getAssetName();
const asset = release.assets.find((a) => a.name === assetName);
if (!asset) {
const available = release.assets.map((a) => a.name).join(', ');
throw new Error(
`No binary found for ${process.platform}-${process.arch} (looking for ${assetName}).\nAvailable assets: ${available}`,
);
}
const sizeMb = (asset.size / (1024 * 1024)).toFixed(1);
core.info(`Downloading ${assetName} (${sizeMb} MB)...`);
// Download the new binary
const binaryData = await downloadFile(asset.browser_download_url);
// Determine where to write the updated binary
const executablePath = getExecutablePath();
if (!executablePath) {
core.info('');
core.info('game-ci is running via Node.js (not as a standalone binary).');
core.info('To update the npm package, run:');
core.info(' npm install -g unity-builder@latest');
core.info('');
core.info('To install the standalone binary instead:');
core.info(' curl -fsSL https://raw.githubusercontent.com/game-ci/unity-builder/main/install.sh | sh');
return;
}
// Write the new binary.
// On Windows, we cannot overwrite a running executable directly.
// Write to a temporary file, then rename.
const temporaryPath = `${executablePath}.update`;
const backupPath = `${executablePath}.backup`;
fs.writeFileSync(temporaryPath, binaryData);
if (process.platform !== 'win32') {
fs.chmodSync(temporaryPath, 0o755);
}
// Verify the downloaded binary
try {
const output = execFileSync(temporaryPath, ['version'], { encoding: 'utf8', timeout: 10_000 });
core.info(`Verified new binary: ${output.trim().split('\n')[0]}`);
} catch (verifyError: any) {
fs.unlinkSync(temporaryPath);
throw new Error(`Downloaded binary failed verification: ${verifyError.message}`);
}
// Replace the current binary
try {
// Backup current
if (fs.existsSync(backupPath)) {
fs.unlinkSync(backupPath);
}
fs.renameSync(executablePath, backupPath);
fs.renameSync(temporaryPath, executablePath);
// Clean up backup
try {
fs.unlinkSync(backupPath);
} catch {
// On Windows the backup may be locked; that is fine
}
} catch (replaceError: any) {
// Attempt to restore from backup
if (fs.existsSync(backupPath) && !fs.existsSync(executablePath)) {
fs.renameSync(backupPath, executablePath);
}
// Clean up temporary file
if (fs.existsSync(temporaryPath)) {
fs.unlinkSync(temporaryPath);
}
throw new Error(`Failed to replace binary: ${replaceError.message}`);
}
core.info('');
core.info(`Successfully updated game-ci to ${latestVersion}`);
} catch (error: any) {
core.error(`Update failed: ${error.message}`);
throw error;
}
},
};
export default updateCommand;

View File

@@ -0,0 +1,37 @@
import type { CommandModule } from 'yargs';
import * as core from '@actions/core';
import fs from 'node:fs';
import path from 'node:path';
const versionCommand: CommandModule = {
command: 'version',
describe: 'Show version info',
builder: {},
handler: async () => {
try {
// Read version from package.json
let packageJsonPath = path.join(__dirname, '..', '..', '..', 'package.json');
if (!fs.existsSync(packageJsonPath)) {
packageJsonPath = path.join(__dirname, '..', '..', 'package.json');
}
if (!fs.existsSync(packageJsonPath)) {
packageJsonPath = path.join(process.cwd(), 'package.json');
}
if (fs.existsSync(packageJsonPath)) {
const packageData = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
core.info(`game-ci (unity-builder) v${packageData.version}`);
core.info(`Node.js ${process.version}`);
core.info(`Platform: ${process.platform} ${process.arch}`);
} else {
core.info('game-ci (unity-builder)');
core.info('Version information unavailable');
}
} catch (error: any) {
core.info('game-ci (unity-builder)');
core.error(`Could not read version: ${error.message}`);
}
},
};
export default versionCommand;

113
src/cli/input-mapper.ts Normal file
View File

@@ -0,0 +1,113 @@
import { Cli } from '../model/cli/cli';
import GitHub from '../model/github';
/**
* Maps CLI arguments (kebab-case flags) to the Input/OrchestratorOptions
* interface used by the action. This bridges the gap between user-friendly
* CLI flags and the camelCase environment/input system unity-builder expects.
*
* The existing Input class already queries Cli.options, environment variables,
* and GitHub Action inputs in priority order. We populate Cli.options so that
* the rest of the codebase works unchanged.
*/
export interface CliArguments {
targetPlatform?: string;
unityVersion?: string;
projectPath?: string;
buildProfile?: string;
buildName?: string;
buildsPath?: string;
buildMethod?: string;
customParameters?: string;
versioning?: string;
version?: string;
customImage?: string;
manualExit?: boolean;
enableGpu?: boolean;
androidVersionCode?: string;
androidExportType?: string;
androidKeystoreName?: string;
androidKeystoreBase64?: string;
androidKeystorePass?: string;
androidKeyaliasName?: string;
androidKeyaliasPass?: string;
androidTargetSdkVersion?: string;
androidSymbolType?: string;
dockerCpuLimit?: string;
dockerMemoryLimit?: string;
dockerIsolationMode?: string;
dockerWorkspacePath?: string;
containerRegistryRepository?: string;
containerRegistryImageVersion?: string;
runAsHostUser?: string;
chownFilesTo?: string;
sshAgent?: string;
sshPublicKeysDirectoryPath?: string;
gitPrivateToken?: string;
providerStrategy?: string;
awsStackName?: string;
kubeConfig?: string;
kubeVolume?: string;
kubeVolumeSize?: string;
kubeStorageClass?: string;
containerCpu?: string;
containerMemory?: string;
cacheKey?: string;
watchToEnd?: string;
allowDirtyBuild?: boolean;
skipActivation?: string;
cloneDepth?: string;
readInputFromOverrideList?: string;
readInputOverrideCommand?: string;
postBuildSteps?: string;
preBuildSteps?: string;
customJob?: string;
unityLicensingServer?: string;
cacheUnityInstallationOnMac?: boolean;
unityHubVersionOnMac?: string;
testMode?: string;
testResultsPath?: string;
testCategory?: string;
testFilter?: string;
coverageOptions?: string;
enableCodeCoverage?: boolean;
mode?: string;
[key: string]: unknown;
}
/**
* Converts kebab-case CLI flags to camelCase keys matching the Input class
* property names, then injects them into Cli.options so the existing
* Input.getInput() / OrchestratorOptions.getInput() chain picks them up.
*/
export function mapCliArgumentsToInput(cliArguments: CliArguments): void {
// Disable GitHub Actions input reading when in CLI mode
GitHub.githubInputEnabled = false;
// The existing Cli.options mechanism is used by Input.getInput() to query
// CLI-provided values. We set it directly.
const mapped: Record<string, unknown> = {};
for (const [key, value] of Object.entries(cliArguments)) {
if (value !== undefined && key !== '_' && key !== '$0') {
mapped[key] = typeof value === 'boolean' ? String(value) : value;
}
}
// Ensure mode is set so Cli.isCliMode returns true
if (!mapped['mode']) {
mapped['mode'] = 'cli';
}
Cli.options = mapped;
}

View File

@@ -1,8 +1,9 @@
import * as core from '@actions/core';
import { Action, BuildParameters, Cache, CloudRunner, Docker, ImageTag, Output } from './model';
import { Action, BuildParameters, Cache, Orchestrator, Docker, ImageTag, Output } from './model';
import { Cli } from './model/cli/cli';
import MacBuilder from './model/mac-builder';
import PlatformSetup from './model/platform-setup';
async function runMain() {
try {
if (Cli.InitCliMode()) {
@@ -18,22 +19,35 @@ async function runMain() {
const buildParameters = await BuildParameters.create();
const baseImage = new ImageTag(buildParameters);
if (buildParameters.cloudRunnerCluster !== 'local') {
await CloudRunner.run(buildParameters, baseImage.toString());
} else {
let exitCode = -1;
if (buildParameters.providerStrategy === 'local') {
core.info('Building locally');
await PlatformSetup.setup(buildParameters, actionFolder);
if (process.platform === 'darwin') {
MacBuilder.run(actionFolder, workspace, buildParameters);
} else {
await Docker.run(baseImage, { workspace, actionFolder, ...buildParameters });
}
exitCode =
process.platform === 'darwin'
? await MacBuilder.run(actionFolder)
: await Docker.run(baseImage.toString(), {
workspace,
actionFolder,
...buildParameters,
});
} else {
await Orchestrator.run(buildParameters, baseImage.toString());
exitCode = 0;
}
// Set output
await Output.setBuildVersion(buildParameters.buildVersion);
await Output.setAndroidVersionCode(buildParameters.androidVersionCode);
await Output.setEngineExitCode(exitCode);
if (exitCode !== 0) {
core.setFailed(`Build failed with exit code ${exitCode}`);
}
} catch (error) {
core.setFailed((error as Error).message);
}
}
runMain();

View File

@@ -0,0 +1,29 @@
// Integration test for exercising real GitHub check creation and updates.
import Orchestrator from '../model/orchestrator/orchestrator';
import UnityVersioning from '../model/unity-versioning';
import GitHub from '../model/github';
import { TIMEOUT_INFINITE, createParameters } from '../test-utils/orchestrator-test-helpers';
const runIntegration = process.env.RUN_GITHUB_INTEGRATION_TESTS === 'true';
const describeOrSkip = runIntegration ? describe : describe.skip;
describeOrSkip('Orchestrator Github Checks Integration', () => {
it(
'creates and updates a real GitHub check',
async () => {
const buildParameter = await createParameters({
versioning: 'None',
projectPath: 'test-project',
unityVersion: UnityVersioning.read('test-project'),
asyncOrchestrator: `true`,
githubChecks: `true`,
});
await Orchestrator.setup(buildParameter);
const checkId = await GitHub.createGitHubCheck(`integration create`);
expect(checkId).not.toEqual('');
await GitHub.updateGitHubCheck(`1 ${new Date().toISOString()}`, `integration`);
await GitHub.updateGitHubCheck(`2 ${new Date().toISOString()}`, `integration`, `success`, `completed`);
},
TIMEOUT_INFINITE,
);
});

View File

@@ -1,4 +1,4 @@
import { stat } from 'fs/promises';
import { stat } from 'node:fs/promises';
describe('Integrity tests', () => {
describe('package-lock.json', () => {

3
src/jest.globals.ts Normal file
View File

@@ -0,0 +1,3 @@
import { fetch as undiciFetch, Headers, Request, Response } from 'undici';
Object.assign(globalThis, { fetch: undiciFetch, Headers, Request, Response });

View File

@@ -1,5 +1,5 @@
import path from 'path';
import fs from 'fs';
import path from 'node:path';
import fs from 'node:fs';
import Action from './action';
describe('Action', () => {

View File

@@ -1,23 +1,27 @@
import path from 'path';
import path from 'node:path';
class Action {
static get supportedPlatforms() {
static get supportedPlatforms(): string[] {
return ['linux', 'win32', 'darwin'];
}
static get isRunningLocally() {
static get isRunningLocally(): boolean {
return process.env.RUNNER_WORKSPACE === undefined;
}
static get isRunningFromSource() {
static get isRunningFromSource(): boolean {
return path.basename(__dirname) === 'model';
}
static get canonicalName() {
static get canonicalName(): string {
if (Action.isRunningFromSource) {
return path.basename(path.dirname(path.join(path.dirname(__filename), '/..')));
}
return 'unity-builder';
}
static get rootFolder() {
static get rootFolder(): string {
if (Action.isRunningFromSource) {
return path.dirname(path.dirname(path.dirname(__filename)));
}
@@ -25,12 +29,12 @@ class Action {
return path.dirname(path.dirname(__filename));
}
static get actionFolder() {
static get actionFolder(): string {
return `${Action.rootFolder}/dist`;
}
static get workspace() {
return process.env.GITHUB_WORKSPACE;
static get workspace(): string {
return process.env.GITHUB_WORKSPACE!;
}
static checkCompatibility() {

View File

@@ -3,15 +3,15 @@ import AndroidVersioning from './android-versioning';
describe('Android Versioning', () => {
describe('versionToVersionCode', () => {
it('defaults to 0 when versioning strategy is none', () => {
expect(AndroidVersioning.versionToVersionCode('none')).toBe(0);
expect(AndroidVersioning.versionToVersionCode('none')).toBe('0');
});
it('defaults to 1 when version is not a valid semver', () => {
expect(AndroidVersioning.versionToVersionCode('abcd')).toBe(1);
expect(AndroidVersioning.versionToVersionCode('abcd')).toBe('1');
});
it('returns a number', () => {
expect(AndroidVersioning.versionToVersionCode('123.456.789')).toBe(123456789);
expect(AndroidVersioning.versionToVersionCode('123.456.789')).toBe('123456789');
});
it('throw when generated version code is too large', () => {
@@ -21,11 +21,11 @@ describe('Android Versioning', () => {
describe('determineVersionCode', () => {
it('defaults to parsed version', () => {
expect(AndroidVersioning.determineVersionCode('1.2.3', '')).toBe(1002003);
expect(AndroidVersioning.determineVersionCode('1.2.3', '')).toBe('1002003');
});
it('use specified code', () => {
expect(AndroidVersioning.determineVersionCode('1.2.3', 2)).toBe(2);
expect(AndroidVersioning.determineVersionCode('1.2.3', '2')).toBe('2');
});
});

View File

@@ -2,19 +2,19 @@ import * as core from '@actions/core';
import * as semver from 'semver';
export default class AndroidVersioning {
static determineVersionCode(version, inputVersionCode) {
if (!inputVersionCode) {
static determineVersionCode(version: string, inputVersionCode: string): string {
if (inputVersionCode === '') {
return AndroidVersioning.versionToVersionCode(version);
}
return inputVersionCode;
}
static versionToVersionCode(version) {
static versionToVersionCode(version: string): string {
if (version === 'none') {
core.info(`Versioning strategy is set to ${version}, so android version code should not be applied.`);
return 0;
return '0';
}
const parsedVersion = semver.parse(version);
@@ -22,7 +22,7 @@ export default class AndroidVersioning {
if (!parsedVersion) {
core.warning(`Could not parse "${version}" to semver, defaulting android version code to 1`);
return 1;
return '1';
}
// The greatest value Google Plays allows is 2100000000.
@@ -36,10 +36,10 @@ export default class AndroidVersioning {
}
core.info(`Using android versionCode ${versionCode}`);
return versionCode;
return versionCode.toString();
}
static determineSdkManagerParameters(targetSdkVersion) {
static determineSdkManagerParameters(targetSdkVersion: string) {
const parsedVersion = Number.parseInt(targetSdkVersion.slice(-2), 10);
return Number.isNaN(parsedVersion) ? '' : `platforms;android-${parsedVersion}`;

View File

@@ -5,21 +5,17 @@ import BuildParameters from './build-parameters';
import Input from './input';
import Platform from './platform';
// Todo - Don't use process.env directly, that's what the input model class is for.
const testLicense =
'<?xml version="1.0" encoding="UTF-8"?><root>\n <License id="Terms">\n <MachineBindings>\n <Binding Key="1" Value="576562626572264761624c65526f7578"/>\n <Binding Key="2" Value="576562626572264761624c65526f7578"/>\n </MachineBindings>\n <MachineID Value="D7nTUnjNAmtsUMcnoyrqkgIbYdM="/>\n <SerialHash Value="2033b8ac3e6faa3742ca9f0bfae44d18f2a96b80"/>\n <Features>\n <Feature Value="33"/>\n <Feature Value="1"/>\n <Feature Value="12"/>\n <Feature Value="2"/>\n <Feature Value="24"/>\n <Feature Value="3"/>\n <Feature Value="36"/>\n <Feature Value="17"/>\n <Feature Value="19"/>\n <Feature Value="62"/>\n </Features>\n <DeveloperData Value="AQAAAEY0LUJHUlgtWEQ0RS1aQ1dWLUM1SlctR0RIQg=="/>\n <SerialMasked Value="F4-BGRX-XD4E-ZCWV-C5JW-XXXX"/>\n <StartDate Value="2021-02-08T00:00:00"/>\n <UpdateDate Value="2021-02-09T00:34:57"/>\n <InitialActivationDate Value="2021-02-08T00:34:56"/>\n <LicenseVersion Value="6.x"/>\n <ClientProvidedVersion Value="2018.4.30f1"/>\n <AlwaysOnline Value="false"/>\n <Entitlements>\n <Entitlement Ns="unity_editor" Tag="UnityPersonal" Type="EDITOR" ValidTo="9999-12-31T00:00:00"/>\n <Entitlement Ns="unity_editor" Tag="DarkSkin" Type="EDITOR_FEATURE" ValidTo="9999-12-31T00:00:00"/>\n </Entitlements>\n </License>\n<Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"/><SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/><Reference URI="#Terms"><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/></Transforms><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><DigestValue>m0Db8UK+ktnOLJBtHybkfetpcKo=</DigestValue></Reference></SignedInfo><SignatureValue>o/pUbSQAukz7+ZYAWhnA0AJbIlyyCPL7bKVEM2lVqbrXt7cyey+umkCXamuOgsWPVUKBMkXtMH8L\n5etLmD0getWIhTGhzOnDCk+gtIPfL4jMo9tkEuOCROQAXCci23VFscKcrkB+3X6h4wEOtA2APhOY\nB+wvC794o8/82ffjP79aVAi57rp3Wmzx+9pe9yMwoJuljAy2sc2tIMgdQGWVmOGBpQm3JqsidyzI\nJWG2kjnc7pDXK9pwYzXoKiqUqqrut90d+kQqRyv7MSZXR50HFqD/LI69h68b7P8Bjo3bPXOhNXGR\n9YCoemH6EkfCJxp2gIjzjWW+l2Hj2EsFQi8YXw==</SignatureValue></Signature></root>';
process.env.UNITY_LICENSE = testLicense;
const determineVersion = jest.spyOn(Versioning, 'determineBuildVersion').mockImplementation(async () => '1.3.37');
const determineUnityVersion = jest
.spyOn(UnityVersioning, 'determineUnityVersion')
.mockImplementation(() => '2019.2.11f1');
const determineSdkManagerParameters = jest
.spyOn(AndroidVersioning, 'determineSdkManagerParameters')
.mockImplementation(() => 'platforms;android-30');
afterEach(() => {
jest.clearAllMocks();
jest.restoreAllMocks();
});
beforeEach(() => {
jest.spyOn(Versioning, 'determineBuildVersion').mockImplementation(async () => '1.3.37');
process.env.UNITY_LICENSE = testLicense; // Todo - Don't use process.env directly, that's what the input model class is for.
});
describe('BuildParameters', () => {
@@ -29,48 +25,62 @@ describe('BuildParameters', () => {
});
it('determines the version only once', async () => {
jest.spyOn(Versioning, 'determineBuildVersion').mockImplementation(async () => '1.3.37');
await BuildParameters.create();
expect(determineVersion).toHaveBeenCalledTimes(1);
await expect(Versioning.determineBuildVersion).toHaveBeenCalledTimes(1);
});
it('determines the unity version only once', async () => {
jest.spyOn(UnityVersioning, 'determineUnityVersion').mockImplementation(() => '2019.2.11f1');
await BuildParameters.create();
expect(determineUnityVersion).toHaveBeenCalledTimes(1);
expect(UnityVersioning.determineUnityVersion).toHaveBeenCalledTimes(1);
});
it('returns the android version code with provided input', async () => {
const mockValue = '42';
jest.spyOn(Input, 'androidVersionCode', 'get').mockReturnValue(mockValue);
expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ androidVersionCode: mockValue }));
await expect(BuildParameters.create()).resolves.toEqual(
expect.objectContaining({ androidVersionCode: mockValue }),
);
});
it('returns the android version code from version by default', async () => {
const mockValue = '';
jest.spyOn(Input, 'androidVersionCode', 'get').mockReturnValue(mockValue);
expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ androidVersionCode: 1003037 }));
await expect(BuildParameters.create()).resolves.toEqual(
expect.objectContaining({ androidVersionCode: '1003037' }),
);
});
it('determines the android sdk manager parameters only once', async () => {
jest.spyOn(AndroidVersioning, 'determineSdkManagerParameters').mockImplementation(() => 'platforms;android-30');
await BuildParameters.create();
expect(determineSdkManagerParameters).toHaveBeenCalledTimes(1);
expect(AndroidVersioning.determineSdkManagerParameters).toHaveBeenCalledTimes(1);
});
it('returns the targetPlatform', async () => {
const mockValue = 'somePlatform';
jest.spyOn(Input, 'targetPlatform', 'get').mockReturnValue(mockValue);
expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ targetPlatform: mockValue }));
await expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ targetPlatform: mockValue }));
});
it('returns the project path', async () => {
const mockValue = 'path/to/project';
jest.spyOn(UnityVersioning, 'determineUnityVersion').mockImplementation(() => '2019.2.11f1');
jest.spyOn(Input, 'projectPath', 'get').mockReturnValue(mockValue);
expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ projectPath: mockValue }));
await expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ projectPath: mockValue }));
});
it('returns the build profile', async () => {
const mockValue = 'path/to/build_profile.asset';
jest.spyOn(Input, 'buildProfile', 'get').mockReturnValue(mockValue);
await expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ buildProfile: mockValue }));
});
it('returns the build name', async () => {
const mockValue = 'someBuildName';
jest.spyOn(Input, 'buildName', 'get').mockReturnValue(mockValue);
expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ buildName: mockValue }));
await expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ buildName: mockValue }));
});
it('returns the build path', async () => {
@@ -79,92 +89,135 @@ describe('BuildParameters', () => {
const expectedBuildPath = `${mockPath}/${mockPlatform}`;
jest.spyOn(Input, 'buildsPath', 'get').mockReturnValue(mockPath);
jest.spyOn(Input, 'targetPlatform', 'get').mockReturnValue(mockPlatform);
expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ buildPath: expectedBuildPath }));
await expect(BuildParameters.create()).resolves.toEqual(
expect.objectContaining({ buildPath: expectedBuildPath }),
);
});
it('returns the build file', async () => {
const mockValue = 'someBuildName';
const mockPlatform = 'somePlatform';
jest.spyOn(Input, 'buildName', 'get').mockReturnValue(mockValue);
expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ buildFile: mockValue }));
jest.spyOn(Input, 'targetPlatform', 'get').mockReturnValue(mockPlatform);
await expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ buildFile: mockValue }));
});
test.each([Platform.types.StandaloneWindows, Platform.types.StandaloneWindows64])(
'appends exe for %s',
async (targetPlatform) => {
test.each`
targetPlatform | expectedExtension | androidExportType
${Platform.types.Android} | ${'.apk'} | ${'androidPackage'}
${Platform.types.Android} | ${'.aab'} | ${'androidAppBundle'}
${Platform.types.Android} | ${''} | ${'androidStudioProject'}
${Platform.types.StandaloneWindows} | ${'.exe'} | ${'n/a'}
${Platform.types.StandaloneWindows64} | ${'.exe'} | ${'n/a'}
`(
'appends $expectedExtension for $targetPlatform with androidExportType $androidExportType',
async ({ targetPlatform, expectedExtension, androidExportType }) => {
jest.spyOn(Input, 'targetPlatform', 'get').mockReturnValue(targetPlatform);
jest.spyOn(Input, 'buildName', 'get').mockReturnValue(targetPlatform);
expect(BuildParameters.create()).resolves.toEqual(
expect.objectContaining({ buildFile: `${targetPlatform}.exe` }),
jest.spyOn(Input, 'androidExportType', 'get').mockReturnValue(androidExportType);
await expect(BuildParameters.create()).resolves.toEqual(
expect.objectContaining({ buildFile: `${targetPlatform}${expectedExtension}` }),
);
},
);
test.each([Platform.types.Android])('appends apk for %s', async (targetPlatform) => {
jest.spyOn(Input, 'targetPlatform', 'get').mockReturnValue(targetPlatform);
jest.spyOn(Input, 'buildName', 'get').mockReturnValue(targetPlatform);
jest.spyOn(Input, 'androidAppBundle', 'get').mockReturnValue(false);
expect(BuildParameters.create()).resolves.toEqual(
expect.objectContaining({ buildFile: `${targetPlatform}.apk` }),
);
});
test.each([Platform.types.Android])('appends aab for %s', async (targetPlatform) => {
jest.spyOn(Input, 'targetPlatform', 'get').mockReturnValue(targetPlatform);
jest.spyOn(Input, 'buildName', 'get').mockReturnValue(targetPlatform);
jest.spyOn(Input, 'androidAppBundle', 'get').mockReturnValue(true);
expect(BuildParameters.create()).resolves.toEqual(
expect.objectContaining({ buildFile: `${targetPlatform}.aab` }),
);
});
test.each`
targetPlatform | androidSymbolType
${Platform.types.Android} | ${'none'}
${Platform.types.Android} | ${'public'}
${Platform.types.Android} | ${'debugging'}
${Platform.types.StandaloneWindows} | ${'none'}
${Platform.types.StandaloneWindows64} | ${'none'}
`(
'androidSymbolType is set to $androidSymbolType when targetPlatform is $targetPlatform and input targetSymbolType is $androidSymbolType',
async ({ targetPlatform, androidSymbolType }) => {
jest.spyOn(Input, 'targetPlatform', 'get').mockReturnValue(targetPlatform);
jest.spyOn(Input, 'androidSymbolType', 'get').mockReturnValue(androidSymbolType);
jest.spyOn(Input, 'buildName', 'get').mockReturnValue(targetPlatform);
await expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ androidSymbolType }));
},
);
it('returns the build method', async () => {
const mockValue = 'Namespace.ClassName.BuildMethod';
jest.spyOn(Input, 'buildMethod', 'get').mockReturnValue(mockValue);
expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ buildMethod: mockValue }));
await expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ buildMethod: mockValue }));
});
it('returns the android keystore name', async () => {
const mockValue = 'keystore.keystore';
jest.spyOn(Input, 'androidKeystoreName', 'get').mockReturnValue(mockValue);
expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ androidKeystoreName: mockValue }));
await expect(BuildParameters.create()).resolves.toEqual(
expect.objectContaining({ androidKeystoreName: mockValue }),
);
});
it('returns the android keystore base64-encoded content', async () => {
const mockValue = 'secret';
jest.spyOn(Input, 'androidKeystoreBase64', 'get').mockReturnValue(mockValue);
expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ androidKeystoreBase64: mockValue }));
await expect(BuildParameters.create()).resolves.toEqual(
expect.objectContaining({ androidKeystoreBase64: mockValue }),
);
});
it('returns the android keystore pass', async () => {
const mockValue = 'secret';
jest.spyOn(Input, 'androidKeystorePass', 'get').mockReturnValue(mockValue);
expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ androidKeystorePass: mockValue }));
await expect(BuildParameters.create()).resolves.toEqual(
expect.objectContaining({ androidKeystorePass: mockValue }),
);
});
it('returns the android keyalias name', async () => {
const mockValue = 'secret';
jest.spyOn(Input, 'androidKeyaliasName', 'get').mockReturnValue(mockValue);
expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ androidKeyaliasName: mockValue }));
await expect(BuildParameters.create()).resolves.toEqual(
expect.objectContaining({ androidKeyaliasName: mockValue }),
);
});
it('returns the android keyalias pass', async () => {
const mockValue = 'secret';
jest.spyOn(Input, 'androidKeyaliasPass', 'get').mockReturnValue(mockValue);
expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ androidKeyaliasPass: mockValue }));
await expect(BuildParameters.create()).resolves.toEqual(
expect.objectContaining({ androidKeyaliasPass: mockValue }),
);
});
it('returns the android target sdk version', async () => {
const mockValue = 'AndroidApiLevelAuto';
jest.spyOn(Input, 'androidTargetSdkVersion', 'get').mockReturnValue(mockValue);
expect(BuildParameters.create()).resolves.toEqual(
await expect(BuildParameters.create()).resolves.toEqual(
expect.objectContaining({ androidTargetSdkVersion: mockValue }),
);
});
it('returns the unity licensing server address', async () => {
const mockValue = 'http://example.com';
jest.spyOn(Input, 'unityLicensingServer', 'get').mockReturnValue(mockValue);
await expect(BuildParameters.create()).resolves.toEqual(
expect.objectContaining({ unityLicensingServer: mockValue }),
);
});
it('throws error when no unity license provider provided', async () => {
delete process.env.UNITY_LICENSE; // Need to delete this as it is set for every test currently
await expect(BuildParameters.create()).rejects.toThrowError();
});
it('return serial when no license server is provided', async () => {
const mockValue = '123';
delete process.env.UNITY_LICENSE; // Need to delete this as it is set for every test currently
process.env.UNITY_SERIAL = mockValue;
await expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ unitySerial: mockValue }));
delete process.env.UNITY_SERIAL;
});
it('returns the custom parameters', async () => {
const mockValue = '-profile SomeProfile -someBoolean -someValue exampleValue';
jest.spyOn(Input, 'customParameters', 'get').mockReturnValue(mockValue);
expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ customParameters: mockValue }));
await expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ customParameters: mockValue }));
});
});
});

View File

@@ -1,7 +1,7 @@
import { customAlphabet } from 'nanoid';
import AndroidVersioning from './android-versioning';
import CloudRunnerConstants from './cloud-runner/services/cloud-runner-constants';
import CloudRunnerBuildGuid from './cloud-runner/services/cloud-runner-guid';
import OrchestratorConstants from './orchestrator/options/orchestrator-constants';
import OrchestratorBuildGuid from './orchestrator/options/orchestrator-guid';
import Input from './input';
import Platform from './platform';
import UnityVersioning from './unity-versioning';
@@ -9,19 +9,31 @@ import Versioning from './versioning';
import { GitRepoReader } from './input-readers/git-repo';
import { GithubCliReader } from './input-readers/github-cli';
import { Cli } from './cli/cli';
import GitHub from './github';
import OrchestratorOptions from './orchestrator/options/orchestrator-options';
import Orchestrator from './orchestrator/orchestrator';
import * as core from '@actions/core';
class BuildParameters {
// eslint-disable-next-line no-undef
[key: string]: any;
public editorVersion!: string;
public customImage!: string;
public unitySerial!: string;
public runnerTempPath: string | undefined;
public unityLicensingServer!: string;
public skipActivation!: string;
public runnerTempPath!: string;
public targetPlatform!: string;
public projectPath!: string;
public buildProfile!: string;
public buildName!: string;
public buildPath!: string;
public buildFile!: string;
public buildMethod!: string;
public buildVersion!: string;
public manualExit!: boolean;
public enableGpu!: boolean;
public androidVersionCode!: string;
public androidKeystoreName!: string;
public androidKeystoreBase64!: string;
@@ -30,78 +42,136 @@ class BuildParameters {
public androidKeyaliasPass!: string;
public androidTargetSdkVersion!: string;
public androidSdkManagerParameters!: string;
public androidExportType!: string;
public androidSymbolType!: string;
public dockerCpuLimit!: string;
public dockerMemoryLimit!: string;
public dockerIsolationMode!: string;
public containerRegistryRepository!: string;
public containerRegistryImageVersion!: string;
public customParameters!: string;
public sshAgent!: string;
public cloudRunnerCluster!: string;
public awsBaseStackName!: string;
public sshPublicKeysDirectoryPath!: string;
public providerStrategy!: string;
public gitPrivateToken!: string;
public awsStackName!: string;
public awsEndpoint?: string;
public awsCloudFormationEndpoint?: string;
public awsEcsEndpoint?: string;
public awsKinesisEndpoint?: string;
public awsCloudWatchLogsEndpoint?: string;
public awsS3Endpoint?: string;
public storageProvider!: string;
public rcloneRemote!: string;
public kubeConfig!: string;
public cloudRunnerMemory!: string;
public cloudRunnerCpu!: string;
public containerMemory!: string;
public containerCpu!: string;
public containerNamespace!: string;
public kubeVolumeSize!: string;
public kubeVolume!: string;
public kubeStorageClass!: string;
public runAsHostUser!: string;
public chownFilesTo!: string;
public customJobHooks!: string;
public cachePushOverrideCommand!: string;
public cachePullOverrideCommand!: string;
public readInputFromOverrideList!: string;
public readInputOverrideCommand!: string;
public checkDependencyHealthOverride!: string;
public startDependenciesOverride!: string;
public commandHooks!: string;
public pullInputList!: string[];
public inputPullCommand!: string;
public cacheKey!: string;
public postBuildSteps!: string;
public preBuildSteps!: string;
public postBuildContainerHooks!: string;
public preBuildContainerHooks!: string;
public customJob!: string;
public runNumber!: string;
public branch!: string;
public githubRepo!: string;
public orchestratorRepoName!: string;
public cloneDepth!: number;
public gitSha!: string;
public logId!: string;
public buildGuid!: string;
public cloudRunnerBranch!: string;
public cloudRunnerIntegrationTests!: boolean;
public cloudRunnerBuilderPlatform!: string | undefined;
public orchestratorBranch!: string;
public orchestratorDebug!: boolean | undefined;
public buildPlatform!: string | undefined;
public isCliMode!: boolean;
public maxRetainedWorkspaces!: number;
public useLargePackages!: boolean;
public useCompressionStrategy!: boolean;
public garbageMaxAge!: number;
public githubChecks!: boolean;
public asyncWorkflow!: boolean;
public githubCheckId!: string;
public finalHooks!: string[];
public skipLfs!: boolean;
public skipCache!: boolean;
public cacheUnityInstallationOnMac!: boolean;
public unityHubVersionOnMac!: string;
public dockerWorkspacePath!: string;
public static shouldUseRetainedWorkspaceMode(buildParameters: BuildParameters) {
return buildParameters.maxRetainedWorkspaces > 0 && Orchestrator.lockedWorkspace !== ``;
}
static async create(): Promise<BuildParameters> {
const buildFile = this.parseBuildFile(Input.buildName, Input.targetPlatform, Input.androidAppBundle);
const buildFile = this.parseBuildFile(Input.buildName, Input.targetPlatform, Input.androidExportType);
const editorVersion = UnityVersioning.determineUnityVersion(Input.projectPath, Input.unityVersion);
const buildVersion = await Versioning.determineBuildVersion(Input.versioningStrategy, Input.specifiedVersion);
const androidVersionCode = AndroidVersioning.determineVersionCode(buildVersion, Input.androidVersionCode);
const androidSdkManagerParameters = AndroidVersioning.determineSdkManagerParameters(Input.androidTargetSdkVersion);
// Todo - Don't use process.env directly, that's what the input model class is for.
// ---
let unitySerial = '';
if (!process.env.UNITY_SERIAL && Input.githubInputEnabled) {
// No serial was present, so it is a personal license that we need to convert
if (!process.env.UNITY_LICENSE) {
throw new Error(`Missing Unity License File and no Serial was found. If this
is a personal license, make sure to follow the activation
steps and set the UNITY_LICENSE GitHub secret or enter a Unity
serial number inside the UNITY_SERIAL GitHub secret.`);
const androidSymbolExportType = Input.androidSymbolType;
if (Platform.isAndroid(Input.targetPlatform)) {
switch (androidSymbolExportType) {
case 'none':
case 'public':
case 'debugging':
break;
default:
throw new Error(
`Invalid androidSymbolType: ${Input.androidSymbolType}. Must be one of: none, public, debugging`,
);
}
unitySerial = this.getSerialFromLicenseFile(process.env.UNITY_LICENSE);
} else {
unitySerial = process.env.UNITY_SERIAL!;
}
let unitySerial = '';
if (Input.unityLicensingServer === '') {
if (!Input.unitySerial && GitHub.githubInputEnabled) {
// No serial was present, so it is a personal license that we need to convert
if (!Input.unityLicense) {
throw new Error(
`Missing Unity License File and no Serial was found. If this
is a personal license, make sure to follow the activation
steps and set the UNITY_LICENSE GitHub secret or enter a Unity
serial number inside the UNITY_SERIAL GitHub secret.`,
);
}
unitySerial = this.getSerialFromLicenseFile(Input.unityLicense);
} else {
unitySerial = Input.unitySerial!;
}
}
if (unitySerial !== undefined && unitySerial.length === 27) {
core.setSecret(unitySerial);
core.setSecret(`${unitySerial.slice(0, -4)}XXXX`);
}
return {
editorVersion,
customImage: Input.customImage,
unitySerial,
runnerTempPath: process.env.RUNNER_TEMP,
unityLicensingServer: Input.unityLicensingServer,
skipActivation: Input.skipActivation,
runnerTempPath: Input.runnerTempPath,
targetPlatform: Input.targetPlatform,
projectPath: Input.projectPath,
buildProfile: Input.buildProfile,
buildName: Input.buildName,
buildPath: `${Input.buildsPath}/${Input.targetPlatform}`,
buildFile,
buildMethod: Input.buildMethod,
buildVersion,
manualExit: Input.manualExit,
enableGpu: Input.enableGpu,
androidVersionCode,
androidKeystoreName: Input.androidKeystoreName,
androidKeystoreBase64: Input.androidKeystoreBase64,
@@ -110,56 +180,95 @@ class BuildParameters {
androidKeyaliasPass: Input.androidKeyaliasPass,
androidTargetSdkVersion: Input.androidTargetSdkVersion,
androidSdkManagerParameters,
androidExportType: Input.androidExportType,
androidSymbolType: androidSymbolExportType,
customParameters: Input.customParameters,
sshAgent: Input.sshAgent,
gitPrivateToken: Input.gitPrivateToken || (await GithubCliReader.GetGitHubAuthToken()),
sshPublicKeysDirectoryPath: Input.sshPublicKeysDirectoryPath,
gitPrivateToken: Input.gitPrivateToken ?? (await GithubCliReader.GetGitHubAuthToken()),
runAsHostUser: Input.runAsHostUser,
chownFilesTo: Input.chownFilesTo,
cloudRunnerCluster: Input.cloudRunnerCluster,
cloudRunnerBuilderPlatform: Input.cloudRunnerBuilderPlatform,
awsBaseStackName: Input.awsBaseStackName,
kubeConfig: Input.kubeConfig,
cloudRunnerMemory: Input.cloudRunnerMemory,
cloudRunnerCpu: Input.cloudRunnerCpu,
kubeVolumeSize: Input.kubeVolumeSize,
kubeVolume: Input.kubeVolume,
postBuildSteps: Input.postBuildSteps,
preBuildSteps: Input.preBuildSteps,
customJob: Input.customJob,
dockerCpuLimit: Input.dockerCpuLimit,
dockerMemoryLimit: Input.dockerMemoryLimit,
dockerIsolationMode: Input.dockerIsolationMode,
containerRegistryRepository: Input.containerRegistryRepository,
containerRegistryImageVersion: Input.containerRegistryImageVersion,
providerStrategy: OrchestratorOptions.providerStrategy,
buildPlatform: OrchestratorOptions.buildPlatform,
kubeConfig: OrchestratorOptions.kubeConfig,
containerMemory: OrchestratorOptions.containerMemory,
containerCpu: OrchestratorOptions.containerCpu,
containerNamespace: OrchestratorOptions.containerNamespace,
kubeVolumeSize: OrchestratorOptions.kubeVolumeSize,
kubeVolume: OrchestratorOptions.kubeVolume,
postBuildContainerHooks: OrchestratorOptions.postBuildContainerHooks,
preBuildContainerHooks: OrchestratorOptions.preBuildContainerHooks,
customJob: OrchestratorOptions.customJob,
runNumber: Input.runNumber,
branch: Input.branch.replace('/head', '') || (await GitRepoReader.GetBranch()),
cloudRunnerBranch: Input.cloudRunnerBranch.split('/').reverse()[0],
cloudRunnerIntegrationTests: Input.cloudRunnerTests,
githubRepo: Input.githubRepo || (await GitRepoReader.GetRemote()) || 'game-ci/unity-builder',
orchestratorBranch: OrchestratorOptions.orchestratorBranch.split('/').reverse()[0],
orchestratorDebug: OrchestratorOptions.orchestratorDebug,
githubRepo: (Input.githubRepo ?? (await GitRepoReader.GetRemote())) || OrchestratorOptions.orchestratorRepoName,
orchestratorRepoName: OrchestratorOptions.orchestratorRepoName,
cloneDepth: Number.parseInt(OrchestratorOptions.cloneDepth),
isCliMode: Cli.isCliMode,
awsStackName: Input.awsBaseStackName,
awsStackName: OrchestratorOptions.awsStackName,
awsEndpoint: OrchestratorOptions.awsEndpoint,
awsCloudFormationEndpoint: OrchestratorOptions.awsCloudFormationEndpoint,
awsEcsEndpoint: OrchestratorOptions.awsEcsEndpoint,
awsKinesisEndpoint: OrchestratorOptions.awsKinesisEndpoint,
awsCloudWatchLogsEndpoint: OrchestratorOptions.awsCloudWatchLogsEndpoint,
awsS3Endpoint: OrchestratorOptions.awsS3Endpoint,
storageProvider: OrchestratorOptions.storageProvider,
rcloneRemote: OrchestratorOptions.rcloneRemote,
gitSha: Input.gitSha,
logId: customAlphabet(CloudRunnerConstants.alphabet, 9)(),
buildGuid: CloudRunnerBuildGuid.generateGuid(Input.runNumber, Input.targetPlatform),
customJobHooks: Input.customJobHooks(),
cachePullOverrideCommand: Input.cachePullOverrideCommand(),
cachePushOverrideCommand: Input.cachePushOverrideCommand(),
readInputOverrideCommand: Input.readInputOverrideCommand(),
readInputFromOverrideList: Input.readInputFromOverrideList(),
kubeStorageClass: Input.kubeStorageClass,
checkDependencyHealthOverride: Input.checkDependencyHealthOverride,
startDependenciesOverride: Input.startDependenciesOverride,
cacheKey: Input.cacheKey,
logId: customAlphabet(OrchestratorConstants.alphabet, 9)(),
buildGuid: OrchestratorBuildGuid.generateGuid(Input.runNumber, Input.targetPlatform),
commandHooks: OrchestratorOptions.commandHooks,
inputPullCommand: OrchestratorOptions.inputPullCommand,
pullInputList: OrchestratorOptions.pullInputList,
kubeStorageClass: OrchestratorOptions.kubeStorageClass,
cacheKey: OrchestratorOptions.cacheKey,
maxRetainedWorkspaces: Number.parseInt(OrchestratorOptions.maxRetainedWorkspaces),
useLargePackages: OrchestratorOptions.useLargePackages,
useCompressionStrategy: OrchestratorOptions.useCompressionStrategy,
garbageMaxAge: OrchestratorOptions.garbageMaxAge,
githubChecks: OrchestratorOptions.githubChecks,
asyncWorkflow: OrchestratorOptions.asyncOrchestrator,
githubCheckId: OrchestratorOptions.githubCheckId,
finalHooks: OrchestratorOptions.finalHooks,
skipLfs: OrchestratorOptions.skipLfs,
skipCache: OrchestratorOptions.skipCache,
cacheUnityInstallationOnMac: Input.cacheUnityInstallationOnMac,
unityHubVersionOnMac: Input.unityHubVersionOnMac,
dockerWorkspacePath: Input.dockerWorkspacePath,
};
}
static parseBuildFile(filename, platform, androidAppBundle) {
static parseBuildFile(filename: string, platform: string, androidExportType: string): string {
if (Platform.isWindows(platform)) {
return `${filename}.exe`;
}
if (Platform.isAndroid(platform)) {
return androidAppBundle ? `${filename}.aab` : `${filename}.apk`;
switch (androidExportType) {
case `androidPackage`:
return `${filename}.apk`;
case `androidAppBundle`:
return `${filename}.aab`;
case `androidStudioProject`:
return filename;
default:
throw new Error(
`Unknown Android Export Type: ${androidExportType}. Must be one of androidPackage for apk, androidAppBundle for aab, androidStudioProject for android project`,
);
}
}
return filename;
}
static getSerialFromLicenseFile(license) {
static getSerialFromLicenseFile(license: string) {
const startKey = `<DeveloperData Value="`;
const endKey = `"/>`;
const startIndex = license.indexOf(startKey) + startKey.length;

View File

@@ -1,5 +1,5 @@
import * as core from '@actions/core';
import fs from 'fs';
import fs from 'node:fs';
import Action from './action';
import Project from './project';

View File

@@ -16,7 +16,7 @@ export class CliFunctionsRepository {
});
}
public static GetCliFunctions(key) {
public static GetCliFunctions(key: any) {
const results = CliFunctionsRepository.targets.find((x) => x.key === key);
if (results === undefined || results.length === 0) {
throw new Error(`no CLI mode found for ${key}`);

View File

@@ -1,21 +1,24 @@
import { Command } from 'commander-ts';
import { BuildParameters, CloudRunner, ImageTag, Input } from '..';
import { BuildParameters, Orchestrator, ImageTag, Input } from '..';
import * as core from '@actions/core';
import { ActionYamlReader } from '../input-readers/action-yaml';
import CloudRunnerLogger from '../cloud-runner/services/cloud-runner-logger';
import CloudRunnerQueryOverride from '../cloud-runner/services/cloud-runner-query-override';
import OrchestratorLogger from '../orchestrator/services/core/orchestrator-logger';
import OrchestratorQueryOverride from '../orchestrator/options/orchestrator-query-override';
import { CliFunction, CliFunctionsRepository } from './cli-functions-repository';
import { AwsCliCommands } from '../cloud-runner/providers/aws/commands/aws-cli-commands';
import { Caching } from '../cloud-runner/remote-client/caching';
import { LfsHashing } from '../cloud-runner/services/lfs-hashing';
import { RemoteClient } from '../cloud-runner/remote-client';
import { Caching } from '../orchestrator/remote-client/caching';
import { LfsHashing } from '../orchestrator/services/utility/lfs-hashing';
import { RemoteClient } from '../orchestrator/remote-client';
import OrchestratorOptionsReader from '../orchestrator/options/orchestrator-options-reader';
import GitHub from '../github';
import { OptionValues } from 'commander';
import { InputKey } from '../input';
export class Cli {
public static options;
public static options: OptionValues | undefined;
static get isCliMode() {
return Cli.options !== undefined && Cli.options.mode !== undefined && Cli.options.mode !== '';
}
public static query(key, alternativeKey) {
public static query(key: string, alternativeKey: string) {
if (Cli.options && Cli.options[key] !== undefined) {
return Cli.options[key];
}
@@ -27,13 +30,13 @@ export class Cli {
}
public static InitCliMode() {
CliFunctionsRepository.PushCliFunctionSource(AwsCliCommands);
CliFunctionsRepository.PushCliFunctionSource(RemoteClient);
CliFunctionsRepository.PushCliFunctionSource(Caching);
CliFunctionsRepository.PushCliFunctionSource(LfsHashing);
CliFunctionsRepository.PushCliFunctionSource(RemoteClient);
const program = new Command();
program.version('0.0.1');
const properties = Object.getOwnPropertyNames(Input);
const properties = OrchestratorOptionsReader.GetProperties();
const actionYamlReader: ActionYamlReader = new ActionYamlReader();
for (const element of properties) {
program.option(`--${element} <${element}>`, actionYamlReader.GetActionYamlValue(element));
@@ -48,6 +51,8 @@ export class Cli {
program.option('--cachePushFrom <cachePushFrom>', 'cache push from source folder');
program.option('--cachePushTo <cachePushTo>', 'cache push to caching folder');
program.option('--artifactName <artifactName>', 'caching artifact name');
program.option('--select <select>', 'select a particular resource');
program.option('--logFile <logFile>', 'output to log file (log stream only)');
program.parse(process.argv);
Cli.options = program.opts();
@@ -55,43 +60,116 @@ export class Cli {
}
static async RunCli(): Promise<void> {
Input.githubInputEnabled = false;
if (Cli.options['populateOverride'] === `true`) {
await CloudRunnerQueryOverride.PopulateQueryOverrideInput();
GitHub.githubInputEnabled = false;
if (Cli.options!['populateOverride'] === `true`) {
await OrchestratorQueryOverride.PopulateQueryOverrideInput();
}
Cli.logInput();
const results = CliFunctionsRepository.GetCliFunctions(Cli.options.mode);
CloudRunnerLogger.log(`Entrypoint: ${results.key}`);
Cli.options.versioning = 'None';
if (Cli.options!['logInput']) {
Cli.logInput();
}
const results = CliFunctionsRepository.GetCliFunctions(Cli.options?.mode);
OrchestratorLogger.log(`Entrypoint: ${results.key}`);
Cli.options!.versioning = 'None';
return await results.target[results.propertyKey]();
Orchestrator.buildParameters = await BuildParameters.create();
Orchestrator.buildParameters.buildGuid = process.env.BUILD_GUID || ``;
OrchestratorLogger.log(`Build Params:
${JSON.stringify(Orchestrator.buildParameters, undefined, 4)}
`);
Orchestrator.lockedWorkspace = process.env.LOCKED_WORKSPACE || ``;
OrchestratorLogger.log(`Locked Workspace: ${Orchestrator.lockedWorkspace}`);
await Orchestrator.setup(Orchestrator.buildParameters);
return await results.target[results.propertyKey](Cli.options);
}
@CliFunction(`print-input`, `prints all input`)
private static logInput() {
core.info(`\n`);
core.info(`INPUT:`);
const properties = Object.getOwnPropertyNames(Input);
const properties = OrchestratorOptionsReader.GetProperties();
for (const element of properties) {
if (
Input[element] !== undefined &&
Input[element] !== '' &&
typeof Input[element] !== `function` &&
element in Input &&
Input[element as InputKey] !== undefined &&
Input[element as InputKey] !== '' &&
typeof Input[element as InputKey] !== `function` &&
element !== 'length' &&
element !== 'cliOptions' &&
element !== 'prototype'
) {
core.info(`${element} ${Input[element]}`);
core.info(`${element} ${Input[element as InputKey]}`);
}
}
core.info(`\n`);
}
@CliFunction(`cli`, `runs a cloud runner build`)
@CliFunction(`cli-build`, `runs a orchestrator build`)
public static async CLIBuild(): Promise<string> {
const buildParameter = await BuildParameters.create();
const baseImage = new ImageTag(buildParameter);
return await CloudRunner.run(buildParameter, baseImage.toString());
return (await Orchestrator.run(buildParameter, baseImage.toString())).BuildResults;
}
@CliFunction(`async-workflow`, `runs a orchestrator build`)
public static async asyncronousWorkflow(): Promise<string> {
const buildParameter = await BuildParameters.create();
const baseImage = new ImageTag(buildParameter);
await Orchestrator.setup(buildParameter);
return (await Orchestrator.run(buildParameter, baseImage.toString())).BuildResults;
}
@CliFunction(`checks-update`, `runs a orchestrator build`)
public static async checksUpdate() {
const buildParameter = await BuildParameters.create();
await Orchestrator.setup(buildParameter);
const input = JSON.parse(process.env.CHECKS_UPDATE || ``);
core.info(`Checks Update ${process.env.CHECKS_UPDATE}`);
if (input.mode === `create`) {
throw new Error(`Not supported: only use update`);
} else if (input.mode === `update`) {
await GitHub.updateGitHubCheckRequest(input.data);
}
}
@CliFunction(`garbage-collect`, `runs garbage collection`)
public static async GarbageCollect(): Promise<string> {
const buildParameter = await BuildParameters.create();
await Orchestrator.setup(buildParameter);
return await Orchestrator.Provider.garbageCollect(``, false, 0, false, false);
}
@CliFunction(`list-resources`, `lists active resources`)
public static async ListResources(): Promise<string[]> {
const buildParameter = await BuildParameters.create();
await Orchestrator.setup(buildParameter);
const result = await Orchestrator.Provider.listResources();
OrchestratorLogger.log(JSON.stringify(result, undefined, 4));
return result.map((x) => x.Name);
}
@CliFunction(`list-workflow`, `lists running workflows`)
public static async ListWorkflow(): Promise<string[]> {
const buildParameter = await BuildParameters.create();
await Orchestrator.setup(buildParameter);
return (await Orchestrator.Provider.listWorkflow()).map((x) => x.Name);
}
@CliFunction(`watch`, `follows logs of a running workflow`)
public static async Watch(): Promise<string> {
const buildParameter = await BuildParameters.create();
await Orchestrator.setup(buildParameter);
return await Orchestrator.Provider.watchWorkflow();
}
}

View File

@@ -1,3 +0,0 @@
export class CloudRunnerStatics {
public static readonly logPrefix = `Cloud-Runner`;
}

View File

@@ -1,13 +0,0 @@
import CloudRunnerEnvironmentVariable from './services/cloud-runner-environment-variable';
import CloudRunnerSecret from './services/cloud-runner-secret';
export class CloudRunnerStepState {
public image: string;
public environment: CloudRunnerEnvironmentVariable[];
public secrets: CloudRunnerSecret[];
constructor(image: string, environmentVariables: CloudRunnerEnvironmentVariable[], secrets: CloudRunnerSecret[]) {
this.image = image;
this.environment = environmentVariables;
this.secrets = secrets;
}
}

View File

@@ -1,142 +0,0 @@
import { BuildParameters, ImageTag } from '..';
import CloudRunner from './cloud-runner';
import Input from '../input';
import { CloudRunnerStatics } from './cloud-runner-statics';
import { TaskParameterSerializer } from './services/task-parameter-serializer';
import UnityVersioning from '../unity-versioning';
import { Cli } from '../cli/cli';
import CloudRunnerLogger from './services/cloud-runner-logger';
import { v4 as uuidv4 } from 'uuid';
describe('Cloud Runner', () => {
it('responds', () => {});
});
describe('Cloud Runner', () => {
const testSecretName = 'testSecretName';
const testSecretValue = 'testSecretValue';
if (Input.cloudRunnerTests) {
it('All build parameters sent to cloud runner as env vars', async () => {
// Build parameters
Cli.options = {
versioning: 'None',
projectPath: 'test-project',
unityVersion: UnityVersioning.read('test-project'),
targetPlatform: 'StandaloneLinux64',
customJob: `
- name: 'step 1'
image: 'alpine'
commands: 'printenv'
secrets:
- name: '${testSecretName}'
value: '${testSecretValue}'
`,
};
Input.githubInputEnabled = false;
// Setup parameters
const buildParameter = await BuildParameters.create();
Input.githubInputEnabled = true;
const baseImage = new ImageTag(buildParameter);
// Run the job
const file = await CloudRunner.run(buildParameter, baseImage.toString());
// Assert results
expect(file).toContain(JSON.stringify(buildParameter));
expect(file).toContain(`${Input.ToEnvVarFormat(testSecretName)}=${testSecretValue}`);
const environmentVariables = TaskParameterSerializer.readBuildEnvironmentVariables();
const newLinePurgedFile = file
.replace(/\s+/g, '')
.replace(new RegExp(`\\[${CloudRunnerStatics.logPrefix}\\]`, 'g'), '');
for (const element of environmentVariables) {
if (element.value !== undefined && typeof element.value !== 'function') {
if (typeof element.value === `string`) {
element.value = element.value.replace(/\s+/g, '');
}
CloudRunnerLogger.log(`checking input/build param ${element.name} ${element.value}`);
}
}
for (const element of environmentVariables) {
if (element.value !== undefined && typeof element.value !== 'function') {
expect(newLinePurgedFile).toContain(`${element.name}`);
expect(newLinePurgedFile).toContain(`${element.name}=${element.value}`);
}
}
delete Cli.options;
}, 1000000);
it('Run one build it should not use cache, run subsequent build which should use cache', async () => {
Cli.options = {
versioning: 'None',
projectPath: 'test-project',
unityVersion: UnityVersioning.determineUnityVersion('test-project', UnityVersioning.read('test-project')),
targetPlatform: 'StandaloneLinux64',
cacheKey: `test-case-${uuidv4()}`,
};
Input.githubInputEnabled = false;
const buildParameter = await BuildParameters.create();
const baseImage = new ImageTag(buildParameter);
const results = await CloudRunner.run(buildParameter, baseImage.toString());
const libraryString = 'Rebuilding Library because the asset database could not be found!';
const buildSucceededString = 'Build succeeded';
expect(results).toContain(libraryString);
expect(results).toContain(buildSucceededString);
CloudRunnerLogger.log(`run 1 succeeded`);
const buildParameter2 = await BuildParameters.create();
const baseImage2 = new ImageTag(buildParameter2);
const results2 = await CloudRunner.run(buildParameter2, baseImage2.toString());
CloudRunnerLogger.log(`run 2 succeeded`);
expect(results2).toContain(buildSucceededString);
expect(results2).toEqual(expect.not.stringContaining(libraryString));
Input.githubInputEnabled = true;
delete Cli.options;
}, 1000000);
}
it('Local cloud runner returns commands', async () => {
// Build parameters
Cli.options = {
versioning: 'None',
projectPath: 'test-project',
unityVersion: UnityVersioning.read('test-project'),
cloudRunnerCluster: 'local-system',
targetPlatform: 'StandaloneLinux64',
customJob: `
- name: 'step 1'
image: 'alpine'
commands: 'dir'
secrets:
- name: '${testSecretName}'
value: '${testSecretValue}'
`,
};
Input.githubInputEnabled = false;
// Setup parameters
const buildParameter = await BuildParameters.create();
const baseImage = new ImageTag(buildParameter);
// Run the job
await expect(CloudRunner.run(buildParameter, baseImage.toString())).resolves.not.toThrow();
Input.githubInputEnabled = true;
delete Cli.options;
}, 1000000);
it('Test cloud runner returns commands', async () => {
// Build parameters
Cli.options = {
versioning: 'None',
projectPath: 'test-project',
unityVersion: UnityVersioning.read('test-project'),
cloudRunnerCluster: 'test',
targetPlatform: 'StandaloneLinux64',
};
Input.githubInputEnabled = false;
// Setup parameters
const buildParameter = await BuildParameters.create();
const baseImage = new ImageTag(buildParameter);
// Run the job
await expect(CloudRunner.run(buildParameter, baseImage.toString())).resolves.not.toThrow();
Input.githubInputEnabled = true;
delete Cli.options;
}, 1000000);
});

Some files were not shown because too many files have changed in this diff Show More