Compare commits

..

32 Commits

Author SHA1 Message Date
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
196 changed files with 102630 additions and 157764 deletions

View File

@@ -1,11 +1,22 @@
{ {
"plugins": ["jest", "@typescript-eslint", "prettier", "unicorn"], "plugins": [
"extends": ["plugin:unicorn/recommended", "plugin:github/recommended", "plugin:prettier/recommended"], "jest",
"@typescript-eslint",
"prettier",
"unicorn"
],
"extends": [
"plugin:unicorn/recommended",
"plugin:github/recommended",
"plugin:prettier/recommended"
],
"parser": "@typescript-eslint/parser", "parser": "@typescript-eslint/parser",
"parserOptions": { "parserOptions": {
"ecmaVersion": 2020, "ecmaVersion": 2020,
"sourceType": "module", "sourceType": "module",
"extraFileExtensions": [".mjs"], "extraFileExtensions": [
".mjs"
],
"ecmaFeatures": { "ecmaFeatures": {
"impliedStrict": true "impliedStrict": true
}, },
@@ -22,7 +33,10 @@
// Namespaces or sometimes needed // Namespaces or sometimes needed
"import/no-namespace": "off", "import/no-namespace": "off",
// Properly format comments // Properly format comments
"spaced-comment": ["error", "always"], "spaced-comment": [
"error",
"always"
],
"lines-around-comment": [ "lines-around-comment": [
"error", "error",
{ {
@@ -38,14 +52,31 @@
// Mandatory spacing // Mandatory spacing
"padding-line-between-statements": [ "padding-line-between-statements": [
"error", "error",
{ "blankLine": "always", "prev": "*", "next": "return" }, {
{ "blankLine": "always", "prev": "directive", "next": "*" }, "blankLine": "always",
{ "blankLine": "any", "prev": "directive", "next": "directive" } "prev": "*",
"next": "return"
},
{
"blankLine": "always",
"prev": "directive",
"next": "*"
},
{
"blankLine": "any",
"prev": "directive",
"next": "directive"
}
], ],
// Enforce camelCase // Enforce camelCase
"camelcase": "error", "camelcase": "error",
// Allow forOfStatements // Allow forOfStatements
"no-restricted-syntax": ["error", "ForInStatement", "LabeledStatement", "WithStatement"], "no-restricted-syntax": [
"error",
"ForInStatement",
"LabeledStatement",
"WithStatement"
],
// Continue is viable in forOf loops in generators // Continue is viable in forOf loops in generators
"no-continue": "off", "no-continue": "off",
// From experience, named exports are almost always desired. I got tired of this rule // From experience, named exports are almost always desired. I got tired of this rule
@@ -53,8 +84,17 @@
// Unused vars are useful to keep method signatures consistent and documented // Unused vars are useful to keep method signatures consistent and documented
"@typescript-eslint/no-unused-vars": "off", "@typescript-eslint/no-unused-vars": "off",
// For this project only use kebab-case // 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' // 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"
} }
} }

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 #### Checklist
<!-- please check all items and add your own --> <!-- 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) - [ ] Readme (updated or not needed)
- [ ] Tests (added, updated or not needed) - [ ] Tests (added, updated or not needed)

View File

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

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

@@ -0,0 +1,79 @@
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.32f1
- 2022.3.13f1
- 2023.1.19f1
- 2023.2.2f1
targetPlatform:
- StandaloneOSX # Build a MacOS executable
- iOS # Build an iOS executable
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 }}
with:
buildName: 'GameCI Test Build'
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@v4
with:
name: Build ${{ matrix.targetPlatform }} on MacOS (${{ matrix.unityVersion }})
path: build
retention-days: 14

View File

@@ -1,10 +1,12 @@
name: Builds name: Builds - Ubuntu
on: on:
push: { branches: [main] } workflow_dispatch:
pull_request: push:
paths-ignore:
- '.github/**' concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
env: env:
UNITY_LICENSE: UNITY_LICENSE:
@@ -34,44 +36,40 @@ env:
jobs: jobs:
buildForAllPlatformsUbuntu: buildForAllPlatformsUbuntu:
name: Build for ${{ matrix.targetPlatform }} on version ${{ matrix.unityVersion }} name: ${{ matrix.targetPlatform }} on ${{ matrix.unityVersion }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
cloudRunnerCluster: providerStrategy:
# - local-docker # - local-docker
- local - local
projectPath: projectPath:
- test-project - test-project
unityVersion: unityVersion:
- 2019.2.11f1 - 2021.3.32f1
- 2019.3.15f1 - 2022.3.13f1
- 2023.1.19f1
- 2023.2.2f1
targetPlatform: targetPlatform:
- StandaloneOSX # Build a macOS standalone (Intel 64-bit). - StandaloneOSX # Build a macOS standalone (Intel 64-bit) with mono backend.
- StandaloneWindows64 # Build a Windows 64-bit standalone. - StandaloneWindows64 # Build a Windows 64-bit standalone with mono backend.
- StandaloneLinux64 # Build a Linux 64-bit standalone. - StandaloneLinux64 # Build a Linux 64-bit standalone with mono backend.
- iOS # Build an iOS player. - iOS # Build an iOS player.
- Android # Build an Android .apk. - Android # Build an Android .apk.
- WebGL # WebGL. - 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: steps:
########################### ###########################
# Checkout # # Checkout #
########################### ###########################
- uses: actions/checkout@v3 - uses: actions/checkout@v4
with: with:
lfs: true lfs: true
########################### ###########################
# Cache # # Cache #
########################### ###########################
- uses: actions/cache@v3 - uses: actions/cache@v4
with: with:
path: ${{ matrix.projectPath }}/Library path: ${{ matrix.projectPath }}/Library
key: Library-${{ matrix.projectPath }}-ubuntu-${{ matrix.targetPlatform }} key: Library-${{ matrix.projectPath }}-ubuntu-${{ matrix.targetPlatform }}
@@ -82,19 +80,69 @@ jobs:
########################### ###########################
# Build # # Build #
########################### ###########################
- uses: ./ - name: Build
uses: ./
id: build-1
continue-on-error: true
env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
with: with:
buildName: 'GameCI Test Build'
projectPath: ${{ matrix.projectPath }} projectPath: ${{ matrix.projectPath }}
unityVersion: ${{ matrix.unityVersion }} unityVersion: ${{ matrix.unityVersion }}
targetPlatform: ${{ matrix.targetPlatform }} targetPlatform: ${{ matrix.targetPlatform }}
customParameters: -profile SomeProfile -someBoolean -someValue exampleValue customParameters: -profile SomeProfile -someBoolean -someValue exampleValue
cloudRunnerCluster: ${{ matrix.cloudRunnerCluster }} providerStrategy: ${{ matrix.providerStrategy }}
- 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 }}
unityVersion: ${{ matrix.unityVersion }}
targetPlatform: ${{ matrix.targetPlatform }}
customParameters: -profile SomeProfile -someBoolean -someValue exampleValue
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 }}
unityVersion: ${{ matrix.unityVersion }}
targetPlatform: ${{ matrix.targetPlatform }}
customParameters: -profile SomeProfile -someBoolean -someValue exampleValue
providerStrategy: ${{ matrix.providerStrategy }}
allowDirtyBuild: true
########################### ###########################
# Upload # # Upload #
########################### ###########################
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v4
with: with:
name: Build Ubuntu (${{ matrix.unityVersion }}) name: Build ${{ matrix.targetPlatform }} on Ubuntu (${{ matrix.unityVersion }})
path: build path: build
retention-days: 14 retention-days: 14

View File

@@ -0,0 +1,132 @@
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.1.19f1
- 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
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 }}
with:
buildName: 'GameCI Test Build'
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
- 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 }}
with:
buildName: 'GameCI Test Build'
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
- 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 }}
with:
buildName: 'GameCI Test Build'
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@v4
with:
name: Build ${{ matrix.targetPlatform }} on Windows (${{ matrix.unityVersion }})
path: build
retention-days: 14

View File

@@ -15,13 +15,13 @@ jobs:
cleanupCloudRunner: cleanupCloudRunner:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
if: github.event.event_type != 'pull_request_target' if: github.event.event_type != 'pull_request_target'
with: with:
lfs: true lfs: true
- uses: actions/setup-node@v3 - uses: actions/setup-node@v4
with: with:
node-version: 16.x node-version: '18'
- run: yarn - run: yarn
- run: yarn run cli --help - run: yarn run cli --help
env: env:

View File

@@ -23,7 +23,7 @@ env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: eu-west-2 AWS_DEFAULT_REGION: eu-west-2
AWS_BASE_STACK_NAME: game-ci-github-pipelines AWS_STACK_NAME: game-ci-github-pipelines
CLOUD_RUNNER_BRANCH: ${{ github.ref }} CLOUD_RUNNER_BRANCH: ${{ github.ref }}
CLOUD_RUNNER_DEBUG: true CLOUD_RUNNER_DEBUG: true
CLOUD_RUNNER_DEBUG_TREE: true CLOUD_RUNNER_DEBUG_TREE: true
@@ -39,20 +39,21 @@ jobs:
if: github.event.event_type != 'pull_request_target' if: github.event.event_type != 'pull_request_target'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout (default) - timeout-minutes: 180
uses: actions/checkout@v3
with:
lfs: false
- run: yarn
- run: yarn run cli -m checks-update
timeout-minutes: 180
env: env:
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }} UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
PROJECT_PATH: test-project PROJECT_PATH: test-project
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GIT_PRIVATE_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TARGET_PLATFORM: StandaloneWindows64 TARGET_PLATFORM: StandaloneWindows64
cloudRunnerTests: true cloudRunnerTests: true
versioning: None versioning: None
CLOUD_RUNNER_CLUSTER: local-docker CLOUD_RUNNER_CLUSTER: local-docker
AWS_BASE_STACK_NAME: game-ci-github-pipelines AWS_STACK_NAME: game-ci-github-pipelines
CHECKS_UPDATE: ${{ github.event.inputs.checksObject }} CHECKS_UPDATE: ${{ github.event.inputs.checksObject }}
run: |
git clone -b cloud-runner-develop https://github.com/game-ci/unity-builder
cd unity-builder
yarn
ls
yarn run cli -m checks-update

View File

@@ -21,81 +21,140 @@ env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: eu-west-2 AWS_DEFAULT_REGION: eu-west-2
AWS_BASE_STACK_NAME: game-ci-team-pipelines AWS_STACK_NAME: game-ci-team-pipelines
CLOUD_RUNNER_BRANCH: ${{ github.ref }} CLOUD_RUNNER_BRANCH: ${{ github.ref }}
CLOUD_RUNNER_DEBUG: true
CLOUD_RUNNER_DEBUG_TREE: true
DEBUG: true DEBUG: true
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }} UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
PROJECT_PATH: test-project PROJECT_PATH: test-project
UNITY_VERSION: 2019.3.15f1 UNITY_VERSION: 2019.3.15f1
USE_IL2CPP: false USE_IL2CPP: false
USE_GKE_GCLOUD_AUTH_PLUGIN: true USE_GKE_GCLOUD_AUTH_PLUGIN: true
GIT_PRIVATE_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
jobs: jobs:
integrationTests: smokeTests:
name: Integration Tests name: Smoke Tests
if: github.event.event_type != 'pull_request_target' if: github.event.event_type != 'pull_request_target'
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
cloudRunnerCluster: test:
- aws #- 'cloud-runner-async-workflow'
- 'cloud-runner-caching'
# - 'cloud-runner-end2end-caching'
# - 'cloud-runner-end2end-retaining'
- 'cloud-runner-environment'
- 'cloud-runner-hooks'
- 'cloud-runner-local-persistence'
- 'cloud-runner-locking-core'
- 'cloud-runner-locking-get-locked'
providerStrategy:
#- aws
- local-docker - local-docker
- k8s #- k8s
steps: steps:
- name: Checkout (default) - name: Checkout (default)
uses: actions/checkout@v3 uses: actions/checkout@v4
with: with:
lfs: false lfs: false
- uses: google-github-actions/auth@v1
with:
credentials_json: ${{ secrets.GOOGLE_SERVICE_ACCOUNT_KEY }}
- name: 'Set up Cloud SDK'
uses: 'google-github-actions/setup-gcloud@v1'
- name: Get GKE cluster credentials
run: |
export USE_GKE_GCLOUD_AUTH_PLUGIN=True
gcloud components install gke-gcloud-auth-plugin
gcloud container clusters get-credentials $GKE_CLUSTER --zone $GKE_ZONE --project $GKE_PROJECT
- name: Configure AWS Credentials - name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1 uses: aws-actions/configure-aws-credentials@v1
with: with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: eu-west-2 aws-region: eu-west-2
- uses: google-github-actions/auth@v1
if: matrix.providerStrategy == 'k8s'
with:
credentials_json: ${{ secrets.GOOGLE_SERVICE_ACCOUNT_KEY }}
- name: 'Set up Cloud SDK'
if: matrix.providerStrategy == 'k8s'
uses: 'google-github-actions/setup-gcloud@v1.1.0'
- name: Get GKE cluster credentials
if: matrix.providerStrategy == 'k8s'
run: |
export USE_GKE_GCLOUD_AUTH_PLUGIN=True
gcloud components install gke-gcloud-auth-plugin
gcloud container clusters get-credentials $GKE_CLUSTER --zone $GKE_ZONE --project $GKE_PROJECT
- run: yarn - run: yarn
- run: yarn run test "cloud-runner-async-workflow" --detectOpenHandles --forceExit --runInBand - run: yarn run test "${{ matrix.test }}" --detectOpenHandles --forceExit --runInBand
if: matrix.CloudRunnerCluster != 'local-docker' timeout-minutes: 35
timeout-minutes: 180
env: env:
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }} UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
PROJECT_PATH: test-project PROJECT_PATH: test-project
GIT_PRIVATE_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TARGET_PLATFORM: StandaloneWindows64 TARGET_PLATFORM: StandaloneWindows64
cloudRunnerTests: true cloudRunnerTests: true
versioning: None versioning: None
CLOUD_RUNNER_CLUSTER: ${{ matrix.cloudRunnerCluster }} CLOUD_RUNNER_CLUSTER: ${{ matrix.providerStrategy }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} tests:
- run: yarn run test-i --detectOpenHandles --forceExit --runInBand # needs:
if: matrix.CloudRunnerCluster == 'local-docker' # - smokeTests
timeout-minutes: 180 # - buildTargetTests
name: Integration Tests
if: github.event.event_type != 'pull_request_target'
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
providerStrategy:
- aws
- local-docker
- k8s
test:
- 'cloud-runner-async-workflow'
#- 'cloud-runner-caching'
- 'cloud-runner-end2end-locking'
- 'cloud-runner-end2end-caching'
- 'cloud-runner-end2end-retaining'
- 'cloud-runner-environment'
#- 'cloud-runner-hooks'
- 'cloud-runner-s3-steps'
#- 'cloud-runner-local-persistence'
#- 'cloud-runner-locking-core'
#- 'cloud-runner-locking-get-locked'
steps:
- name: Checkout (default)
uses: actions/checkout@v4
with:
lfs: false
- 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
- uses: google-github-actions/auth@v1
if: matrix.providerStrategy == 'k8s'
with:
credentials_json: ${{ secrets.GOOGLE_SERVICE_ACCOUNT_KEY }}
- name: 'Set up Cloud SDK'
if: matrix.providerStrategy == 'k8s'
uses: 'google-github-actions/setup-gcloud@v1.1.0'
- name: Get GKE cluster credentials
if: matrix.providerStrategy == 'k8s'
run: |
export USE_GKE_GCLOUD_AUTH_PLUGIN=True
gcloud components install gke-gcloud-auth-plugin
gcloud container clusters get-credentials $GKE_CLUSTER --zone $GKE_ZONE --project $GKE_PROJECT
- run: yarn
- run: yarn run test "${{ matrix.test }}" --detectOpenHandles --forceExit --runInBand
timeout-minutes: 60
env: env:
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }} UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
PROJECT_PATH: test-project PROJECT_PATH: test-project
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TARGET_PLATFORM: StandaloneWindows64 TARGET_PLATFORM: StandaloneWindows64
cloudRunnerTests: true cloudRunnerTests: true
versioning: None versioning: None
CLOUD_RUNNER_CLUSTER: ${{ matrix.cloudRunnerCluster }} PROVIDER_STRATEGY: ${{ matrix.providerStrategy }}
localBuildTests: buildTargetTests:
name: Local Build Target Tests name: Local Build Target Tests
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
cloudRunnerCluster: providerStrategy:
#- aws #- aws
- local-docker - local-docker
#- k8s #- k8s
@@ -108,26 +167,24 @@ jobs:
- Android # Build an Android .apk. - Android # Build an Android .apk.
steps: steps:
- name: Checkout (default) - name: Checkout (default)
uses: actions/checkout@v3 uses: actions/checkout@v4
with: with:
lfs: false lfs: false
- run: yarn - run: yarn
- uses: ./ - uses: ./
id: unity-build id: unity-build
timeout-minutes: 90 timeout-minutes: 30
env: env:
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }} UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
with: with:
cloudRunnerTests: true cloudRunnerTests: true
versioning: None versioning: None
projectPath: test-project
gitPrivateToken: ${{ secrets.GITHUB_TOKEN }}
targetPlatform: ${{ matrix.targetPlatform }} targetPlatform: ${{ matrix.targetPlatform }}
cloudRunnerCluster: ${{ matrix.cloudRunnerCluster }} providerStrategy: ${{ matrix.providerStrategy }}
- run: | - run: |
cp ./cloud-runner-cache/cache/${{ steps.unity-build.outputs.CACHE_KEY }}/build/${{ steps.unity-build.outputs.BUILD_ARTIFACT }} ${{ steps.unity-build.outputs.BUILD_ARTIFACT }} cp ./cloud-runner-cache/cache/${{ steps.unity-build.outputs.CACHE_KEY }}/build/${{ steps.unity-build.outputs.BUILD_ARTIFACT }} ${{ steps.unity-build.outputs.BUILD_ARTIFACT }}
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v3
with: with:
name: ${{ matrix.cloudRunnerCluster }} Build (${{ matrix.targetPlatform }}) name: ${{ matrix.providerStrategy }} Build (${{ matrix.targetPlatform }})
path: ${{ steps.unity-build.outputs.BUILD_ARTIFACT }} path: ${{ steps.unity-build.outputs.BUILD_ARTIFACT }}
retention-days: 14 retention-days: 14

View File

@@ -7,15 +7,19 @@ on:
env: env:
CODECOV_TOKEN: '2f2eb890-30e2-4724-83eb-7633832cf0de' CODECOV_TOKEN: '2f2eb890-30e2-4724-83eb-7633832cf0de'
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs: jobs:
tests: tests:
name: Tests name: Tests
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- uses: actions/setup-node@v3 - uses: actions/setup-node@v4
with: with:
node-version: '16' node-version: '18'
- run: yarn - run: yarn
- run: yarn lint - run: yarn lint
- run: yarn test --coverage - run: yarn test --coverage

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@v3
with:
lfs: true
###########################
# Cache #
###########################
- uses: actions/cache@v3
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@v3
with:
name: Build MacOS (${{ matrix.unityVersion }})
path: build
retention-days: 14

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@v3
with:
lfs: true
###########################
# Cache #
###########################
- uses: actions/cache@v3
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@v3
with:
name: Build Windows (${{ matrix.unityVersion }})
path: build
retention-days: 14

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

@@ -25,7 +25,7 @@ Steps to be performed to submit a pull request:
#### Pull Request Prerequisites #### 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; 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 #### Windows users
Make sure your editor and terminal that run the tests are set to `Powershell 7` or above with 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 #### License

View File

@@ -10,8 +10,9 @@ Part of the <a href="https://game.ci">GameCI</a> open source project.
<br /> <br />
<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) [![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)
[![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 - 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) [![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 />
<br /> <br />

View File

@@ -22,7 +22,7 @@ inputs:
buildName: buildName:
required: false required: false
default: '' default: ''
description: 'Name of the build.' description: 'Name of the build. Should not include a file extension.'
buildsPath: buildsPath:
required: false required: false
default: '' default: ''
@@ -31,6 +31,10 @@ inputs:
required: false required: false
default: '' default: ''
description: 'Path to a Namespace.Class.StaticMethod to run to perform the build.' 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.'
customParameters: customParameters:
required: false required: false
default: '' default: ''
@@ -47,10 +51,12 @@ inputs:
required: false required: false
default: '' default: ''
description: 'The android versionCode' description: 'The android versionCode'
androidAppBundle: androidExportType:
required: false required: false
default: 'false' default: 'androidPackage'
description: 'Whether to build .aab instead of .apk' description:
'The android export type. Should be androidPackage for apk, androidAppBundle for aab, or androidStudioProject for
an android studio project.'
androidKeystoreName: androidKeystoreName:
required: false required: false
default: '' default: ''
@@ -75,10 +81,18 @@ inputs:
required: false required: false
default: '' default: ''
description: 'The android target API level.' 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: sshAgent:
required: false required: false
default: '' default: ''
description: 'SSH Agent path to forward to the container' 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: gitPrivateToken:
required: false required: false
default: '' default: ''
@@ -87,11 +101,43 @@ inputs:
required: false required: false
default: '' default: ''
description: '[CloudRunner] GitHub owner name or organization/team name' description: '[CloudRunner] 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: chownFilesTo:
required: false required: false
default: '' default: ''
description: description:
'User and optionally group (user or user:group or uid:gid) to give ownership of the resulting build artifacts' '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: allowDirtyBuild:
required: false required: false
default: '' default: ''
@@ -108,7 +154,7 @@ inputs:
description: description:
'[CloudRunner] Run a pre build job after the repository setup but before the build job (in yaml format with the '[CloudRunner] 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)' keys image, secrets (name, value object array), command line string)'
customStepFiles: containerHookFiles:
required: false required: false
default: '' default: ''
description: description:
@@ -120,7 +166,7 @@ inputs:
description: description:
'[CloudRunner] Specify the names (by file name) of custom hooks to run before or after cloud runner jobs, must '[CloudRunner] Specify the names (by file name) of custom hooks to run before or after cloud runner jobs, must
match a yaml step file inside your repo in the folder .game-ci/hooks/' match a yaml step file inside your repo in the folder .game-ci/hooks/'
customJobHooks: customCommandHooks:
required: false required: false
default: '' default: ''
description: '[CloudRunner] Specify custom commands and trigger hooks (injects commands into jobs)' description: '[CloudRunner] Specify custom commands and trigger hooks (injects commands into jobs)'
@@ -130,11 +176,11 @@ inputs:
description: description:
'[CloudRunner] Run a custom job instead of the standard build automation for cloud runner (in yaml format with the '[CloudRunner] 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)' keys image, secrets (name, value object array), command line string)'
awsBaseStackName: awsStackName:
default: 'game-ci' default: 'game-ci'
required: false required: false
description: '[CloudRunner] The Cloud Formation stack name that must be setup before using this option.' description: '[CloudRunner] The Cloud Formation stack name that must be setup before using this option.'
cloudRunnerCluster: providerStrategy:
default: 'local' default: 'local'
required: false required: false
description: description:
@@ -187,6 +233,27 @@ inputs:
description: description:
'[CloudRunner] Whether or not to watch the build to the end. Can be used for especially long running jobs e.g '[CloudRunner] 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.' 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:
'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: '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'
outputs: outputs:
volume: volume:
description: 'The Persistent Volume (PV) where the build artifacts have been stored by Kubernetes' description: 'The Persistent Volume (PV) where the build artifacts have been stored by Kubernetes'
@@ -194,9 +261,14 @@ outputs:
description: 'The generated version used for the Unity build' description: 'The generated version used for the Unity build'
androidVersionCode: androidVersionCode:
description: 'The generated versionCode used for the Android Unity build' 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: branding:
icon: 'box' icon: 'box'
color: 'gray-dark' color: 'gray-dark'
runs: runs:
using: 'node16' using: 'node20'
main: 'dist/index.js' 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

@@ -29,12 +29,23 @@ namespace UnityBuilderAction
} }
} }
#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
// Define BuildPlayer Options // Define BuildPlayer Options
var buildPlayerOptions = new BuildPlayerOptions { var buildPlayerOptions = new BuildPlayerOptions {
scenes = scenes, scenes = scenes,
locationPathName = options["customBuildPath"], locationPathName = options["customBuildPath"],
target = (BuildTarget) Enum.Parse(typeof(BuildTarget), options["buildTarget"]), target = (BuildTarget) Enum.Parse(typeof(BuildTarget), options["buildTarget"]),
options = buildOptions options = buildOptions,
#if UNITY_2021_2_OR_NEWER
subtarget = (int) buildSubtarget
#endif
}; };
// Set version for this build // Set version for this build

View File

@@ -1,14 +1,14 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using UnityEditor; using UnityEditor;
using System.Reflection;
namespace UnityBuilderAction.Input namespace UnityBuilderAction.Input
{ {
public class AndroidSettings public static class AndroidSettings
{ {
public static void Apply(Dictionary<string, string> options) public static void Apply(Dictionary<string, string> options)
{ {
EditorUserBuildSettings.buildAppBundle = options["customBuildPath"].EndsWith(".aab");
#if UNITY_2019_1_OR_NEWER #if UNITY_2019_1_OR_NEWER
if (options.TryGetValue("androidKeystoreName", out string keystoreName) && !string.IsNullOrEmpty(keystoreName)) if (options.TryGetValue("androidKeystoreName", out string keystoreName) && !string.IsNullOrEmpty(keystoreName))
{ {
@@ -16,13 +16,21 @@ namespace UnityBuilderAction.Input
PlayerSettings.Android.keystoreName = keystoreName; PlayerSettings.Android.keystoreName = keystoreName;
} }
#endif #endif
if (options.TryGetValue("androidKeystorePass", out string keystorePass) && !string.IsNullOrEmpty(keystorePass)) // 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; 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; 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; PlayerSettings.Android.keyaliasPass = keyaliasPass;
if (options.TryGetValue("androidTargetSdkVersion", out string androidTargetSdkVersion) && !string.IsNullOrEmpty(androidTargetSdkVersion))
string androidTargetSdkVersion;
if (options.TryGetValue("androidTargetSdkVersion", out androidTargetSdkVersion) && !string.IsNullOrEmpty(androidTargetSdkVersion))
{ {
var targetSdkVersion = AndroidSdkVersions.AndroidApiLevelAuto; var targetSdkVersion = AndroidSdkVersions.AndroidApiLevelAuto;
try try
@@ -36,6 +44,62 @@ namespace UnityBuilderAction.Input
} }
PlayerSettings.Android.targetSdkVersion = targetSdkVersion; 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);
break;
case "androidAppBundle":
EditorUserBuildSettings.exportAsGoogleAndroidProject = false;
if (buildAppBundle != null)
buildAppBundle.SetValue(null, true);
break;
case "androidPackage":
EditorUserBuildSettings.exportAsGoogleAndroidProject = false;
if (buildAppBundle != null)
buildAppBundle.SetValue(null, false);
break;
}
}
string symbolType;
if (options.TryGetValue("androidSymbolType", out symbolType) && !string.IsNullOrEmpty(symbolType))
{
#if 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
}
} }
} }
} }

View File

@@ -12,29 +12,35 @@ namespace UnityBuilderAction.Input
public static Dictionary<string, string> GetValidatedOptions() 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"); Console.WriteLine("Missing argument -projectPath");
EditorApplication.Exit(110); EditorApplication.Exit(110);
} }
if (!validatedOptions.TryGetValue("buildTarget", out var buildTarget)) { string buildTarget;
if (!validatedOptions.TryGetValue("buildTarget", out buildTarget)) {
Console.WriteLine("Missing argument -buildTarget"); Console.WriteLine("Missing argument -buildTarget");
EditorApplication.Exit(120); EditorApplication.Exit(120);
} }
if (!Enum.IsDefined(typeof(BuildTarget), buildTarget)) { if (!Enum.IsDefined(typeof(BuildTarget), buildTarget)) {
Console.WriteLine($"{buildTarget} is not a defined {nameof(BuildTarget)}");
EditorApplication.Exit(121); EditorApplication.Exit(121);
} }
if (!validatedOptions.TryGetValue("customBuildPath", out var customBuildPath)) { string customBuildPath;
if (!validatedOptions.TryGetValue("customBuildPath", out customBuildPath)) {
Console.WriteLine("Missing argument -customBuildPath"); Console.WriteLine("Missing argument -customBuildPath");
EditorApplication.Exit(130); EditorApplication.Exit(130);
} }
const string defaultCustomBuildName = "TestBuild"; const string defaultCustomBuildName = "TestBuild";
if (!validatedOptions.TryGetValue("customBuildName", out var customBuildName)) { string customBuildName;
if (!validatedOptions.TryGetValue("customBuildName", out customBuildName)) {
Console.WriteLine($"Missing argument -customBuildName, defaulting to {defaultCustomBuildName}."); Console.WriteLine($"Missing argument -customBuildName, defaulting to {defaultCustomBuildName}.");
validatedOptions.Add("customBuildName", defaultCustomBuildName); validatedOptions.Add("customBuildName", defaultCustomBuildName);
} else if (customBuildName == "") { } else if (customBuildName == "") {

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

@@ -106,7 +106,8 @@ namespace UnityBuilderAction.Versioning
using (var process = new System.Diagnostics.Process()) { using (var process = new System.Diagnostics.Process()) {
string workingDirectory = UnityEngine.Application.dataPath; 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); } if (exitCode != 0) { throw new GitException(exitCode, errors); }
return output; 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>

244266
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

1303
dist/licenses.txt generated vendored

File diff suppressed because it is too large Load Diff

View File

@@ -4,8 +4,13 @@
# Create directories for license activation # Create directories for license activation
# #
sudo mkdir /Library/Application\ Support/Unity UNITY_LICENSE_PATH="/Library/Application Support/Unity"
sudo chmod -R 777 /Library/Application\ Support/Unity
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" ACTIVATE_LICENSE_PATH="$ACTION_FOLDER/BlankProject"
mkdir -p "$ACTIVATE_LICENSE_PATH" mkdir -p "$ACTIVATE_LICENSE_PATH"
@@ -21,7 +26,6 @@ source $ACTION_FOLDER/platforms/mac/steps/return_license.sh
# Remove license activation directory # Remove license activation directory
# #
sudo rm -r /Library/Application\ Support/Unity
rm -r "$ACTIVATE_LICENSE_PATH" rm -r "$ACTIVATE_LICENSE_PATH"
# #
@@ -37,7 +41,7 @@ echo ""
echo "Please note that the exit code is not very descriptive." echo "Please note that the exit code is not very descriptive."
echo "Most likely it will not help you solve the issue." echo "Most likely it will not help you solve the issue."
echo "" 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 "" echo ""
fi; fi;

View File

@@ -8,7 +8,7 @@ echo "Requesting activation"
# Activate license # Activate license
/Applications/Unity/Hub/Editor/$UNITY_VERSION/Unity.app/Contents/MacOS/Unity \ /Applications/Unity/Hub/Editor/$UNITY_VERSION/Unity.app/Contents/MacOS/Unity \
-logFile /dev/stdout \ -logFile - \
-batchmode \ -batchmode \
-nographics \ -nographics \
-quit \ -quit \
@@ -30,6 +30,7 @@ else
# Activation failed so exit with the code from the license verification step # Activation failed so exit with the code from the license verification step
echo "Unclassified error occured while trying to activate license." echo "Unclassified error occured while trying to activate license."
echo "Exit code was: $UNITY_EXIT_CODE" echo "Exit code was: $UNITY_EXIT_CODE"
echo "::error ::There was an error while trying to activate the Unity license."
exit $UNITY_EXIT_CODE exit $UNITY_EXIT_CODE
fi fi

View File

@@ -128,7 +128,8 @@ echo ""
# Reference: https://docs.unity3d.com/2019.3/Documentation/Manual/CommandLineArguments.html # Reference: https://docs.unity3d.com/2019.3/Documentation/Manual/CommandLineArguments.html
/Applications/Unity/Hub/Editor/$UNITY_VERSION/Unity.app/Contents/MacOS/Unity \ /Applications/Unity/Hub/Editor/$UNITY_VERSION/Unity.app/Contents/MacOS/Unity \
-quit \ -logFile - \
$( [ "${MANUAL_EXIT}" == "true" ] || echo "-quit" ) \
-batchmode \ -batchmode \
-nographics \ -nographics \
-username "$UNITY_EMAIL" \ -username "$UNITY_EMAIL" \
@@ -146,8 +147,9 @@ echo ""
-androidKeyaliasName "$ANDROID_KEYALIAS_NAME" \ -androidKeyaliasName "$ANDROID_KEYALIAS_NAME" \
-androidKeyaliasPass "$ANDROID_KEYALIAS_PASS" \ -androidKeyaliasPass "$ANDROID_KEYALIAS_PASS" \
-androidTargetSdkVersion "$ANDROID_TARGET_SDK_VERSION" \ -androidTargetSdkVersion "$ANDROID_TARGET_SDK_VERSION" \
$CUSTOM_PARAMETERS \ -androidExportType "$ANDROID_EXPORT_TYPE" \
> "$UNITY_PROJECT_PATH/out.log" 2>&1 -androidSymbolType "$ANDROID_SYMBOL_TYPE" \
$CUSTOM_PARAMETERS
# Catch exit code # Catch exit code
BUILD_EXIT_CODE=$? BUILD_EXIT_CODE=$?

View File

@@ -5,7 +5,7 @@ echo "Changing to \"$ACTIVATE_LICENSE_PATH\" directory."
pushd "$ACTIVATE_LICENSE_PATH" pushd "$ACTIVATE_LICENSE_PATH"
/Applications/Unity/Hub/Editor/$UNITY_VERSION/Unity.app/Contents/MacOS/Unity \ /Applications/Unity/Hub/Editor/$UNITY_VERSION/Unity.app/Contents/MacOS/Unity \
-logFile /dev/stdout \ -logFile - \
-batchmode \ -batchmode \
-nographics \ -nographics \
-quit \ -quit \

View File

@@ -1,45 +1,83 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# # Ensure machine ID is randomized for personal license activation
# Create directory for 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
ACTIVATE_LICENSE_PATH="$GITHUB_WORKSPACE/_activate-license~" fi
mkdir -p "$ACTIVATE_LICENSE_PATH"
# #
# Run steps # Prepare Android SDK, if needed
# # We do this here to ensure it has root permissions
source /steps/set_gitcredential.sh
source /steps/activate.sh
source /steps/build.sh
source /steps/return_license.sh
#
# Remove license activation directory
# #
rm -r "$ACTIVATE_LICENSE_PATH" fullProjectPath="$GITHUB_WORKSPACE/$PROJECT_PATH"
# if [[ "$BUILD_TARGET" == "Android" ]]; then
# Instructions for debugging 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 if [[ -n "$ANDROID_SDK_MANAGER_PARAMETERS" ]]; then
echo "" echo "Updating Android SDK with parameters: $ANDROID_SDK_MANAGER_PARAMETERS"
echo "###########################" $SDKMANAGER "$ANDROID_SDK_MANAGER_PARAMETERS"
echo "# Failure #" else
echo "###########################" echo "Updating Android SDK with auto detected target API version"
echo "" # Read the line containing AndroidTargetSdkVersion from the file
echo "Please note that the exit code is not very descriptive." targetAPILine=$(grep 'AndroidTargetSdkVersion' "$fullProjectPath/ProjectSettings/ProjectSettings.asset")
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;
# # Extract the number after the semicolon
# Exit with code from the build step. 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,78 +1,56 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Run in ACTIVATE_LICENSE_PATH directory if [[ -n "$UNITY_SERIAL" && -n "$UNITY_EMAIL" && -n "$UNITY_PASSWORD" ]]; then
echo "Changing to \"$ACTIVATE_LICENSE_PATH\" directory."
pushd "$ACTIVATE_LICENSE_PATH"
if [[ -n "$UNITY_LICENSE" ]] || [[ -n "$UNITY_LICENSE_FILE" ]]; 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. echo "Requesting activation"
# * 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)"
# Set the license file path # Loop the unity-editor call until the license is activated with exponential backoff and a maximum of 5 retries
FILE_PATH=UnityLicenseFile.ulf retry_count=0
if [[ -n "$UNITY_LICENSE" ]]; then # Initialize delay to 15 seconds
# Copy license file from Github variables delay=15
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
# Activate license # Loop until UNITY_EXIT_CODE is 0 or retry count reaches 5
ACTIVATION_OUTPUT=$(unity-editor \ while [[ $retry_count -lt 5 ]]
do
# Activate license
unity-editor \
-logFile /dev/stdout \ -logFile /dev/stdout \
-quit \ -quit \
-manualLicenseFile $FILE_PATH) -serial "$UNITY_SERIAL" \
-username "$UNITY_EMAIL" \
-password "$UNITY_PASSWORD" \
-projectPath "/BlankProject"
# Store the exit code from the verify command # Store the exit code from the verify command
UNITY_EXIT_CODE=$? UNITY_EXIT_CODE=$?
# The exit code for personal activation is always 1; # Check if UNITY_EXIT_CODE is 0
# Determine whether activation was successful. if [[ $UNITY_EXIT_CODE -eq 0 ]]
# then
# Successful output should include the following: echo "Activation successful"
# break
# "LICENSE SYSTEM [2020120 18:51:20] Next license update check is after 2019-11-25T18:23:38" else
# # Increment retry count
ACTIVATION_SUCCESSFUL=$(echo $ACTIVATION_OUTPUT | grep 'Next license update check is after' | wc -l) ((retry_count++))
# Set exit code to 0 if activation was successful echo "::warning ::Activation failed, attempting retry #$retry_count"
if [[ $ACTIVATION_SUCCESSFUL -eq 1 ]]; then echo "Activation failed, retrying in $delay seconds..."
UNITY_EXIT_CODE=0 sleep $delay
fi;
# Remove license file # Double the delay for the next iteration
rm -f $FILE_PATH delay=$((delay * 2))
fi
done
elif [[ -n "$UNITY_SERIAL" && -n "$UNITY_EMAIL" && -n "$UNITY_PASSWORD" ]]; then if [[ $retry_count -eq 5 ]]
# then
# PROFESSIONAL (SERIAL) LICENSE MODE echo "Activation failed after 5 retries"
# fi
# 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=$?
elif [[ -n "$UNITY_LICENSING_SERVER" ]]; then elif [[ -n "$UNITY_LICENSING_SERVER" ]]; then
# #
@@ -97,10 +75,13 @@ else
# #
echo "License activation strategy could not be determined." echo "License activation strategy could not be determined."
echo "" 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." 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; exit 1;
fi fi
@@ -115,8 +96,6 @@ else
# Activation failed so exit with the code from the license verification step # Activation failed so exit with the code from the license verification step
echo "Unclassified error occured while trying to activate license." echo "Unclassified error occured while trying to activate license."
echo "Exit code was: $UNITY_EXIT_CODE" echo "Exit code was: $UNITY_EXIT_CODE"
echo "::error ::There was an error while trying to activate the Unity license."
exit $UNITY_EXIT_CODE exit $UNITY_EXIT_CODE
fi fi
# Return to previous working directory
popd

View File

@@ -62,19 +62,6 @@ else
# #
fi fi
#
# Prepare Android SDK, if needed
#
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 # Pre-build debug information
# #
@@ -119,7 +106,7 @@ echo ""
unity-editor \ unity-editor \
-logfile /dev/stdout \ -logfile /dev/stdout \
-quit \ $( [ "${MANUAL_EXIT}" == "true" ] || echo "-quit" ) \
-customBuildName "$BUILD_NAME" \ -customBuildName "$BUILD_NAME" \
-projectPath "$UNITY_PROJECT_PATH" \ -projectPath "$UNITY_PROJECT_PATH" \
-buildTarget "$BUILD_TARGET" \ -buildTarget "$BUILD_TARGET" \
@@ -133,6 +120,8 @@ unity-editor \
-androidKeyaliasName "$ANDROID_KEYALIAS_NAME" \ -androidKeyaliasName "$ANDROID_KEYALIAS_NAME" \
-androidKeyaliasPass "$ANDROID_KEYALIAS_PASS" \ -androidKeyaliasPass "$ANDROID_KEYALIAS_PASS" \
-androidTargetSdkVersion "$ANDROID_TARGET_SDK_VERSION" \ -androidTargetSdkVersion "$ANDROID_TARGET_SDK_VERSION" \
-androidExportType "$ANDROID_EXPORT_TYPE" \
-androidSymbolType "$ANDROID_SYMBOL_TYPE" \
$CUSTOM_PARAMETERS $CUSTOM_PARAMETERS
# Catch exit code # Catch exit code
@@ -151,6 +140,7 @@ fi
# Make a given user owner of all artifacts # Make a given user owner of all artifacts
if [[ -n "$CHOWN_FILES_TO" ]]; then 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" "$BUILD_PATH_FULL"
chown -R "$CHOWN_FILES_TO" "$UNITY_PROJECT_PATH" chown -R "$CHOWN_FILES_TO" "$UNITY_PROJECT_PATH"
fi fi

View File

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

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

@@ -0,0 +1,40 @@
#!/usr/bin/env bash
#
# Run steps
#
source /steps/set_extra_git_configs.sh
source /steps/set_gitcredential.sh
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
source /steps/build.sh
source /steps/return_license.sh
#
# 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 credential.helper store
git config --global --replace-all url."https://token:$GIT_PRIVATE_TOKEN@github.com/".insteadOf ssh://git@github.com/ 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 git@github.com
git config --global --add url."https://token:$GIT_PRIVATE_TOKEN@github.com/".insteadOf "https://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://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:" git config --global url."https://git:$GIT_PRIVATE_TOKEN@github.com/".insteadOf "git@github.com:"

View File

@@ -1,7 +1,69 @@
# Activates Unity # Activates Unity
& "C:\Program Files\Unity\Hub\Editor\$Env:UNITY_VERSION\Editor\Unity.exe" -batchmode -quit -nographics `
-username $Env:UNITY_EMAIL ` Write-Output ""
-password $Env:UNITY_PASSWORD ` Write-Output "###########################"
-serial $Env:UNITY_SERIAL ` Write-Output "# Activating #"
-projectPath "c:/BlankProject" ` Write-Output "###########################"
-logfile | Out-Host 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
}
}
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

@@ -66,6 +66,26 @@ else
Get-ChildItem -Path $Env:UNITY_PROJECT_PATH\Assets\Editor -Recurse 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 # Pre-build debug information
# #
@@ -112,46 +132,67 @@ Write-Output ""
# If $Env:CUSTOM_PARAMETERS contains spaces and is passed directly on the command line to Unity, powershell will wrap it # 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. # 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) $_, $customParametersArray = Invoke-Expression('Write-Output -- "" ' + $Env:CUSTOM_PARAMETERS)
$unityArgs = @(
"-quit",
"-batchmode",
"-nographics",
"-silent-crashes",
"-customBuildName", "`"$Env:BUILD_NAME`"",
"-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`"",
"-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
& "C:\Program Files\Unity\Hub\Editor\$Env:UNITY_VERSION\Editor\Unity.exe" -quit -batchmode -nographics ` # Remove null items as that will fail the Start-Process call
-projectPath $Env:UNITY_PROJECT_PATH ` $unityArgs = $unityArgs | Where-Object { $_ -ne $null }
-executeMethod $Env:BUILD_METHOD `
-buildTarget $Env:BUILD_TARGET `
-customBuildTarget $Env:BUILD_TARGET `
-customBuildPath $Env:CUSTOM_BUILD_PATH `
-buildVersion $Env:VERSION `
-androidVersionCode $Env:ANDROID_VERSION_CODE `
-androidKeystoreName $Env:ANDROID_KEYSTORE_NAME `
-androidKeystorePass $Env:ANDROID_KEYSTORE_PASS `
-androidKeyaliasName $Env:ANDROID_KEYALIAS_NAME `
-androidKeyaliasPass $Env:ANDROID_KEYALIAS_PASS `
-androidTargetSdkVersion $Env:ANDROID_TARGET_SDK_VERSION `
$customParametersArray `
-logfile | Out-Host
# Catch exit code $unityProcess = Start-Process -FilePath "$Env:UNITY_PATH/Editor/Unity.exe" `
$Env:BUILD_EXIT_CODE=$? -ArgumentList $unityArgs `
-PassThru `
-NoNewWindow
# Display results # Cache the handle so exit code works properly
if ($Env:BUILD_EXIT_CODE -eq 0) # https://stackoverflow.com/questions/10262231/obtaining-exitcode-using-start-process-and-waitforexit-instead-of-wait
{ $unityHandle = $unityProcess.Handle
Write-Output "Build Succeeded!"
} else while ($true) {
{ if ($unityProcess.HasExited) {
Write-Output "$('Build failed, with exit code ')$($Env:BUILD_EXIT_CODE)$('"')" 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
} }
# TODO: Determine if we need to set permissions on any files
#
# Results
#
Write-Output ""
Write-Output "###########################"
Write-Output "# Build output #"
Write-Output "###########################"
Write-Output ""
Get-ChildItem $Env:BUILD_PATH_FULL
Write-Output ""

View File

@@ -1,15 +1,32 @@
# Activate Unity Get-Process
& "c:\steps\activate.ps1"
# Import any necessary registry keys, ie: location of windows 10 sdk # Import any necessary registry keys, ie: location of windows 10 sdk
# No guarantee that there will be any necessary registry keys, ie: tvOS # 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 # Register the Visual Studio installation so Unity can find it
regsvr32 C:\ProgramData\Microsoft\VisualStudio\Setup\x64\Microsoft.VisualStudio.Setup.Configuration.Native.dll 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 }
# Setup Git Credentials
. "c:\steps\set_gitcredential.ps1"
# Activate Unity
. "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
}
# Build the project # Build the project
& "c:\steps\build.ps1" . "c:\steps\build.ps1"
# Free the seat for the activated license # Free the seat for the activated license
& "c:\steps\return_license.ps1" . "c:\steps\return_license.ps1"
Get-Process
exit $BUILD_EXIT_CODE

View File

@@ -1,7 +1,52 @@
# Return the active Unity license # Return the active Unity license
& "C:\Program Files\Unity\Hub\Editor\$Env:UNITY_VERSION\Editor\Unity.exe" -batchmode -quit -nographics `
-username $Env:UNITY_EMAIL ` Write-Output ""
-password $Env:UNITY_PASSWORD ` Write-Output "###########################"
-returnlicense ` Write-Output "# Return License #"
-projectPath "c:/BlankProject" ` Write-Output "###########################"
-logfile | Out-Host 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 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

@@ -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

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

View File

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

View File

@@ -18,7 +18,6 @@ module.exports = {
transform: { transform: {
'^.+\\.ts$': 'ts-jest', '^.+\\.ts$': 'ts-jest',
}, },
autoRun: false,
// Indicates whether each individual test should be reported during the run // Indicates whether each individual test should be reported during the run
verbose: true, verbose: true,

View File

@@ -1,6 +1,6 @@
{ {
"name": "unity-builder", "name": "unity-builder",
"version": "2.0.0", "version": "3.0.0",
"description": "Build Unity projects for different platforms.", "description": "Build Unity projects for different platforms.",
"main": "dist/index.js", "main": "dist/index.js",
"repository": "git@github.com:game-ci/unity-builder.git", "repository": "git@github.com:game-ci/unity-builder.git",
@@ -12,19 +12,23 @@
"lint": "prettier --check \"src/**/*.{js,ts}\" && eslint src/**/*.ts", "lint": "prettier --check \"src/**/*.{js,ts}\" && eslint src/**/*.ts",
"format": "prettier --write \"src/**/*.{js,ts}\"", "format": "prettier --write \"src/**/*.{js,ts}\"",
"cli": "yarn ts-node src/index.ts -m cli", "cli": "yarn ts-node src/index.ts -m cli",
"gcp-secrets-tests": "cross-env cloudRunnerCluster=aws cloudRunnerTests=true readInputOverrideCommand=\"gcp-secret-manager\" populateOverride=true readInputFromOverrideList=UNITY_EMAIL,UNITY_SERIAL,UNITY_PASSWORD yarn test -i -t \"cloud runner\"", "gcp-secrets-tests": "cross-env providerStrategy=aws cloudRunnerTests=true readInputOverrideCommand=\"gcp-secret-manager\" populateOverride=true readInputFromOverrideList=UNITY_EMAIL,UNITY_SERIAL,UNITY_PASSWORD yarn test -i -t \"cloud runner\"",
"gcp-secrets-cli": "cross-env cloudRunnerTests=true readInputOverrideCommand=\"gcp-secret-manager\" yarn ts-node src/index.ts -m cli --populateOverride true --readInputFromOverrideList UNITY_EMAIL,UNITY_SERIAL,UNITY_PASSWORD", "gcp-secrets-cli": "cross-env cloudRunnerTests=true readInputOverrideCommand=\"gcp-secret-manager\" 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-secret-manager\" 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-secret-manager\" 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-aws": "cross-env providerStrategy=aws yarn run test-cli",
"cli-k8s": "cross-env cloudRunnerCluster=k8s yarn run test-cli", "cli-k8s": "cross-env providerStrategy=k8s yarn run test-cli",
"test-cli": "cross-env cloudRunnerTests=true yarn ts-node src/index.ts -m cli --projectPath test-project", "test-cli": "cross-env cloudRunnerTests=true yarn ts-node src/index.ts -m cli --projectPath test-project",
"test": "jest", "test": "jest",
"test-i": "cross-env cloudRunnerTests=true yarn test -i -t \"cloud runner\"", "test-i": "cross-env cloudRunnerTests=true yarn test -i -t \"cloud runner\"",
"test-i-*": "yarn run test-i-aws && yarn run test-i-k8s", "test-i-*": "yarn run test-i-aws && yarn run test-i-k8s",
"test-i-aws": "cross-env cloudRunnerTests=true cloudRunnerCluster=aws yarn test -i -t \"cloud runner\"", "test-i-aws": "cross-env cloudRunnerTests=true providerStrategy=aws yarn test -i -t \"cloud runner\"",
"test-i-k8s": "cross-env cloudRunnerTests=true cloudRunnerCluster=k8s yarn test -i -t \"cloud runner\"" "test-i-k8s": "cross-env cloudRunnerTests=true providerStrategy=k8s yarn test -i -t \"cloud runner\""
},
"engines": {
"node": ">=18.x"
}, },
"dependencies": { "dependencies": {
"@actions/cache": "^3.1.3",
"@actions/core": "^1.10.0", "@actions/core": "^1.10.0",
"@actions/exec": "^1.1.0", "@actions/exec": "^1.1.0",
"@actions/github": "^5.0.0", "@actions/github": "^5.0.0",
@@ -38,20 +42,22 @@
"kubernetes-client": "^9.0.0", "kubernetes-client": "^9.0.0",
"nanoid": "^3.3.1", "nanoid": "^3.3.1",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"semver": "^7.3.5", "semver": "^7.5.2",
"unity-changeset": "^1.6.0", "unity-changeset": "^2.0.0",
"uuid": "^8.3.2", "uuid": "^9.0.0",
"yaml": "^1.10.2" "yaml": "^2.2.2"
}, },
"devDependencies": { "devDependencies": {
"@arkweid/lefthook": "^0.7.7", "@evilmartians/lefthook": "^1.2.9",
"@types/base-64": "^1.0.0",
"@types/jest": "^27.4.1", "@types/jest": "^27.4.1",
"@types/node": "^17.0.23", "@types/node": "^17.0.23",
"@types/semver": "^7.3.9", "@types/semver": "^7.3.9",
"@types/uuid": "^9.0.0",
"@typescript-eslint/parser": "4.8.1", "@typescript-eslint/parser": "4.8.1",
"@vercel/ncc": "^0.33.3", "@vercel/ncc": "^0.36.1",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"eslint": "7.17.0", "eslint": "^7.23.0",
"eslint-config-prettier": "8.1.0", "eslint-config-prettier": "8.1.0",
"eslint-plugin-github": "^4.1.1", "eslint-plugin-github": "^4.1.1",
"eslint-plugin-jest": "24.1.3", "eslint-plugin-jest": "24.1.3",
@@ -59,11 +65,16 @@
"eslint-plugin-unicorn": "28.0.2", "eslint-plugin-unicorn": "28.0.2",
"jest": "^27.5.1", "jest": "^27.5.1",
"jest-circus": "^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", "js-yaml": "^4.1.0",
"prettier": "^2.5.1", "prettier": "^2.5.1",
"ts-jest": "^27.1.3", "ts-jest": "^27.1.3",
"ts-node": "10.4.0", "ts-node": "10.4.0",
"typescript": "4.1.3" "typescript": "4.7.4",
"yarn-audit-fix": "^9.3.8"
},
"volta": {
"node": "20.5.1",
"yarn": "1.22.19"
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,15 +3,15 @@ import AndroidVersioning from './android-versioning';
describe('Android Versioning', () => { describe('Android Versioning', () => {
describe('versionToVersionCode', () => { describe('versionToVersionCode', () => {
it('defaults to 0 when versioning strategy is none', () => { 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', () => { 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', () => { 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', () => { it('throw when generated version code is too large', () => {
@@ -21,11 +21,11 @@ describe('Android Versioning', () => {
describe('determineVersionCode', () => { describe('determineVersionCode', () => {
it('defaults to parsed version', () => { 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', () => { 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'; import * as semver from 'semver';
export default class AndroidVersioning { export default class AndroidVersioning {
static determineVersionCode(version, inputVersionCode) { static determineVersionCode(version: string, inputVersionCode: string): string {
if (!inputVersionCode) { if (inputVersionCode === '') {
return AndroidVersioning.versionToVersionCode(version); return AndroidVersioning.versionToVersionCode(version);
} }
return inputVersionCode; return inputVersionCode;
} }
static versionToVersionCode(version) { static versionToVersionCode(version: string): string {
if (version === 'none') { if (version === 'none') {
core.info(`Versioning strategy is set to ${version}, so android version code should not be applied.`); 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); const parsedVersion = semver.parse(version);
@@ -22,7 +22,7 @@ export default class AndroidVersioning {
if (!parsedVersion) { if (!parsedVersion) {
core.warning(`Could not parse "${version}" to semver, defaulting android version code to 1`); 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. // The greatest value Google Plays allows is 2100000000.
@@ -36,10 +36,10 @@ export default class AndroidVersioning {
} }
core.info(`Using android versionCode ${versionCode}`); 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); const parsedVersion = Number.parseInt(targetSdkVersion.slice(-2), 10);
return Number.isNaN(parsedVersion) ? '' : `platforms;android-${parsedVersion}`; return Number.isNaN(parsedVersion) ? '' : `platforms;android-${parsedVersion}`;

View File

@@ -33,7 +33,7 @@ describe('BuildParameters', () => {
it('determines the unity version only once', async () => { it('determines the unity version only once', async () => {
jest.spyOn(UnityVersioning, 'determineUnityVersion').mockImplementation(() => '2019.2.11f1'); jest.spyOn(UnityVersioning, 'determineUnityVersion').mockImplementation(() => '2019.2.11f1');
await BuildParameters.create(); await BuildParameters.create();
await expect(UnityVersioning.determineUnityVersion).toHaveBeenCalledTimes(1); expect(UnityVersioning.determineUnityVersion).toHaveBeenCalledTimes(1);
}); });
it('returns the android version code with provided input', async () => { it('returns the android version code with provided input', async () => {
@@ -47,13 +47,15 @@ describe('BuildParameters', () => {
it('returns the android version code from version by default', async () => { it('returns the android version code from version by default', async () => {
const mockValue = ''; const mockValue = '';
jest.spyOn(Input, 'androidVersionCode', 'get').mockReturnValue(mockValue); jest.spyOn(Input, 'androidVersionCode', 'get').mockReturnValue(mockValue);
await 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 () => { it('determines the android sdk manager parameters only once', async () => {
jest.spyOn(AndroidVersioning, 'determineSdkManagerParameters').mockImplementation(() => 'platforms;android-30'); jest.spyOn(AndroidVersioning, 'determineSdkManagerParameters').mockImplementation(() => 'platforms;android-30');
await BuildParameters.create(); await BuildParameters.create();
await expect(AndroidVersioning.determineSdkManagerParameters).toHaveBeenCalledTimes(1); expect(AndroidVersioning.determineSdkManagerParameters).toHaveBeenCalledTimes(1);
}); });
it('returns the targetPlatform', async () => { it('returns the targetPlatform', async () => {
@@ -95,34 +97,41 @@ describe('BuildParameters', () => {
await expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ buildFile: mockValue })); await expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ buildFile: mockValue }));
}); });
test.each([Platform.types.StandaloneWindows, Platform.types.StandaloneWindows64])( test.each`
'appends exe for %s', targetPlatform | expectedExtension | androidExportType
async (targetPlatform) => { ${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, 'targetPlatform', 'get').mockReturnValue(targetPlatform);
jest.spyOn(Input, 'buildName', 'get').mockReturnValue(targetPlatform); jest.spyOn(Input, 'buildName', 'get').mockReturnValue(targetPlatform);
jest.spyOn(Input, 'androidExportType', 'get').mockReturnValue(androidExportType);
await expect(BuildParameters.create()).resolves.toEqual( await expect(BuildParameters.create()).resolves.toEqual(
expect.objectContaining({ buildFile: `${targetPlatform}.exe` }), expect.objectContaining({ buildFile: `${targetPlatform}${expectedExtension}` }),
); );
}, },
); );
test.each([Platform.types.Android])('appends apk for %s', async (targetPlatform) => { test.each`
jest.spyOn(Input, 'targetPlatform', 'get').mockReturnValue(targetPlatform); targetPlatform | androidSymbolType
jest.spyOn(Input, 'buildName', 'get').mockReturnValue(targetPlatform); ${Platform.types.Android} | ${'none'}
jest.spyOn(Input, 'androidAppBundle', 'get').mockReturnValue(false); ${Platform.types.Android} | ${'public'}
await expect(BuildParameters.create()).resolves.toEqual( ${Platform.types.Android} | ${'debugging'}
expect.objectContaining({ buildFile: `${targetPlatform}.apk` }), ${Platform.types.StandaloneWindows} | ${'none'}
); ${Platform.types.StandaloneWindows64} | ${'none'}
}); `(
'androidSymbolType is set to $androidSymbolType when targetPlatform is $targetPlatform and input targetSymbolType is $androidSymbolType',
test.each([Platform.types.Android])('appends aab for %s', async (targetPlatform) => { async ({ targetPlatform, androidSymbolType }) => {
jest.spyOn(Input, 'targetPlatform', 'get').mockReturnValue(targetPlatform); jest.spyOn(Input, 'targetPlatform', 'get').mockReturnValue(targetPlatform);
jest.spyOn(Input, 'buildName', 'get').mockReturnValue(targetPlatform); jest.spyOn(Input, 'androidSymbolType', 'get').mockReturnValue(androidSymbolType);
jest.spyOn(Input, 'androidAppBundle', 'get').mockReturnValue(true); jest.spyOn(Input, 'buildName', 'get').mockReturnValue(targetPlatform);
await expect(BuildParameters.create()).resolves.toEqual( await expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ androidSymbolType }));
expect.objectContaining({ buildFile: `${targetPlatform}.aab` }), },
); );
});
it('returns the build method', async () => { it('returns the build method', async () => {
const mockValue = 'Namespace.ClassName.BuildMethod'; const mockValue = 'Namespace.ClassName.BuildMethod';

View File

@@ -1,7 +1,7 @@
import { customAlphabet } from 'nanoid'; import { customAlphabet } from 'nanoid';
import AndroidVersioning from './android-versioning'; import AndroidVersioning from './android-versioning';
import CloudRunnerConstants from './cloud-runner/services/cloud-runner-constants'; import CloudRunnerConstants from './cloud-runner/options/cloud-runner-constants';
import CloudRunnerBuildGuid from './cloud-runner/services/cloud-runner-guid'; import CloudRunnerBuildGuid from './cloud-runner/options/cloud-runner-guid';
import Input from './input'; import Input from './input';
import Platform from './platform'; import Platform from './platform';
import UnityVersioning from './unity-versioning'; import UnityVersioning from './unity-versioning';
@@ -10,14 +10,19 @@ import { GitRepoReader } from './input-readers/git-repo';
import { GithubCliReader } from './input-readers/github-cli'; import { GithubCliReader } from './input-readers/github-cli';
import { Cli } from './cli/cli'; import { Cli } from './cli/cli';
import GitHub from './github'; import GitHub from './github';
import CloudRunnerOptions from './cloud-runner/cloud-runner-options'; import CloudRunnerOptions from './cloud-runner/options/cloud-runner-options';
import CloudRunner from './cloud-runner/cloud-runner';
import * as core from '@actions/core';
class BuildParameters { class BuildParameters {
// eslint-disable-next-line no-undef
[key: string]: any;
public editorVersion!: string; public editorVersion!: string;
public customImage!: string; public customImage!: string;
public unitySerial!: string; public unitySerial!: string;
public unityLicensingServer!: string; public unityLicensingServer!: string;
public runnerTempPath: string | undefined; public runnerTempPath!: string;
public targetPlatform!: string; public targetPlatform!: string;
public projectPath!: string; public projectPath!: string;
public buildName!: string; public buildName!: string;
@@ -25,6 +30,7 @@ class BuildParameters {
public buildFile!: string; public buildFile!: string;
public buildMethod!: string; public buildMethod!: string;
public buildVersion!: string; public buildVersion!: string;
public manualExit!: boolean;
public androidVersionCode!: string; public androidVersionCode!: string;
public androidKeystoreName!: string; public androidKeystoreName!: string;
public androidKeystoreBase64!: string; public androidKeystoreBase64!: string;
@@ -33,26 +39,35 @@ class BuildParameters {
public androidKeyaliasPass!: string; public androidKeyaliasPass!: string;
public androidTargetSdkVersion!: string; public androidTargetSdkVersion!: string;
public androidSdkManagerParameters!: 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 customParameters!: string;
public sshAgent!: string; public sshAgent!: string;
public cloudRunnerCluster!: string; public sshPublicKeysDirectoryPath!: string;
public awsBaseStackName!: string; public providerStrategy!: string;
public gitPrivateToken!: string; public gitPrivateToken!: string;
public awsStackName!: string; public awsStackName!: string;
public kubeConfig!: string; public kubeConfig!: string;
public cloudRunnerMemory!: string; public containerMemory!: string;
public cloudRunnerCpu!: string; public containerCpu!: string;
public kubeVolumeSize!: string; public kubeVolumeSize!: string;
public kubeVolume!: string; public kubeVolume!: string;
public kubeStorageClass!: string; public kubeStorageClass!: string;
public runAsHostUser!: String;
public chownFilesTo!: string; public chownFilesTo!: string;
public customJobHooks!: string; public commandHooks!: string;
public readInputFromOverrideList!: string; public pullInputList!: string[];
public readInputOverrideCommand!: string; public inputPullCommand!: string;
public cacheKey!: string; public cacheKey!: string;
public postBuildSteps!: string; public postBuildContainerHooks!: string;
public preBuildSteps!: string; public preBuildContainerHooks!: string;
public customJob!: string; public customJob!: string;
public runNumber!: string; public runNumber!: string;
public branch!: string; public branch!: string;
@@ -61,48 +76,77 @@ class BuildParameters {
public logId!: string; public logId!: string;
public buildGuid!: string; public buildGuid!: string;
public cloudRunnerBranch!: string; public cloudRunnerBranch!: string;
public cloudRunnerDebug!: boolean; public cloudRunnerDebug!: boolean | undefined;
public cloudRunnerBuilderPlatform!: string | undefined; public buildPlatform!: string | undefined;
public isCliMode!: boolean; public isCliMode!: boolean;
public retainWorkspace!: boolean;
public maxRetainedWorkspaces!: number; public maxRetainedWorkspaces!: number;
public useSharedLargePackages!: boolean; public useLargePackages!: boolean;
public useLz4Compression!: boolean; public useCompressionStrategy!: boolean;
public garbageCollectionMaxAge!: number; public garbageMaxAge!: number;
public constantGarbageCollection!: boolean;
public githubChecks!: boolean; 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 && CloudRunner.lockedWorkspace !== ``;
}
static async create(): Promise<BuildParameters> { 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 editorVersion = UnityVersioning.determineUnityVersion(Input.projectPath, Input.unityVersion);
const buildVersion = await Versioning.determineBuildVersion(Input.versioningStrategy, Input.specifiedVersion); const buildVersion = await Versioning.determineBuildVersion(Input.versioningStrategy, Input.specifiedVersion);
const androidVersionCode = AndroidVersioning.determineVersionCode(buildVersion, Input.androidVersionCode); const androidVersionCode = AndroidVersioning.determineVersionCode(buildVersion, Input.androidVersionCode);
const androidSdkManagerParameters = AndroidVersioning.determineSdkManagerParameters(Input.androidTargetSdkVersion); const androidSdkManagerParameters = AndroidVersioning.determineSdkManagerParameters(Input.androidTargetSdkVersion);
// Todo - Don't use process.env directly, that's what the input model class is for. 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`,
);
}
}
let unitySerial = ''; let unitySerial = '';
if (Input.unityLicensingServer === '') { if (Input.unityLicensingServer === '') {
if (!process.env.UNITY_SERIAL && GitHub.githubInputEnabled) { if (!Input.unitySerial && GitHub.githubInputEnabled) {
// No serial was present, so it is a personal license that we need to convert // No serial was present, so it is a personal license that we need to convert
if (!process.env.UNITY_LICENSE) { if (!Input.unityLicense) {
throw new Error(`Missing Unity License File and no Serial was found. If this throw new Error(
`Missing Unity License File and no Serial was found. If this
is a personal license, make sure to follow the activation is a personal license, make sure to follow the activation
steps and set the UNITY_LICENSE GitHub secret or enter a Unity steps and set the UNITY_LICENSE GitHub secret or enter a Unity
serial number inside the UNITY_SERIAL GitHub secret.`); serial number inside the UNITY_SERIAL GitHub secret.`,
);
} }
unitySerial = this.getSerialFromLicenseFile(process.env.UNITY_LICENSE); unitySerial = this.getSerialFromLicenseFile(Input.unityLicense);
} else { } else {
unitySerial = process.env.UNITY_SERIAL!; unitySerial = Input.unitySerial!;
} }
} }
if (unitySerial !== undefined && unitySerial.length === 27) {
core.setSecret(unitySerial);
core.setSecret(`${unitySerial.slice(0, -4)}XXXX`);
}
return { return {
editorVersion, editorVersion,
customImage: Input.customImage, customImage: Input.customImage,
unitySerial, unitySerial,
unityLicensingServer: Input.unityLicensingServer, unityLicensingServer: Input.unityLicensingServer,
runnerTempPath: process.env.RUNNER_TEMP, runnerTempPath: Input.runnerTempPath,
targetPlatform: Input.targetPlatform, targetPlatform: Input.targetPlatform,
projectPath: Input.projectPath, projectPath: Input.projectPath,
buildName: Input.buildName, buildName: Input.buildName,
@@ -110,6 +154,7 @@ class BuildParameters {
buildFile, buildFile,
buildMethod: Input.buildMethod, buildMethod: Input.buildMethod,
buildVersion, buildVersion,
manualExit: Input.manualExit,
androidVersionCode, androidVersionCode,
androidKeystoreName: Input.androidKeystoreName, androidKeystoreName: Input.androidKeystoreName,
androidKeystoreBase64: Input.androidKeystoreBase64, androidKeystoreBase64: Input.androidKeystoreBase64,
@@ -118,20 +163,28 @@ class BuildParameters {
androidKeyaliasPass: Input.androidKeyaliasPass, androidKeyaliasPass: Input.androidKeyaliasPass,
androidTargetSdkVersion: Input.androidTargetSdkVersion, androidTargetSdkVersion: Input.androidTargetSdkVersion,
androidSdkManagerParameters, androidSdkManagerParameters,
androidExportType: Input.androidExportType,
androidSymbolType: androidSymbolExportType,
customParameters: Input.customParameters, customParameters: Input.customParameters,
sshAgent: Input.sshAgent, sshAgent: Input.sshAgent,
sshPublicKeysDirectoryPath: Input.sshPublicKeysDirectoryPath,
gitPrivateToken: Input.gitPrivateToken || (await GithubCliReader.GetGitHubAuthToken()), gitPrivateToken: Input.gitPrivateToken || (await GithubCliReader.GetGitHubAuthToken()),
runAsHostUser: Input.runAsHostUser,
chownFilesTo: Input.chownFilesTo, chownFilesTo: Input.chownFilesTo,
cloudRunnerCluster: CloudRunnerOptions.cloudRunnerCluster, dockerCpuLimit: Input.dockerCpuLimit,
cloudRunnerBuilderPlatform: CloudRunnerOptions.cloudRunnerBuilderPlatform, dockerMemoryLimit: Input.dockerMemoryLimit,
awsBaseStackName: CloudRunnerOptions.awsBaseStackName, dockerIsolationMode: Input.dockerIsolationMode,
containerRegistryRepository: Input.containerRegistryRepository,
containerRegistryImageVersion: Input.containerRegistryImageVersion,
providerStrategy: CloudRunnerOptions.providerStrategy,
buildPlatform: CloudRunnerOptions.buildPlatform,
kubeConfig: CloudRunnerOptions.kubeConfig, kubeConfig: CloudRunnerOptions.kubeConfig,
cloudRunnerMemory: CloudRunnerOptions.cloudRunnerMemory, containerMemory: CloudRunnerOptions.containerMemory,
cloudRunnerCpu: CloudRunnerOptions.cloudRunnerCpu, containerCpu: CloudRunnerOptions.containerCpu,
kubeVolumeSize: CloudRunnerOptions.kubeVolumeSize, kubeVolumeSize: CloudRunnerOptions.kubeVolumeSize,
kubeVolume: CloudRunnerOptions.kubeVolume, kubeVolume: CloudRunnerOptions.kubeVolume,
postBuildSteps: CloudRunnerOptions.postBuildSteps, postBuildContainerHooks: CloudRunnerOptions.postBuildContainerHooks,
preBuildSteps: CloudRunnerOptions.preBuildSteps, preBuildContainerHooks: CloudRunnerOptions.preBuildContainerHooks,
customJob: CloudRunnerOptions.customJob, customJob: CloudRunnerOptions.customJob,
runNumber: Input.runNumber, runNumber: Input.runNumber,
branch: Input.branch.replace('/head', '') || (await GitRepoReader.GetBranch()), branch: Input.branch.replace('/head', '') || (await GitRepoReader.GetBranch()),
@@ -139,38 +192,55 @@ class BuildParameters {
cloudRunnerDebug: CloudRunnerOptions.cloudRunnerDebug, cloudRunnerDebug: CloudRunnerOptions.cloudRunnerDebug,
githubRepo: Input.githubRepo || (await GitRepoReader.GetRemote()) || 'game-ci/unity-builder', githubRepo: Input.githubRepo || (await GitRepoReader.GetRemote()) || 'game-ci/unity-builder',
isCliMode: Cli.isCliMode, isCliMode: Cli.isCliMode,
awsStackName: CloudRunnerOptions.awsBaseStackName, awsStackName: CloudRunnerOptions.awsStackName,
gitSha: Input.gitSha, gitSha: Input.gitSha,
logId: customAlphabet(CloudRunnerConstants.alphabet, 9)(), logId: customAlphabet(CloudRunnerConstants.alphabet, 9)(),
buildGuid: CloudRunnerBuildGuid.generateGuid(Input.runNumber, Input.targetPlatform), buildGuid: CloudRunnerBuildGuid.generateGuid(Input.runNumber, Input.targetPlatform),
customJobHooks: CloudRunnerOptions.customJobHooks(), commandHooks: CloudRunnerOptions.commandHooks,
readInputOverrideCommand: CloudRunnerOptions.readInputOverrideCommand(), inputPullCommand: CloudRunnerOptions.inputPullCommand,
readInputFromOverrideList: CloudRunnerOptions.readInputFromOverrideList(), pullInputList: CloudRunnerOptions.pullInputList,
kubeStorageClass: CloudRunnerOptions.kubeStorageClass, kubeStorageClass: CloudRunnerOptions.kubeStorageClass,
cacheKey: CloudRunnerOptions.cacheKey, cacheKey: CloudRunnerOptions.cacheKey,
retainWorkspace: CloudRunnerOptions.retainWorkspaces, maxRetainedWorkspaces: Number.parseInt(CloudRunnerOptions.maxRetainedWorkspaces),
useSharedLargePackages: CloudRunnerOptions.useSharedLargePackages, useLargePackages: CloudRunnerOptions.useLargePackages,
useLz4Compression: CloudRunnerOptions.useLz4Compression, useCompressionStrategy: CloudRunnerOptions.useCompressionStrategy,
maxRetainedWorkspaces: CloudRunnerOptions.maxRetainedWorkspaces, garbageMaxAge: CloudRunnerOptions.garbageMaxAge,
constantGarbageCollection: CloudRunnerOptions.constantGarbageCollection,
garbageCollectionMaxAge: CloudRunnerOptions.garbageCollectionMaxAge,
githubChecks: CloudRunnerOptions.githubChecks, githubChecks: CloudRunnerOptions.githubChecks,
asyncWorkflow: CloudRunnerOptions.asyncCloudRunner,
githubCheckId: CloudRunnerOptions.githubCheckId,
finalHooks: CloudRunnerOptions.finalHooks,
skipLfs: CloudRunnerOptions.skipLfs,
skipCache: CloudRunnerOptions.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)) { if (Platform.isWindows(platform)) {
return `${filename}.exe`; return `${filename}.exe`;
} }
if (Platform.isAndroid(platform)) { 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; return filename;
} }
static getSerialFromLicenseFile(license) { static getSerialFromLicenseFile(license: string) {
const startKey = `<DeveloperData Value="`; const startKey = `<DeveloperData Value="`;
const endKey = `"/>`; const endKey = `"/>`;
const startIndex = license.indexOf(startKey) + startKey.length; const startIndex = license.indexOf(startKey) + startKey.length;

View File

@@ -1,5 +1,5 @@
import * as core from '@actions/core'; import * as core from '@actions/core';
import fs from 'fs'; import fs from 'node:fs';
import Action from './action'; import Action from './action';
import Project from './project'; 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); const results = CliFunctionsRepository.targets.find((x) => x.key === key);
if (results === undefined || results.length === 0) { if (results === undefined || results.length === 0) {
throw new Error(`no CLI mode found for ${key}`); throw new Error(`no CLI mode found for ${key}`);

View File

@@ -2,24 +2,25 @@ import { Command } from 'commander-ts';
import { BuildParameters, CloudRunner, ImageTag, Input } from '..'; import { BuildParameters, CloudRunner, ImageTag, Input } from '..';
import * as core from '@actions/core'; import * as core from '@actions/core';
import { ActionYamlReader } from '../input-readers/action-yaml'; import { ActionYamlReader } from '../input-readers/action-yaml';
import CloudRunnerLogger from '../cloud-runner/services/cloud-runner-logger'; import CloudRunnerLogger from '../cloud-runner/services/core/cloud-runner-logger';
import CloudRunnerQueryOverride from '../cloud-runner/services/cloud-runner-query-override'; import CloudRunnerQueryOverride from '../cloud-runner/options/cloud-runner-query-override';
import { CliFunction, CliFunctionsRepository } from './cli-functions-repository'; import { CliFunction, CliFunctionsRepository } from './cli-functions-repository';
import { Caching } from '../cloud-runner/remote-client/caching'; import { Caching } from '../cloud-runner/remote-client/caching';
import { LfsHashing } from '../cloud-runner/services/lfs-hashing'; import { LfsHashing } from '../cloud-runner/services/utility/lfs-hashing';
import { RemoteClient } from '../cloud-runner/remote-client'; import { RemoteClient } from '../cloud-runner/remote-client';
import CloudRunnerOptionsReader from '../cloud-runner/services/cloud-runner-options-reader'; import CloudRunnerOptionsReader from '../cloud-runner/options/cloud-runner-options-reader';
import GitHub from '../github'; import GitHub from '../github';
import { TaskParameterSerializer } from '../cloud-runner/services/task-parameter-serializer'; import { CloudRunnerFolders } from '../cloud-runner/options/cloud-runner-folders';
import { CloudRunnerFolders } from '../cloud-runner/services/cloud-runner-folders'; import { CloudRunnerSystem } from '../cloud-runner/services/core/cloud-runner-system';
import { CloudRunnerSystem } from '../cloud-runner/services/cloud-runner-system'; import { OptionValues } from 'commander';
import { InputKey } from '../input';
export class Cli { export class Cli {
public static options; public static options: OptionValues | undefined;
static get isCliMode() { static get isCliMode() {
return Cli.options !== undefined && Cli.options.mode !== undefined && Cli.options.mode !== ''; 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) { if (Cli.options && Cli.options[key] !== undefined) {
return Cli.options[key]; return Cli.options[key];
} }
@@ -61,22 +62,24 @@ export class Cli {
static async RunCli(): Promise<void> { static async RunCli(): Promise<void> {
GitHub.githubInputEnabled = false; GitHub.githubInputEnabled = false;
if (Cli.options['populateOverride'] === `true`) { if (Cli.options!['populateOverride'] === `true`) {
await CloudRunnerQueryOverride.PopulateQueryOverrideInput(); await CloudRunnerQueryOverride.PopulateQueryOverrideInput();
} }
if (Cli.options['logInput']) { if (Cli.options!['logInput']) {
Cli.logInput(); Cli.logInput();
} }
const results = CliFunctionsRepository.GetCliFunctions(Cli.options.mode); const results = CliFunctionsRepository.GetCliFunctions(Cli.options?.mode);
CloudRunnerLogger.log(`Entrypoint: ${results.key}`); CloudRunnerLogger.log(`Entrypoint: ${results.key}`);
Cli.options.versioning = 'None'; Cli.options!.versioning = 'None';
const buildParameter = TaskParameterSerializer.readBuildParameterFromEnvironment(); CloudRunner.buildParameters = await BuildParameters.create();
CloudRunner.buildParameters.buildGuid = process.env.BUILD_GUID || ``;
CloudRunnerLogger.log(`Build Params: CloudRunnerLogger.log(`Build Params:
${JSON.stringify(buildParameter, undefined, 4)} ${JSON.stringify(CloudRunner.buildParameters, undefined, 4)}
`); `);
CloudRunner.buildParameters = buildParameter; CloudRunner.lockedWorkspace = process.env.LOCKED_WORKSPACE || ``;
CloudRunner.lockedWorkspace = process.env.LOCKED_WORKSPACE; CloudRunnerLogger.log(`Locked Workspace: ${CloudRunner.lockedWorkspace}`);
await CloudRunner.setup(CloudRunner.buildParameters);
return await results.target[results.propertyKey](Cli.options); return await results.target[results.propertyKey](Cli.options);
} }
@@ -88,14 +91,15 @@ export class Cli {
const properties = CloudRunnerOptionsReader.GetProperties(); const properties = CloudRunnerOptionsReader.GetProperties();
for (const element of properties) { for (const element of properties) {
if ( if (
Input[element] !== undefined && element in Input &&
Input[element] !== '' && Input[element as InputKey] !== undefined &&
typeof Input[element] !== `function` && Input[element as InputKey] !== '' &&
typeof Input[element as InputKey] !== `function` &&
element !== 'length' && element !== 'length' &&
element !== 'cliOptions' && element !== 'cliOptions' &&
element !== 'prototype' element !== 'prototype'
) { ) {
core.info(`${element} ${Input[element]}`); core.info(`${element} ${Input[element as InputKey]}`);
} }
} }
core.info(`\n`); core.info(`\n`);
@@ -113,12 +117,16 @@ export class Cli {
public static async asyncronousWorkflow(): Promise<string> { public static async asyncronousWorkflow(): Promise<string> {
const buildParameter = await BuildParameters.create(); const buildParameter = await BuildParameters.create();
const baseImage = new ImageTag(buildParameter); const baseImage = new ImageTag(buildParameter);
await CloudRunner.setup(buildParameter);
return await CloudRunner.run(buildParameter, baseImage.toString()); return await CloudRunner.run(buildParameter, baseImage.toString());
} }
@CliFunction(`checks-update`, `runs a cloud runner build`) @CliFunction(`checks-update`, `runs a cloud runner build`)
public static async checksUpdate() { public static async checksUpdate() {
const buildParameter = await BuildParameters.create();
await CloudRunner.setup(buildParameter);
const input = JSON.parse(process.env.CHECKS_UPDATE || ``); const input = JSON.parse(process.env.CHECKS_UPDATE || ``);
core.info(`Checks Update ${process.env.CHECKS_UPDATE}`); core.info(`Checks Update ${process.env.CHECKS_UPDATE}`);
if (input.mode === `create`) { if (input.mode === `create`) {
@@ -182,7 +190,7 @@ export class Cli {
`build-${CloudRunner.buildParameters.buildGuid}`, `build-${CloudRunner.buildParameters.buildGuid}`,
); );
if (!CloudRunner.buildParameters.retainWorkspace) { if (!BuildParameters.shouldUseRetainedWorkspaceMode(CloudRunner.buildParameters)) {
await CloudRunnerSystem.Run( await CloudRunnerSystem.Run(
`rm -r ${CloudRunnerFolders.ToLinuxFolder(CloudRunnerFolders.uniqueCloudRunnerJobFolderAbsolute)}`, `rm -r ${CloudRunnerFolders.ToLinuxFolder(CloudRunnerFolders.uniqueCloudRunnerJobFolderAbsolute)}`,
); );
@@ -190,21 +198,6 @@ export class Cli {
await RemoteClient.runCustomHookFiles(`after-build`); await RemoteClient.runCustomHookFiles(`after-build`);
const parameters = await BuildParameters.create();
CloudRunner.setup(parameters);
if (parameters.constantGarbageCollection) {
await CloudRunnerSystem.Run(
`find /${CloudRunnerFolders.ToLinuxFolder(CloudRunnerFolders.buildVolumeFolder)}/ -name '*.*' -mmin +${
parameters.garbageCollectionMaxAge * 60
} -delete`,
);
await CloudRunnerSystem.Run(
`find ${CloudRunnerFolders.ToLinuxFolder(CloudRunnerFolders.cacheFolderForAllFull)} -name '*.*' -mmin +${
parameters.garbageCollectionMaxAge * 60
} -delete`,
);
}
return new Promise((result) => result(``)); return new Promise((result) => result(``));
} }
} }

View File

@@ -1,290 +0,0 @@
import { Cli } from '../cli/cli';
import CloudRunnerQueryOverride from './services/cloud-runner-query-override';
import GitHub from '../github';
const core = require('@actions/core');
class CloudRunnerOptions {
// ### ### ###
// Input Handling
// ### ### ###
public static getInput(query) {
if (GitHub.githubInputEnabled) {
const coreInput = core.getInput(query);
if (coreInput && coreInput !== '') {
return coreInput;
}
}
const alternativeQuery = CloudRunnerOptions.ToEnvVarFormat(query);
// Query input sources
if (Cli.query(query, alternativeQuery)) {
return Cli.query(query, alternativeQuery);
}
if (CloudRunnerQueryOverride.query(query, alternativeQuery)) {
return CloudRunnerQueryOverride.query(query, alternativeQuery);
}
if (process.env[query] !== undefined) {
return process.env[query];
}
if (alternativeQuery !== query && process.env[alternativeQuery] !== undefined) {
return process.env[alternativeQuery];
}
return;
}
public static ToEnvVarFormat(input: string) {
if (input.toUpperCase() === input) {
return input;
}
return input
.replace(/([A-Z])/g, ' $1')
.trim()
.toUpperCase()
.replace(/ /g, '_');
}
// ### ### ###
// Provider parameters
// ### ### ###
static get region(): string {
return CloudRunnerOptions.getInput('region') || 'eu-west-2';
}
// ### ### ###
// GitHub parameters
// ### ### ###
static get githubChecks(): boolean {
return CloudRunnerOptions.getInput('githubChecks') || false;
}
static get githubOwner() {
return CloudRunnerOptions.getInput('githubOwner') || CloudRunnerOptions.githubRepo.split(`/`)[0] || false;
}
static get githubRepoName() {
return CloudRunnerOptions.getInput('githubRepoName') || CloudRunnerOptions.githubRepo.split(`/`)[1] || false;
}
// ### ### ###
// Git syncronization parameters
// ### ### ###
static get githubRepo() {
return CloudRunnerOptions.getInput('GITHUB_REPOSITORY') || CloudRunnerOptions.getInput('GITHUB_REPO') || undefined;
}
static get branch() {
if (CloudRunnerOptions.getInput(`GITHUB_REF`)) {
return CloudRunnerOptions.getInput(`GITHUB_REF`).replace('refs/', '').replace(`head/`, '').replace(`heads/`, '');
} else if (CloudRunnerOptions.getInput('branch')) {
return CloudRunnerOptions.getInput('branch');
} else {
return '';
}
}
static get gitSha() {
if (CloudRunnerOptions.getInput(`GITHUB_SHA`)) {
return CloudRunnerOptions.getInput(`GITHUB_SHA`);
} else if (CloudRunnerOptions.getInput(`GitSHA`)) {
return CloudRunnerOptions.getInput(`GitSHA`);
}
}
// ### ### ###
// Cloud Runner parameters
// ### ### ###
static get cloudRunnerBuilderPlatform() {
const input = CloudRunnerOptions.getInput('cloudRunnerBuilderPlatform');
if (input) {
return input;
}
if (CloudRunnerOptions.cloudRunnerCluster !== 'local') {
return 'linux';
}
return;
}
static get cloudRunnerBranch() {
return CloudRunnerOptions.getInput('cloudRunnerBranch') || 'main';
}
static get cloudRunnerCluster() {
if (Cli.isCliMode) {
return CloudRunnerOptions.getInput('cloudRunnerCluster') || 'aws';
}
return CloudRunnerOptions.getInput('cloudRunnerCluster') || 'local';
}
static get cloudRunnerCpu() {
return CloudRunnerOptions.getInput('cloudRunnerCpu');
}
static get cloudRunnerMemory() {
return CloudRunnerOptions.getInput('cloudRunnerMemory');
}
static get customJob() {
return CloudRunnerOptions.getInput('customJob') || '';
}
// ### ### ###
// Custom commands from files parameters
// ### ### ###
static get customStepFiles() {
return CloudRunnerOptions.getInput('customStepFiles')?.split(`,`) || [];
}
static get customHookFiles() {
return CloudRunnerOptions.getInput('customHookFiles')?.split(`,`) || [];
}
// ### ### ###
// Custom commands from yaml parameters
// ### ### ###
static customJobHooks() {
return CloudRunnerOptions.getInput('customJobHooks') || '';
}
static get postBuildSteps() {
return CloudRunnerOptions.getInput('postBuildSteps') || '';
}
static get preBuildSteps() {
return CloudRunnerOptions.getInput('preBuildSteps') || '';
}
// ### ### ###
// Input override handling
// ### ### ###
static readInputFromOverrideList() {
return CloudRunnerOptions.getInput('readInputFromOverrideList') || '';
}
static readInputOverrideCommand() {
const value = CloudRunnerOptions.getInput('readInputOverrideCommand');
if (value === 'gcp-secret-manager') {
return 'gcloud secrets versions access 1 --secret="{0}"';
} else if (value === 'aws-secret-manager') {
return 'aws secretsmanager get-secret-value --secret-id {0}';
}
return value || '';
}
// ### ### ###
// Aws
// ### ### ###
static get awsBaseStackName() {
return CloudRunnerOptions.getInput('awsBaseStackName') || 'game-ci';
}
// ### ### ###
// K8s
// ### ### ###
static get kubeConfig() {
return CloudRunnerOptions.getInput('kubeConfig') || '';
}
static get kubeVolume() {
return CloudRunnerOptions.getInput('kubeVolume') || '';
}
static get kubeVolumeSize() {
return CloudRunnerOptions.getInput('kubeVolumeSize') || '5Gi';
}
static get kubeStorageClass(): string {
return CloudRunnerOptions.getInput('kubeStorageClass') || '';
}
// ### ### ###
// Caching
// ### ### ###
static get cacheKey(): string {
return CloudRunnerOptions.getInput('cacheKey') || CloudRunnerOptions.branch;
}
// ### ### ###
// Utility Parameters
// ### ### ###
static get cloudRunnerDebug(): boolean {
return CloudRunnerOptions.getInput(`cloudRunnerTests`) || CloudRunnerOptions.getInput(`cloudRunnerDebug`) || false;
}
static get cloudRunnerDebugTree(): boolean {
return CloudRunnerOptions.getInput(`cloudRunnerDebugTree`) || false;
}
static get cloudRunnerDebugEnv(): boolean {
return CloudRunnerOptions.getInput(`cloudRunnerDebugEnv`) || false;
}
static get watchCloudRunnerToEnd(): boolean {
if (CloudRunnerOptions.asyncCloudRunner) {
return false;
}
return CloudRunnerOptions.getInput(`watchToEnd`) || true;
}
static get asyncCloudRunner(): boolean {
return (CloudRunnerOptions.getInput('asyncCloudRunner') || `false`) === `true` || false;
}
public static get useSharedLargePackages(): boolean {
return (CloudRunnerOptions.getInput(`useSharedLargePackages`) || 'false') === 'true';
}
public static get useSharedBuilder(): boolean {
return (CloudRunnerOptions.getInput(`useSharedBuilder`) || 'true') === 'true';
}
public static get useLz4Compression(): boolean {
return (CloudRunnerOptions.getInput(`useLz4Compression`) || 'false') === 'true';
}
public static get useCleanupCron(): boolean {
return (CloudRunnerOptions.getInput(`useCleanupCron`) || 'true') === 'true';
}
// ### ### ###
// Retained Workspace
// ### ### ###
public static get retainWorkspaces(): boolean {
return CloudRunnerOptions.getInput(`retainWorkspaces`) || false;
}
static get maxRetainedWorkspaces(): number {
return Number(CloudRunnerOptions.getInput(`maxRetainedWorkspaces`)) || 3;
}
// ### ### ###
// Garbage Collection
// ### ### ###
static get constantGarbageCollection(): boolean {
return CloudRunnerOptions.getInput(`constantGarbageCollection`) || true;
}
static get garbageCollectionMaxAge(): number {
return Number(CloudRunnerOptions.getInput(`garbageCollectionMaxAge`)) || 24;
}
}
export default CloudRunnerOptions;

View File

@@ -1,33 +1,42 @@
import AwsBuildPlatform from './providers/aws'; import AwsBuildPlatform from './providers/aws';
import { BuildParameters, Input } from '..'; import { BuildParameters, Input } from '..';
import Kubernetes from './providers/k8s'; import Kubernetes from './providers/k8s';
import CloudRunnerLogger from './services/cloud-runner-logger'; import CloudRunnerLogger from './services/core/cloud-runner-logger';
import { CloudRunnerStepState } from './cloud-runner-step-state'; import { CloudRunnerStepParameters } from './options/cloud-runner-step-parameters';
import { WorkflowCompositionRoot } from './workflows/workflow-composition-root'; import { WorkflowCompositionRoot } from './workflows/workflow-composition-root';
import { CloudRunnerError } from './error/cloud-runner-error'; import { CloudRunnerError } from './error/cloud-runner-error';
import { TaskParameterSerializer } from './services/task-parameter-serializer'; import { TaskParameterSerializer } from './services/core/task-parameter-serializer';
import * as core from '@actions/core'; import * as core from '@actions/core';
import CloudRunnerSecret from './services/cloud-runner-secret'; import CloudRunnerSecret from './options/cloud-runner-secret';
import { ProviderInterface } from './providers/provider-interface'; import { ProviderInterface } from './providers/provider-interface';
import CloudRunnerEnvironmentVariable from './services/cloud-runner-environment-variable'; import CloudRunnerEnvironmentVariable from './options/cloud-runner-environment-variable';
import TestCloudRunner from './providers/test'; import TestCloudRunner from './providers/test';
import LocalCloudRunner from './providers/local'; import LocalCloudRunner from './providers/local';
import LocalDockerCloudRunner from './providers/docker'; import LocalDockerCloudRunner from './providers/docker';
import GitHub from '../github'; import GitHub from '../github';
import SharedWorkspaceLocking from './services/shared-workspace-locking'; import SharedWorkspaceLocking from './services/core/shared-workspace-locking';
import { FollowLogStreamService } from './services/core/follow-log-stream-service';
class CloudRunner { class CloudRunner {
public static Provider: ProviderInterface; public static Provider: ProviderInterface;
public static buildParameters: BuildParameters; public static buildParameters: BuildParameters;
private static defaultSecrets: CloudRunnerSecret[]; private static defaultSecrets: CloudRunnerSecret[];
private static cloudRunnerEnvironmentVariables: CloudRunnerEnvironmentVariable[]; private static cloudRunnerEnvironmentVariables: CloudRunnerEnvironmentVariable[];
static lockedWorkspace: string | undefined; static lockedWorkspace: string = ``;
public static readonly retainedWorkspacePrefix: string = `retained-workspace`; public static readonly retainedWorkspacePrefix: string = `retained-workspace`;
public static githubCheckId; public static get isCloudRunnerEnvironment() {
public static setup(buildParameters: BuildParameters) { return process.env[`GITHUB_ACTIONS`] !== `true`;
}
public static get isCloudRunnerAsyncEnvironment() {
return process.env[`ASYNC_WORKFLOW`] === `true`;
}
public static async setup(buildParameters: BuildParameters) {
CloudRunnerLogger.setup(); CloudRunnerLogger.setup();
CloudRunnerLogger.log(`Setting up cloud runner`); CloudRunnerLogger.log(`Setting up cloud runner`);
CloudRunner.buildParameters = buildParameters; CloudRunner.buildParameters = buildParameters;
if (CloudRunner.buildParameters.githubCheckId === ``) {
CloudRunner.buildParameters.githubCheckId = await GitHub.createGitHubCheck(CloudRunner.buildParameters.buildGuid);
}
CloudRunner.setupSelectedBuildPlatform(); CloudRunner.setupSelectedBuildPlatform();
CloudRunner.defaultSecrets = TaskParameterSerializer.readDefaultSecrets(); CloudRunner.defaultSecrets = TaskParameterSerializer.readDefaultSecrets();
CloudRunner.cloudRunnerEnvironmentVariables = CloudRunner.cloudRunnerEnvironmentVariables =
@@ -45,15 +54,16 @@ class CloudRunner {
core.setOutput( core.setOutput(
Input.ToEnvVarFormat(`buildArtifact`), Input.ToEnvVarFormat(`buildArtifact`),
`build-${CloudRunner.buildParameters.buildGuid}.tar${ `build-${CloudRunner.buildParameters.buildGuid}.tar${
CloudRunner.buildParameters.useLz4Compression ? '.lz4' : '' CloudRunner.buildParameters.useCompressionStrategy ? '.lz4' : ''
}`, }`,
); );
} }
FollowLogStreamService.Reset();
} }
private static setupSelectedBuildPlatform() { private static setupSelectedBuildPlatform() {
CloudRunnerLogger.log(`Cloud Runner platform selected ${CloudRunner.buildParameters.cloudRunnerCluster}`); CloudRunnerLogger.log(`Cloud Runner platform selected ${CloudRunner.buildParameters.providerStrategy}`);
switch (CloudRunner.buildParameters.cloudRunnerCluster) { switch (CloudRunner.buildParameters.providerStrategy) {
case 'k8s': case 'k8s':
CloudRunner.Provider = new Kubernetes(CloudRunner.buildParameters); CloudRunner.Provider = new Kubernetes(CloudRunner.buildParameters);
break; break;
@@ -73,14 +83,20 @@ class CloudRunner {
} }
static async run(buildParameters: BuildParameters, baseImage: string) { static async run(buildParameters: BuildParameters, baseImage: string) {
CloudRunner.setup(buildParameters); await CloudRunner.setup(buildParameters);
if (!CloudRunner.buildParameters.isCliMode) core.startGroup('Setup shared cloud runner resources');
await CloudRunner.Provider.setupWorkflow(
CloudRunner.buildParameters.buildGuid,
CloudRunner.buildParameters,
CloudRunner.buildParameters.branch,
CloudRunner.defaultSecrets,
);
if (!CloudRunner.buildParameters.isCliMode) core.endGroup();
try { try {
CloudRunner.githubCheckId = await GitHub.createGitHubCheck(CloudRunner.buildParameters.buildGuid); if (buildParameters.maxRetainedWorkspaces > 0) {
CloudRunner.lockedWorkspace = SharedWorkspaceLocking.NewWorkspaceName();
if (buildParameters.retainWorkspace) { const result = await SharedWorkspaceLocking.GetLockedWorkspace(
CloudRunner.lockedWorkspace = `${CloudRunner.retainedWorkspacePrefix}-${CloudRunner.buildParameters.buildGuid}`;
const result = await SharedWorkspaceLocking.GetOrCreateLockedWorkspace(
CloudRunner.lockedWorkspace, CloudRunner.lockedWorkspace,
CloudRunner.buildParameters.buildGuid, CloudRunner.buildParameters.buildGuid,
CloudRunner.buildParameters, CloudRunner.buildParameters,
@@ -94,21 +110,21 @@ class CloudRunner {
]; ];
} else { } else {
CloudRunnerLogger.log(`Max retained workspaces reached ${buildParameters.maxRetainedWorkspaces}`); CloudRunnerLogger.log(`Max retained workspaces reached ${buildParameters.maxRetainedWorkspaces}`);
buildParameters.retainWorkspace = false; buildParameters.maxRetainedWorkspaces = 0;
CloudRunner.lockedWorkspace = undefined; CloudRunner.lockedWorkspace = ``;
} }
} }
if (!CloudRunner.buildParameters.isCliMode) core.startGroup('Setup shared cloud runner resources'); const content = { ...CloudRunner.buildParameters };
await CloudRunner.Provider.setupWorkflow( content.gitPrivateToken = ``;
CloudRunner.buildParameters.buildGuid, content.unitySerial = ``;
CloudRunner.buildParameters, const jsonContent = JSON.stringify(content, undefined, 4);
CloudRunner.buildParameters.branch, await GitHub.updateGitHubCheck(jsonContent, CloudRunner.buildParameters.buildGuid);
CloudRunner.defaultSecrets,
);
if (!CloudRunner.buildParameters.isCliMode) core.endGroup();
await GitHub.updateGitHubCheck(CloudRunner.buildParameters.buildGuid, CloudRunner.buildParameters.buildGuid);
const output = await new WorkflowCompositionRoot().run( const output = await new WorkflowCompositionRoot().run(
new CloudRunnerStepState(baseImage, CloudRunner.cloudRunnerEnvironmentVariables, CloudRunner.defaultSecrets), new CloudRunnerStepParameters(
baseImage,
CloudRunner.cloudRunnerEnvironmentVariables,
CloudRunner.defaultSecrets,
),
); );
if (!CloudRunner.buildParameters.isCliMode) core.startGroup('Cleanup shared cloud runner resources'); if (!CloudRunner.buildParameters.isCliMode) core.startGroup('Cleanup shared cloud runner resources');
await CloudRunner.Provider.cleanupWorkflow( await CloudRunner.Provider.cleanupWorkflow(
@@ -121,22 +137,40 @@ class CloudRunner {
if (!CloudRunner.buildParameters.isCliMode) core.endGroup(); if (!CloudRunner.buildParameters.isCliMode) core.endGroup();
await GitHub.updateGitHubCheck(CloudRunner.buildParameters.buildGuid, `success`, `success`, `completed`); await GitHub.updateGitHubCheck(CloudRunner.buildParameters.buildGuid, `success`, `success`, `completed`);
if (CloudRunner.buildParameters.retainWorkspace) { if (BuildParameters.shouldUseRetainedWorkspaceMode(buildParameters)) {
const workspace = CloudRunner.lockedWorkspace || ``;
await SharedWorkspaceLocking.ReleaseWorkspace( await SharedWorkspaceLocking.ReleaseWorkspace(
CloudRunner.lockedWorkspace || ``, workspace,
CloudRunner.buildParameters.buildGuid, CloudRunner.buildParameters.buildGuid,
CloudRunner.buildParameters, CloudRunner.buildParameters,
); );
CloudRunner.lockedWorkspace = undefined; const isLocked = await SharedWorkspaceLocking.IsWorkspaceLocked(workspace, CloudRunner.buildParameters);
if (isLocked) {
throw new Error(
`still locked after releasing ${await SharedWorkspaceLocking.GetAllLocksForWorkspace(
workspace,
buildParameters,
)}`,
);
}
CloudRunner.lockedWorkspace = ``;
} }
await GitHub.triggerWorkflowOnComplete(CloudRunner.buildParameters.finalHooks);
if (buildParameters.constantGarbageCollection) { if (buildParameters.constantGarbageCollection) {
CloudRunner.Provider.garbageCollect(``, true, buildParameters.garbageCollectionMaxAge, true, true); CloudRunner.Provider.garbageCollect(``, true, buildParameters.garbageMaxAge, true, true);
} }
return output; return output;
} catch (error) { } catch (error: any) {
await GitHub.updateGitHubCheck(CloudRunner.buildParameters.buildGuid, error, `failure`, `completed`); CloudRunnerLogger.log(JSON.stringify(error, undefined, 4));
await GitHub.updateGitHubCheck(
CloudRunner.buildParameters.buildGuid,
`Failed - Error ${error?.message || error}`,
`failure`,
`completed`,
);
if (!CloudRunner.buildParameters.isCliMode) core.endGroup(); if (!CloudRunner.buildParameters.isCliMode) core.endGroup();
await CloudRunnerError.handleException(error, CloudRunner.buildParameters, CloudRunner.defaultSecrets); await CloudRunnerError.handleException(error, CloudRunner.buildParameters, CloudRunner.defaultSecrets);
throw error; throw error;

View File

@@ -1,7 +1,7 @@
import CloudRunnerLogger from '../services/cloud-runner-logger'; import CloudRunnerLogger from '../services/core/cloud-runner-logger';
import * as core from '@actions/core'; import * as core from '@actions/core';
import CloudRunner from '../cloud-runner'; import CloudRunner from '../cloud-runner';
import CloudRunnerSecret from '../services/cloud-runner-secret'; import CloudRunnerSecret from '../options/cloud-runner-secret';
import BuildParameters from '../../build-parameters'; import BuildParameters from '../../build-parameters';
export class CloudRunnerError { export class CloudRunnerError {

View File

@@ -1,6 +1,7 @@
import path from 'path'; import path from 'node:path';
import CloudRunnerOptions from '../cloud-runner-options'; import CloudRunnerOptions from './cloud-runner-options';
import CloudRunner from './../cloud-runner'; import CloudRunner from '../cloud-runner';
import BuildParameters from '../../build-parameters';
export class CloudRunnerFolders { export class CloudRunnerFolders {
public static readonly repositoryFolder = 'repo'; public static readonly repositoryFolder = 'repo';
@@ -12,7 +13,7 @@ export class CloudRunnerFolders {
// Only the following paths that do not start a path.join with another "Full" suffixed property need to start with an absolute / // Only the following paths that do not start a path.join with another "Full" suffixed property need to start with an absolute /
public static get uniqueCloudRunnerJobFolderAbsolute(): string { public static get uniqueCloudRunnerJobFolderAbsolute(): string {
return CloudRunner.buildParameters && CloudRunner.buildParameters.retainWorkspace && CloudRunner.lockedWorkspace return CloudRunner.buildParameters && BuildParameters.shouldUseRetainedWorkspaceMode(CloudRunner.buildParameters)
? path.join(`/`, CloudRunnerFolders.buildVolumeFolder, CloudRunner.lockedWorkspace) ? path.join(`/`, CloudRunnerFolders.buildVolumeFolder, CloudRunner.lockedWorkspace)
: path.join(`/`, CloudRunnerFolders.buildVolumeFolder, CloudRunner.buildParameters.buildGuid); : path.join(`/`, CloudRunnerFolders.buildVolumeFolder, CloudRunner.buildParameters.buildGuid);
} }

View File

@@ -1,5 +1,5 @@
import Input from '../../input'; import Input from '../../input';
import CloudRunnerOptions from '../cloud-runner-options'; import CloudRunnerOptions from './cloud-runner-options';
class CloudRunnerOptionsReader { class CloudRunnerOptionsReader {
static GetProperties() { static GetProperties() {

View File

@@ -0,0 +1,283 @@
import { Cli } from '../../cli/cli';
import CloudRunnerQueryOverride from './cloud-runner-query-override';
import GitHub from '../../github';
import * as core from '@actions/core';
class CloudRunnerOptions {
// ### ### ###
// Input Handling
// ### ### ###
public static getInput(query: string): string | undefined {
if (GitHub.githubInputEnabled) {
const coreInput = core.getInput(query);
if (coreInput && coreInput !== '') {
return coreInput;
}
}
const alternativeQuery = CloudRunnerOptions.ToEnvVarFormat(query);
// Query input sources
if (Cli.query(query, alternativeQuery)) {
return Cli.query(query, alternativeQuery);
}
if (CloudRunnerQueryOverride.query(query, alternativeQuery)) {
return CloudRunnerQueryOverride.query(query, alternativeQuery);
}
if (process.env[query] !== undefined) {
return process.env[query];
}
if (alternativeQuery !== query && process.env[alternativeQuery] !== undefined) {
return process.env[alternativeQuery];
}
}
public static ToEnvVarFormat(input: string): string {
if (input.toUpperCase() === input) {
return input;
}
return input
.replace(/([A-Z])/g, ' $1')
.trim()
.toUpperCase()
.replace(/ /g, '_');
}
// ### ### ###
// Provider parameters
// ### ### ###
static get region(): string {
return CloudRunnerOptions.getInput('region') || 'eu-west-2';
}
// ### ### ###
// GitHub parameters
// ### ### ###
static get githubChecks(): boolean {
const value = CloudRunnerOptions.getInput('githubChecks');
return value === `true` || false;
}
static get githubCheckId(): string {
return CloudRunnerOptions.getInput('githubCheckId') || ``;
}
static get githubOwner(): string {
return CloudRunnerOptions.getInput('githubOwner') || CloudRunnerOptions.githubRepo?.split(`/`)[0] || '';
}
static get githubRepoName(): string {
return CloudRunnerOptions.getInput('githubRepoName') || CloudRunnerOptions.githubRepo?.split(`/`)[1] || '';
}
static get finalHooks(): string[] {
return CloudRunnerOptions.getInput('finalHooks')?.split(',') || [];
}
// ### ### ###
// Git syncronization parameters
// ### ### ###
static get githubRepo(): string | undefined {
return CloudRunnerOptions.getInput('GITHUB_REPOSITORY') || CloudRunnerOptions.getInput('GITHUB_REPO') || undefined;
}
static get branch(): string {
if (CloudRunnerOptions.getInput(`GITHUB_REF`)) {
return (
CloudRunnerOptions.getInput(`GITHUB_REF`)?.replace('refs/', '').replace(`head/`, '').replace(`heads/`, '') || ``
);
} else if (CloudRunnerOptions.getInput('branch')) {
return CloudRunnerOptions.getInput('branch') || ``;
} else {
return '';
}
}
// ### ### ###
// Cloud Runner parameters
// ### ### ###
static get buildPlatform(): string {
const input = CloudRunnerOptions.getInput('buildPlatform');
if (input) {
return input;
}
if (CloudRunnerOptions.providerStrategy !== 'local') {
return 'linux';
}
return ``;
}
static get cloudRunnerBranch(): string {
return CloudRunnerOptions.getInput('cloudRunnerBranch') || 'main';
}
static get providerStrategy(): string {
const provider =
CloudRunnerOptions.getInput('cloudRunnerCluster') || CloudRunnerOptions.getInput('providerStrategy');
if (Cli.isCliMode) {
return provider || 'aws';
}
return provider || 'local';
}
static get containerCpu(): string {
return CloudRunnerOptions.getInput('containerCpu') || `1024`;
}
static get containerMemory(): string {
return CloudRunnerOptions.getInput('containerMemory') || `3072`;
}
static get customJob(): string {
return CloudRunnerOptions.getInput('customJob') || '';
}
// ### ### ###
// Custom commands from files parameters
// ### ### ###
static get containerHookFiles(): string[] {
return CloudRunnerOptions.getInput('containerHookFiles')?.split(`,`) || [];
}
static get commandHookFiles(): string[] {
return CloudRunnerOptions.getInput('commandHookFiles')?.split(`,`) || [];
}
// ### ### ###
// Custom commands from yaml parameters
// ### ### ###
static get commandHooks(): string {
return CloudRunnerOptions.getInput('commandHooks') || '';
}
static get postBuildContainerHooks(): string {
return CloudRunnerOptions.getInput('postBuildContainerHooks') || '';
}
static get preBuildContainerHooks(): string {
return CloudRunnerOptions.getInput('preBuildContainerHooks') || '';
}
// ### ### ###
// Input override handling
// ### ### ###
static get pullInputList(): string[] {
return CloudRunnerOptions.getInput('pullInputList')?.split(`,`) || [];
}
static get inputPullCommand(): string {
const value = CloudRunnerOptions.getInput('inputPullCommand');
if (value === 'gcp-secret-manager') {
return 'gcloud secrets versions access 1 --secret="{0}"';
} else if (value === 'aws-secret-manager') {
return 'aws secretsmanager get-secret-value --secret-id {0}';
}
return value || '';
}
// ### ### ###
// Aws
// ### ### ###
static get awsStackName() {
return CloudRunnerOptions.getInput('awsStackName') || 'game-ci';
}
// ### ### ###
// K8s
// ### ### ###
static get kubeConfig(): string {
return CloudRunnerOptions.getInput('kubeConfig') || '';
}
static get kubeVolume(): string {
return CloudRunnerOptions.getInput('kubeVolume') || '';
}
static get kubeVolumeSize(): string {
return CloudRunnerOptions.getInput('kubeVolumeSize') || '25Gi';
}
static get kubeStorageClass(): string {
return CloudRunnerOptions.getInput('kubeStorageClass') || '';
}
// ### ### ###
// Caching
// ### ### ###
static get cacheKey(): string {
return CloudRunnerOptions.getInput('cacheKey') || CloudRunnerOptions.branch;
}
// ### ### ###
// Utility Parameters
// ### ### ###
static get cloudRunnerDebug(): boolean {
return (
CloudRunnerOptions.getInput(`cloudRunnerTests`) === `true` ||
CloudRunnerOptions.getInput(`cloudRunnerDebug`) === `true` ||
CloudRunnerOptions.getInput(`cloudRunnerDebugTree`) === `true` ||
CloudRunnerOptions.getInput(`cloudRunnerDebugEnv`) === `true` ||
false
);
}
static get skipLfs(): boolean {
return CloudRunnerOptions.getInput(`skipLfs`) === `true`;
}
static get skipCache(): boolean {
return CloudRunnerOptions.getInput(`skipCache`) === `true`;
}
public static get asyncCloudRunner(): boolean {
return CloudRunnerOptions.getInput('asyncCloudRunner') === 'true';
}
public static get useLargePackages(): boolean {
return CloudRunnerOptions.getInput(`useLargePackages`) === `true`;
}
public static get useSharedBuilder(): boolean {
return CloudRunnerOptions.getInput(`useSharedBuilder`) === `true`;
}
public static get useCompressionStrategy(): boolean {
return CloudRunnerOptions.getInput(`useCompressionStrategy`) === `true`;
}
public static get useCleanupCron(): boolean {
return (CloudRunnerOptions.getInput(`useCleanupCron`) || 'true') === 'true';
}
// ### ### ###
// Retained Workspace
// ### ### ###
public static get maxRetainedWorkspaces(): string {
return CloudRunnerOptions.getInput(`maxRetainedWorkspaces`) || `0`;
}
// ### ### ###
// Garbage Collection
// ### ### ###
static get garbageMaxAge(): number {
return Number(CloudRunnerOptions.getInput(`garbageMaxAge`)) || 24;
}
}
export default CloudRunnerOptions;

View File

@@ -1,8 +1,8 @@
import Input from '../../input'; import Input from '../../input';
import { GenericInputReader } from '../../input-readers/generic-input-reader'; import { GenericInputReader } from '../../input-readers/generic-input-reader';
import CloudRunnerOptions from '../cloud-runner-options'; import CloudRunnerOptions from './cloud-runner-options';
const formatFunction = (value, arguments_) => { const formatFunction = (value: string, arguments_: any[]) => {
for (const element of arguments_) { for (const element of arguments_) {
value = value.replace(`{${element.key}}`, element.value); value = value.replace(`{${element.key}}`, element.value);
} }
@@ -11,11 +11,11 @@ const formatFunction = (value, arguments_) => {
}; };
class CloudRunnerQueryOverride { class CloudRunnerQueryOverride {
static queryOverrides: any; static queryOverrides: { [key: string]: string } | undefined;
// TODO accept premade secret sources or custom secret source definition yamls // TODO accept premade secret sources or custom secret source definition yamls
public static query(key, alternativeKey) { public static query(key: string, alternativeKey: string) {
if (CloudRunnerQueryOverride.queryOverrides && CloudRunnerQueryOverride.queryOverrides[key] !== undefined) { if (CloudRunnerQueryOverride.queryOverrides && CloudRunnerQueryOverride.queryOverrides[key] !== undefined) {
return CloudRunnerQueryOverride.queryOverrides[key]; return CloudRunnerQueryOverride.queryOverrides[key];
} }
@@ -30,12 +30,12 @@ class CloudRunnerQueryOverride {
return; return;
} }
private static shouldUseOverride(query) { private static shouldUseOverride(query: string) {
if (CloudRunnerOptions.readInputOverrideCommand() !== '') { if (CloudRunnerOptions.inputPullCommand !== '') {
if (CloudRunnerOptions.readInputFromOverrideList() !== '') { if (CloudRunnerOptions.pullInputList.length > 0) {
const doesInclude = const doesInclude =
CloudRunnerOptions.readInputFromOverrideList().split(',').includes(query) || CloudRunnerOptions.pullInputList.includes(query) ||
CloudRunnerOptions.readInputFromOverrideList().split(',').includes(Input.ToEnvVarFormat(query)); CloudRunnerOptions.pullInputList.includes(Input.ToEnvVarFormat(query));
return doesInclude ? true : false; return doesInclude ? true : false;
} else { } else {
@@ -44,19 +44,19 @@ class CloudRunnerQueryOverride {
} }
} }
private static async queryOverride(query) { private static async queryOverride(query: string) {
if (!this.shouldUseOverride(query)) { if (!this.shouldUseOverride(query)) {
throw new Error(`Should not be trying to run override query on ${query}`); throw new Error(`Should not be trying to run override query on ${query}`);
} }
return await GenericInputReader.Run( return await GenericInputReader.Run(
formatFunction(CloudRunnerOptions.readInputOverrideCommand(), [{ key: 0, value: query }]), formatFunction(CloudRunnerOptions.inputPullCommand, [{ key: 0, value: query }]),
); );
} }
public static async PopulateQueryOverrideInput() { public static async PopulateQueryOverrideInput() {
const queries = CloudRunnerOptions.readInputFromOverrideList().split(','); const queries = CloudRunnerOptions.pullInputList;
CloudRunnerQueryOverride.queryOverrides = new Array(); CloudRunnerQueryOverride.queryOverrides = {};
for (const element of queries) { for (const element of queries) {
if (CloudRunnerQueryOverride.shouldUseOverride(element)) { if (CloudRunnerQueryOverride.shouldUseOverride(element)) {
CloudRunnerQueryOverride.queryOverrides[element] = await CloudRunnerQueryOverride.queryOverride(element); CloudRunnerQueryOverride.queryOverrides[element] = await CloudRunnerQueryOverride.queryOverride(element);

View File

@@ -1,7 +1,7 @@
import CloudRunnerEnvironmentVariable from './services/cloud-runner-environment-variable'; import CloudRunnerEnvironmentVariable from './cloud-runner-environment-variable';
import CloudRunnerSecret from './services/cloud-runner-secret'; import CloudRunnerSecret from './cloud-runner-secret';
export class CloudRunnerStepState { export class CloudRunnerStepParameters {
public image: string; public image: string;
public environment: CloudRunnerEnvironmentVariable[]; public environment: CloudRunnerEnvironmentVariable[];
public secrets: CloudRunnerSecret[]; public secrets: CloudRunnerSecret[];

View File

@@ -1,8 +1,8 @@
import CloudRunnerLogger from '../../services/cloud-runner-logger'; import CloudRunnerLogger from '../../services/core/cloud-runner-logger';
import * as core from '@actions/core'; import * as core from '@actions/core';
import * as SDK from 'aws-sdk'; import * as SDK from 'aws-sdk';
import { BaseStackFormation } from './cloud-formations/base-stack-formation'; import { BaseStackFormation } from './cloud-formations/base-stack-formation';
const crypto = require('crypto'); import crypto from 'node:crypto';
export class AWSBaseStack { export class AWSBaseStack {
constructor(baseStackName: string) { constructor(baseStackName: string) {

View File

@@ -1,7 +1,7 @@
import { TaskDefinitionFormation } from './cloud-formations/task-definition-formation'; import { TaskDefinitionFormation } from './cloud-formations/task-definition-formation';
export class AWSCloudFormationTemplates { export class AWSCloudFormationTemplates {
public static getParameterTemplate(p1) { public static getParameterTemplate(p1: string) {
return ` return `
${p1}: ${p1}:
Type: String Type: String
@@ -9,7 +9,7 @@ export class AWSCloudFormationTemplates {
`; `;
} }
public static getSecretTemplate(p1) { public static getSecretTemplate(p1: string) {
return ` return `
${p1}Secret: ${p1}Secret:
Type: AWS::SecretsManager::Secret Type: AWS::SecretsManager::Secret
@@ -19,14 +19,14 @@ export class AWSCloudFormationTemplates {
`; `;
} }
public static getSecretDefinitionTemplate(p1, p2) { public static getSecretDefinitionTemplate(p1: string, p2: string) {
return ` return `
- Name: '${p1}' - Name: '${p1}'
ValueFrom: !Ref ${p2}Secret ValueFrom: !Ref ${p2}Secret
`; `;
} }
public static insertAtTemplate(template, insertionKey, insertion) { public static insertAtTemplate(template: string, insertionKey: string, insertion: string) {
const index = template.search(insertionKey) + insertionKey.length + '\n'.length; const index = template.search(insertionKey) + insertionKey.length + '\n'.length;
template = [template.slice(0, index), insertion, template.slice(index)].join(''); template = [template.slice(0, index), insertion, template.slice(index)].join('');

View File

@@ -1,4 +1,4 @@
import CloudRunnerLogger from '../../services/cloud-runner-logger'; import CloudRunnerLogger from '../../services/core/cloud-runner-logger';
import * as SDK from 'aws-sdk'; import * as SDK from 'aws-sdk';
import * as core from '@actions/core'; import * as core from '@actions/core';
import CloudRunner from '../../cloud-runner'; import CloudRunner from '../../cloud-runner';

View File

@@ -1,12 +1,12 @@
import * as SDK from 'aws-sdk'; import * as SDK from 'aws-sdk';
import CloudRunnerAWSTaskDef from './cloud-runner-aws-task-def'; import CloudRunnerAWSTaskDef from './cloud-runner-aws-task-def';
import CloudRunnerSecret from '../../services/cloud-runner-secret'; import CloudRunnerSecret from '../../options/cloud-runner-secret';
import { AWSCloudFormationTemplates } from './aws-cloud-formation-templates'; import { AWSCloudFormationTemplates } from './aws-cloud-formation-templates';
import CloudRunnerLogger from '../../services/cloud-runner-logger'; import CloudRunnerLogger from '../../services/core/cloud-runner-logger';
import { AWSError } from './aws-error'; import { AWSError } from './aws-error';
import CloudRunner from '../../cloud-runner'; import CloudRunner from '../../cloud-runner';
import { CleanupCronFormation } from './cloud-formations/cleanup-cron-formation'; import { CleanupCronFormation } from './cloud-formations/cleanup-cron-formation';
import CloudRunnerOptions from '../../cloud-runner-options'; import CloudRunnerOptions from '../../options/cloud-runner-options';
import { TaskDefinitionFormation } from './cloud-formations/task-definition-formation'; import { TaskDefinitionFormation } from './cloud-formations/task-definition-formation';
export class AWSJobStack { export class AWSJobStack {
@@ -27,21 +27,19 @@ export class AWSJobStack {
): Promise<CloudRunnerAWSTaskDef> { ): Promise<CloudRunnerAWSTaskDef> {
const taskDefStackName = `${this.baseStackName}-${buildGuid}`; const taskDefStackName = `${this.baseStackName}-${buildGuid}`;
let taskDefCloudFormation = AWSCloudFormationTemplates.readTaskCloudFormationTemplate(); let taskDefCloudFormation = AWSCloudFormationTemplates.readTaskCloudFormationTemplate();
const cpu = CloudRunner.buildParameters.cloudRunnerCpu || '1024';
const memory = CloudRunner.buildParameters.cloudRunnerMemory || '3072';
taskDefCloudFormation = taskDefCloudFormation.replace( taskDefCloudFormation = taskDefCloudFormation.replace(
`ContainerCpu: `ContainerCpu:
Default: 1024`, Default: 1024`,
`ContainerCpu: `ContainerCpu:
Default: ${Number.parseInt(cpu)}`, Default: ${Number.parseInt(CloudRunner.buildParameters.containerCpu)}`,
); );
taskDefCloudFormation = taskDefCloudFormation.replace( taskDefCloudFormation = taskDefCloudFormation.replace(
`ContainerMemory: `ContainerMemory:
Default: 2048`, Default: 2048`,
`ContainerMemory: `ContainerMemory:
Default: ${Number.parseInt(memory)}`, Default: ${Number.parseInt(CloudRunner.buildParameters.containerMemory)}`,
); );
if (CloudRunnerOptions.watchCloudRunnerToEnd) { if (!CloudRunnerOptions.asyncCloudRunner) {
taskDefCloudFormation = AWSCloudFormationTemplates.insertAtTemplate( taskDefCloudFormation = AWSCloudFormationTemplates.insertAtTemplate(
taskDefCloudFormation, taskDefCloudFormation,
'# template resources logstream', '# template resources logstream',
@@ -116,7 +114,7 @@ export class AWSJobStack {
...secretsMappedToCloudFormationParameters, ...secretsMappedToCloudFormationParameters,
]; ];
CloudRunnerLogger.log( CloudRunnerLogger.log(
`Starting AWS job with memory: ${CloudRunner.buildParameters.cloudRunnerMemory} cpu: ${CloudRunner.buildParameters.cloudRunnerCpu}`, `Starting AWS job with memory: ${CloudRunner.buildParameters.containerMemory} cpu: ${CloudRunner.buildParameters.containerCpu}`,
); );
let previousStackExists = true; let previousStackExists = true;
while (previousStackExists) { while (previousStackExists) {
@@ -140,11 +138,16 @@ export class AWSJobStack {
Capabilities: ['CAPABILITY_IAM'], Capabilities: ['CAPABILITY_IAM'],
Parameters: parameters, Parameters: parameters,
}; };
try { try {
CloudRunnerLogger.log(`Creating job aws formation ${taskDefStackName}`); CloudRunnerLogger.log(`Creating job aws formation ${taskDefStackName}`);
await CF.createStack(createStackInput).promise(); await CF.createStack(createStackInput).promise();
await CF.waitFor('stackCreateComplete', { StackName: taskDefStackName }).promise(); await CF.waitFor('stackCreateComplete', { StackName: taskDefStackName }).promise();
const describeStack = await CF.describeStacks({ StackName: taskDefStackName }).promise();
for (const parameter of parameters) {
if (!describeStack.Stacks?.[0].Parameters?.some((x) => x.ParameterKey === parameter.ParameterKey)) {
throw new Error(`Parameter ${parameter.ParameterKey} not found in stack`);
}
}
} catch (error) { } catch (error) {
await AWSError.handleStackCreationFailure(error, CF, taskDefStackName); await AWSError.handleStackCreationFailure(error, CF, taskDefStackName);
throw error; throw error;
@@ -180,7 +183,7 @@ export class AWSJobStack {
if (CloudRunnerOptions.useCleanupCron) { if (CloudRunnerOptions.useCleanupCron) {
try { try {
CloudRunnerLogger.log(`Creating job cleanup formation`); CloudRunnerLogger.log(`Creating job cleanup formation`);
CF.createStack(createCleanupStackInput).promise(); await CF.createStack(createCleanupStackInput).promise();
// await CF.waitFor('stackCreateComplete', { StackName: createCleanupStackInput.StackName }).promise(); // await CF.waitFor('stackCreateComplete', { StackName: createCleanupStackInput.StackName }).promise();
} catch (error) { } catch (error) {

View File

@@ -1,14 +1,14 @@
import * as AWS from 'aws-sdk'; import * as AWS from 'aws-sdk';
import CloudRunnerEnvironmentVariable from '../../services/cloud-runner-environment-variable'; import CloudRunnerEnvironmentVariable from '../../options/cloud-runner-environment-variable';
import * as core from '@actions/core'; import * as core from '@actions/core';
import CloudRunnerAWSTaskDef from './cloud-runner-aws-task-def'; import CloudRunnerAWSTaskDef from './cloud-runner-aws-task-def';
import * as zlib from 'zlib'; import * as zlib from 'node:zlib';
import CloudRunnerLogger from '../../services/cloud-runner-logger'; import CloudRunnerLogger from '../../services/core/cloud-runner-logger';
import { Input } from '../../..'; import { Input } from '../../..';
import CloudRunner from '../../cloud-runner'; import CloudRunner from '../../cloud-runner';
import { CloudRunnerCustomHooks } from '../../services/cloud-runner-custom-hooks'; import { CommandHookService } from '../../services/hooks/command-hook-service';
import { FollowLogStreamService } from '../../services/follow-log-stream-service'; import { FollowLogStreamService } from '../../services/core/follow-log-stream-service';
import CloudRunnerOptions from '../../cloud-runner-options'; import CloudRunnerOptions from '../../options/cloud-runner-options';
import GitHub from '../../../github'; import GitHub from '../../../github';
class AWSTaskRunner { class AWSTaskRunner {
@@ -32,7 +32,7 @@ class AWSTaskRunner {
const streamName = const streamName =
taskDef.taskDefResources?.find((x) => x.LogicalResourceId === 'KinesisStream')?.PhysicalResourceId || ''; taskDef.taskDefResources?.find((x) => x.LogicalResourceId === 'KinesisStream')?.PhysicalResourceId || '';
const task = await AWSTaskRunner.ECS.runTask({ const runParameters = {
cluster, cluster,
taskDefinition, taskDefinition,
platformVersion: '1.4.0', platformVersion: '1.4.0',
@@ -41,7 +41,7 @@ class AWSTaskRunner {
{ {
name: taskDef.taskDefStackName, name: taskDef.taskDefStackName,
environment, environment,
command: ['-c', CloudRunnerCustomHooks.ApplyHooksToCommands(commands, CloudRunner.buildParameters)], command: ['-c', CommandHookService.ApplyHooksToCommands(commands, CloudRunner.buildParameters)],
}, },
], ],
}, },
@@ -53,16 +53,23 @@ class AWSTaskRunner {
securityGroups: [ContainerSecurityGroup], securityGroups: [ContainerSecurityGroup],
}, },
}, },
}).promise(); };
if (JSON.stringify(runParameters.overrides.containerOverrides).length > 8192) {
CloudRunnerLogger.log(JSON.stringify(runParameters.overrides.containerOverrides, undefined, 4));
throw new Error(`Container Overrides length must be at most 8192`);
}
const task = await AWSTaskRunner.ECS.runTask(runParameters).promise();
const taskArn = task.tasks?.[0].taskArn || ''; const taskArn = task.tasks?.[0].taskArn || '';
CloudRunnerLogger.log('Cloud runner job is starting'); CloudRunnerLogger.log('Cloud runner job is starting');
await AWSTaskRunner.waitUntilTaskRunning(taskArn, cluster); await AWSTaskRunner.waitUntilTaskRunning(taskArn, cluster);
CloudRunnerLogger.log( CloudRunnerLogger.log(
`Cloud runner job status is running ${(await AWSTaskRunner.describeTasks(cluster, taskArn))?.lastStatus} Watch:${ `Cloud runner job status is running ${(await AWSTaskRunner.describeTasks(cluster, taskArn))?.lastStatus} Async:${
CloudRunnerOptions.watchCloudRunnerToEnd CloudRunnerOptions.asyncCloudRunner
} Async:${CloudRunnerOptions.asyncCloudRunner}`, }`,
); );
if (!CloudRunnerOptions.watchCloudRunnerToEnd) { if (CloudRunnerOptions.asyncCloudRunner) {
const shouldCleanup: boolean = false; const shouldCleanup: boolean = false;
const output: string = ''; const output: string = '';
CloudRunnerLogger.log(`Watch Cloud Runner To End: false`); CloudRunnerLogger.log(`Watch Cloud Runner To End: false`);
@@ -72,26 +79,31 @@ class AWSTaskRunner {
CloudRunnerLogger.log(`Streaming...`); CloudRunnerLogger.log(`Streaming...`);
const { output, shouldCleanup } = await this.streamLogsUntilTaskStops(cluster, taskArn, streamName); const { output, shouldCleanup } = await this.streamLogsUntilTaskStops(cluster, taskArn, streamName);
await new Promise((resolve) => resolve(5000)); let exitCode;
const taskData = await AWSTaskRunner.describeTasks(cluster, taskArn); let containerState;
const containerState = taskData.containers?.[0]; let taskData;
const exitCode = containerState?.exitCode || undefined; while (exitCode === undefined) {
await new Promise((resolve) => resolve(10000));
taskData = await AWSTaskRunner.describeTasks(cluster, taskArn);
containerState = taskData.containers?.[0];
exitCode = containerState?.exitCode;
}
CloudRunnerLogger.log(`Container State: ${JSON.stringify(containerState, undefined, 4)}`); CloudRunnerLogger.log(`Container State: ${JSON.stringify(containerState, undefined, 4)}`);
const wasSuccessful = exitCode === 0 || (exitCode === undefined && taskData.lastStatus === 'RUNNING'); if (exitCode === undefined) {
CloudRunnerLogger.logWarning(`Undefined exitcode for container`);
}
const wasSuccessful = exitCode === 0;
if (wasSuccessful) { if (wasSuccessful) {
CloudRunnerLogger.log(`Cloud runner job has finished successfully`); CloudRunnerLogger.log(`Cloud runner job has finished successfully`);
return { output, shouldCleanup }; return { output, shouldCleanup };
} else {
if (taskData.stoppedReason === 'Essential container in task exited' && exitCode === 1) {
throw new Error('Container exited with code 1');
}
const message = `Cloud runner job exit code ${exitCode}`;
taskData.overrides = undefined;
taskData.attachments = undefined;
CloudRunnerLogger.log(`${message} ${JSON.stringify(taskData, undefined, 4)}`);
throw new Error(message);
} }
if (taskData?.stoppedReason === 'Essential container in task exited' && exitCode === 1) {
throw new Error('Container exited with code 1');
}
throw new Error(`Task failed`);
} }
private static async waitUntilTaskRunning(taskArn: string, cluster: string) { private static async waitUntilTaskRunning(taskArn: string, cluster: string) {
@@ -129,7 +141,7 @@ class AWSTaskRunner {
const stream = await AWSTaskRunner.getLogStream(kinesisStreamName); const stream = await AWSTaskRunner.getLogStream(kinesisStreamName);
let iterator = await AWSTaskRunner.getLogIterator(stream); let iterator = await AWSTaskRunner.getLogIterator(stream);
const logBaseUrl = `https://${Input.region}.console.aws.amazon.com/cloudwatch/home?region=${Input.region}#logsV2:log-groups/log-group/${CloudRunner.buildParameters.awsBaseStackName}${AWSTaskRunner.encodedUnderscore}${CloudRunner.buildParameters.awsBaseStackName}-${CloudRunner.buildParameters.buildGuid}`; const logBaseUrl = `https://${Input.region}.console.aws.amazon.com/cloudwatch/home?region=${Input.region}#logsV2:log-groups/log-group/${CloudRunner.buildParameters.awsStackName}${AWSTaskRunner.encodedUnderscore}${CloudRunner.buildParameters.awsStackName}-${CloudRunner.buildParameters.buildGuid}`;
CloudRunnerLogger.log(`You view the log stream on AWS Cloud Watch: ${logBaseUrl}`); CloudRunnerLogger.log(`You view the log stream on AWS Cloud Watch: ${logBaseUrl}`);
await GitHub.updateGitHubCheck(`You view the log stream on AWS Cloud Watch: ${logBaseUrl}`, ``); await GitHub.updateGitHubCheck(`You view the log stream on AWS Cloud Watch: ${logBaseUrl}`, ``);
let shouldReadLogs = true; let shouldReadLogs = true;
@@ -192,22 +204,19 @@ class AWSTaskRunner {
} }
private static logRecords( private static logRecords(
records, records: AWS.Kinesis.GetRecordsOutput,
iterator: string, iterator: string,
shouldReadLogs: boolean, shouldReadLogs: boolean,
output: string, output: string,
shouldCleanup: boolean, shouldCleanup: boolean,
) { ) {
if (records.Records.length > 0 && iterator) { if (records.Records.length > 0 && iterator) {
for (let index = 0; index < records.Records.length; index++) { for (const record of records.Records) {
const json = JSON.parse( const json = JSON.parse(zlib.gunzipSync(Buffer.from(record.Data as string, 'base64')).toString('utf8'));
zlib.gunzipSync(Buffer.from(records.Records[index].Data as string, 'base64')).toString('utf8'),
);
if (json.messageType === 'DATA_MESSAGE') { if (json.messageType === 'DATA_MESSAGE') {
for (let logEventsIndex = 0; logEventsIndex < json.logEvents.length; logEventsIndex++) { for (const logEvent of json.logEvents) {
const message = json.logEvents[logEventsIndex].message;
({ shouldReadLogs, shouldCleanup, output } = FollowLogStreamService.handleIteration( ({ shouldReadLogs, shouldCleanup, output } = FollowLogStreamService.handleIteration(
message, logEvent.message,
shouldReadLogs, shouldReadLogs,
shouldCleanup, shouldCleanup,
output, output,
@@ -226,7 +235,7 @@ class AWSTaskRunner {
}).promise(); }).promise();
} }
private static async getLogIterator(stream) { private static async getLogIterator(stream: AWS.Kinesis.DescribeStreamOutput) {
return ( return (
( (
await AWSTaskRunner.Kinesis.getShardIterator({ await AWSTaskRunner.Kinesis.getShardIterator({

View File

@@ -30,7 +30,7 @@ Parameters:
Type: Number Type: Number
Description: How much CPU to give the container. 1024 is 1 CPU Description: How much CPU to give the container. 1024 is 1 CPU
ContainerMemory: ContainerMemory:
Default: 2048 Default: 4096
Type: Number Type: Number
Description: How much memory in megabytes to give the container Description: How much memory in megabytes to give the container
BUILDGUID: BUILDGUID:

View File

@@ -1,11 +1,11 @@
import * as SDK from 'aws-sdk'; import * as SDK from 'aws-sdk';
import CloudRunnerSecret from '../../services/cloud-runner-secret'; import CloudRunnerSecret from '../../options/cloud-runner-secret';
import CloudRunnerEnvironmentVariable from '../../services/cloud-runner-environment-variable'; import CloudRunnerEnvironmentVariable from '../../options/cloud-runner-environment-variable';
import CloudRunnerAWSTaskDef from './cloud-runner-aws-task-def'; import CloudRunnerAWSTaskDef from './cloud-runner-aws-task-def';
import AwsTaskRunner from './aws-task-runner'; import AwsTaskRunner from './aws-task-runner';
import { ProviderInterface } from '../provider-interface'; import { ProviderInterface } from '../provider-interface';
import BuildParameters from '../../../build-parameters'; import BuildParameters from '../../../build-parameters';
import CloudRunnerLogger from '../../services/cloud-runner-logger'; import CloudRunnerLogger from '../../services/core/cloud-runner-logger';
import { AWSJobStack as AwsJobStack } from './aws-job-stack'; import { AWSJobStack as AwsJobStack } from './aws-job-stack';
import { AWSBaseStack as AwsBaseStack } from './aws-base-stack'; import { AWSBaseStack as AwsBaseStack } from './aws-base-stack';
import { Input } from '../../..'; import { Input } from '../../..';
@@ -13,13 +13,13 @@ import { GarbageCollectionService } from './services/garbage-collection-service'
import { ProviderResource } from '../provider-resource'; import { ProviderResource } from '../provider-resource';
import { ProviderWorkflow } from '../provider-workflow'; import { ProviderWorkflow } from '../provider-workflow';
import { TaskService } from './services/task-service'; import { TaskService } from './services/task-service';
import CloudRunnerOptions from '../../cloud-runner-options'; import CloudRunnerOptions from '../../options/cloud-runner-options';
class AWSBuildEnvironment implements ProviderInterface { class AWSBuildEnvironment implements ProviderInterface {
private baseStackName: string; private baseStackName: string;
constructor(buildParameters: BuildParameters) { constructor(buildParameters: BuildParameters) {
this.baseStackName = buildParameters.awsBaseStackName; this.baseStackName = buildParameters.awsStackName;
} }
async listResources(): Promise<ProviderResource[]> { async listResources(): Promise<ProviderResource[]> {
await TaskService.getCloudFormationJobStacks(); await TaskService.getCloudFormationJobStacks();
@@ -75,7 +75,11 @@ class AWSBuildEnvironment implements ProviderInterface {
branchName: string, branchName: string,
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
defaultSecretsArray: { ParameterKey: string; EnvironmentVariable: string; ParameterValue: string }[], defaultSecretsArray: { ParameterKey: string; EnvironmentVariable: string; ParameterValue: string }[],
) {} ) {
process.env.AWS_REGION = Input.region;
const CF = new SDK.CloudFormation();
await new AwsBaseStack(this.baseStackName).setupBaseStack(CF);
}
async runTaskInWorkflow( async runTaskInWorkflow(
buildGuid: string, buildGuid: string,
@@ -94,8 +98,6 @@ class AWSBuildEnvironment implements ProviderInterface {
CloudRunnerLogger.log(`AWS Region: ${CF.config.region}`); CloudRunnerLogger.log(`AWS Region: ${CF.config.region}`);
const entrypoint = ['/bin/sh']; const entrypoint = ['/bin/sh'];
const startTimeMs = Date.now(); const startTimeMs = Date.now();
await new AwsBaseStack(this.baseStackName).setupBaseStack(CF);
const taskDef = await new AwsJobStack(this.baseStackName).setupCloudFormations( const taskDef = await new AwsJobStack(this.baseStackName).setupCloudFormations(
CF, CF,
buildGuid, buildGuid,
@@ -143,6 +145,9 @@ class AWSBuildEnvironment implements ProviderInterface {
await CF.waitFor('stackDeleteComplete', { await CF.waitFor('stackDeleteComplete', {
StackName: taskDef.taskDefStackName, StackName: taskDef.taskDefStackName,
}).promise(); }).promise();
await CF.waitFor('stackDeleteComplete', {
StackName: `${taskDef.taskDefStackName}-cleanup`,
}).promise();
CloudRunnerLogger.log(`Deleted Stack: ${taskDef.taskDefStackName}`); CloudRunnerLogger.log(`Deleted Stack: ${taskDef.taskDefStackName}`);
CloudRunnerLogger.log('Cleanup complete'); CloudRunnerLogger.log('Cleanup complete');
} }

View File

@@ -1,10 +1,10 @@
import AWS from 'aws-sdk'; import AWS from 'aws-sdk';
import Input from '../../../../input'; import Input from '../../../../input';
import CloudRunnerLogger from '../../../services/cloud-runner-logger'; import CloudRunnerLogger from '../../../services/core/cloud-runner-logger';
import { TaskService } from './task-service'; import { TaskService } from './task-service';
export class GarbageCollectionService { export class GarbageCollectionService {
static isOlderThan1day(date: any) { static isOlderThan1day(date: Date) {
const ageDate = new Date(date.getTime() - Date.now()); const ageDate = new Date(date.getTime() - Date.now());
return ageDate.getDay() > 0; return ageDate.getDay() > 0;
@@ -17,14 +17,16 @@ export class GarbageCollectionService {
const cwl = new AWS.CloudWatchLogs(); const cwl = new AWS.CloudWatchLogs();
const taskDefinitionsInUse = new Array(); const taskDefinitionsInUse = new Array();
const tasks = await TaskService.getTasks(); const tasks = await TaskService.getTasks();
for (const task of tasks) { for (const task of tasks) {
const { taskElement, element } = task; const { taskElement, element } = task;
taskDefinitionsInUse.push(taskElement.taskDefinitionArn); taskDefinitionsInUse.push(taskElement.taskDefinitionArn);
if (deleteResources && (!OneDayOlderOnly || GarbageCollectionService.isOlderThan1day(taskElement.CreatedAt))) { if (deleteResources && (!OneDayOlderOnly || GarbageCollectionService.isOlderThan1day(taskElement.createdAt!))) {
CloudRunnerLogger.log(`Stopping task ${taskElement.containers?.[0].name}`); CloudRunnerLogger.log(`Stopping task ${taskElement.containers?.[0].name}`);
await ecs.stopTask({ task: taskElement.taskArn || '', cluster: element }).promise(); await ecs.stopTask({ task: taskElement.taskArn || '', cluster: element }).promise();
} }
} }
const jobStacks = await TaskService.getCloudFormationJobStacks(); const jobStacks = await TaskService.getCloudFormationJobStacks();
for (const element of jobStacks) { for (const element of jobStacks) {
if ( if (
@@ -36,13 +38,15 @@ export class GarbageCollectionService {
return; return;
} }
if (deleteResources && (!OneDayOlderOnly || GarbageCollectionService.isOlderThan1day(element.CreationTime))) { if (deleteResources && (!OneDayOlderOnly || GarbageCollectionService.isOlderThan1day(element.CreationTime))) {
if (element.StackName === 'game-ci' || element.TemplateDescription === 'Game-CI base stack') { if (element.StackName === 'game-ci' || element.TemplateDescription === 'Game-CI base stack') {
CloudRunnerLogger.log(`Skipping ${element.StackName} ignore list`); CloudRunnerLogger.log(`Skipping ${element.StackName} ignore list`);
return; return;
} }
CloudRunnerLogger.log(`Deleting ${element.logGroupName}`);
CloudRunnerLogger.log(`Deleting ${element.StackName}`);
const deleteStackInput: AWS.CloudFormation.DeleteStackInput = { StackName: element.StackName }; const deleteStackInput: AWS.CloudFormation.DeleteStackInput = { StackName: element.StackName };
await CF.deleteStack(deleteStackInput).promise(); await CF.deleteStack(deleteStackInput).promise();
} }
@@ -51,7 +55,7 @@ export class GarbageCollectionService {
for (const element of logGroups) { for (const element of logGroups) {
if ( if (
deleteResources && deleteResources &&
(!OneDayOlderOnly || GarbageCollectionService.isOlderThan1day(new Date(element.createdAt))) (!OneDayOlderOnly || GarbageCollectionService.isOlderThan1day(new Date(element.creationTime!)))
) { ) {
CloudRunnerLogger.log(`Deleting ${element.logGroupName}`); CloudRunnerLogger.log(`Deleting ${element.logGroupName}`);
await cwl.deleteLogGroup({ logGroupName: element.logGroupName || '' }).promise(); await cwl.deleteLogGroup({ logGroupName: element.logGroupName || '' }).promise();

View File

@@ -1,10 +1,12 @@
import AWS from 'aws-sdk'; import AWS from 'aws-sdk';
import Input from '../../../../input'; import Input from '../../../../input';
import CloudRunnerLogger from '../../../services/cloud-runner-logger'; import CloudRunnerLogger from '../../../services/core/cloud-runner-logger';
import { BaseStackFormation } from '../cloud-formations/base-stack-formation'; import { BaseStackFormation } from '../cloud-formations/base-stack-formation';
import AwsTaskRunner from '../aws-task-runner'; import AwsTaskRunner from '../aws-task-runner';
import { ListObjectsRequest } from 'aws-sdk/clients/s3'; import { ListObjectsRequest } from 'aws-sdk/clients/s3';
import CloudRunner from '../../../cloud-runner'; import CloudRunner from '../../../cloud-runner';
import { StackSummaries } from 'aws-sdk/clients/cloudformation';
import { LogGroups } from 'aws-sdk/clients/cloudwatchlogs';
export class TaskService { export class TaskService {
static async watch() { static async watch() {
@@ -18,7 +20,7 @@ export class TaskService {
return output; return output;
} }
public static async getCloudFormationJobStacks() { public static async getCloudFormationJobStacks() {
const result: any[] = []; const result: StackSummaries = [];
CloudRunnerLogger.log(``); CloudRunnerLogger.log(``);
CloudRunnerLogger.log(`List Cloud Formation Stacks`); CloudRunnerLogger.log(`List Cloud Formation Stacks`);
process.env.AWS_REGION = Input.region; process.env.AWS_REGION = Input.region;
@@ -62,7 +64,7 @@ export class TaskService {
return result; return result;
} }
public static async getTasks() { public static async getTasks() {
const result: any[] = []; const result: { taskElement: AWS.ECS.Task; element: string }[] = [];
CloudRunnerLogger.log(``); CloudRunnerLogger.log(``);
CloudRunnerLogger.log(`List Tasks`); CloudRunnerLogger.log(`List Tasks`);
process.env.AWS_REGION = Input.region; process.env.AWS_REGION = Input.region;
@@ -123,7 +125,7 @@ export class TaskService {
return message; return message;
} }
public static async getLogGroups() { public static async getLogGroups() {
const result: any[] = []; const result: LogGroups = [];
process.env.AWS_REGION = Input.region; process.env.AWS_REGION = Input.region;
const ecs = new AWS.CloudWatchLogs(); const ecs = new AWS.CloudWatchLogs();
let logStreamInput: AWS.CloudWatchLogs.DescribeLogGroupsRequest = { let logStreamInput: AWS.CloudWatchLogs.DescribeLogGroupsRequest = {
@@ -159,7 +161,7 @@ export class TaskService {
process.env.AWS_REGION = Input.region; process.env.AWS_REGION = Input.region;
const s3 = new AWS.S3(); const s3 = new AWS.S3();
const listRequest: ListObjectsRequest = { const listRequest: ListObjectsRequest = {
Bucket: CloudRunner.buildParameters.awsBaseStackName, Bucket: CloudRunner.buildParameters.awsStackName,
}; };
const results = await s3.listObjects(listRequest).promise(); const results = await s3.listObjects(listRequest).promise();

View File

@@ -1,19 +1,21 @@
import BuildParameters from '../../../build-parameters'; import BuildParameters from '../../../build-parameters';
import CloudRunnerEnvironmentVariable from '../../services/cloud-runner-environment-variable'; import CloudRunnerEnvironmentVariable from '../../options/cloud-runner-environment-variable';
import CloudRunnerLogger from '../../services/cloud-runner-logger'; import CloudRunnerLogger from '../../services/core/cloud-runner-logger';
import { ProviderInterface } from '../provider-interface'; import { ProviderInterface } from '../provider-interface';
import CloudRunnerSecret from '../../services/cloud-runner-secret'; import CloudRunnerSecret from '../../options/cloud-runner-secret';
import Docker from '../../../docker'; import Docker from '../../../docker';
import { Action } from '../../..'; import { Action } from '../../..';
import { writeFileSync } from 'fs'; import { writeFileSync } from 'node:fs';
import CloudRunner from '../../cloud-runner'; import CloudRunner from '../../cloud-runner';
import { ProviderResource } from '../provider-resource'; import { ProviderResource } from '../provider-resource';
import { ProviderWorkflow } from '../provider-workflow'; import { ProviderWorkflow } from '../provider-workflow';
import { CloudRunnerSystem } from '../../services/cloud-runner-system'; import { CloudRunnerSystem } from '../../services/core/cloud-runner-system';
import * as fs from 'fs'; import * as fs from 'node:fs';
import { CommandHookService } from '../../services/hooks/command-hook-service';
import { StringKeyValuePair } from '../../../shared-types';
class LocalDockerCloudRunner implements ProviderInterface { class LocalDockerCloudRunner implements ProviderInterface {
public buildParameters: BuildParameters | undefined; public buildParameters!: BuildParameters;
listResources(): Promise<ProviderResource[]> { listResources(): Promise<ProviderResource[]> {
return new Promise((resolve) => resolve([])); return new Promise((resolve) => resolve([]));
@@ -50,14 +52,14 @@ class LocalDockerCloudRunner implements ProviderInterface {
if ( if (
fs.existsSync( fs.existsSync(
`${workspace}/cloud-runner-cache/cache/build/build-${buildParameters.buildGuid}.tar${ `${workspace}/cloud-runner-cache/cache/build/build-${buildParameters.buildGuid}.tar${
CloudRunner.buildParameters.useLz4Compression ? '.lz4' : '' CloudRunner.buildParameters.useCompressionStrategy ? '.lz4' : ''
}`, }`,
) )
) { ) {
await CloudRunnerSystem.Run(`ls ${workspace}/cloud-runner-cache/cache/build/`); await CloudRunnerSystem.Run(`ls ${workspace}/cloud-runner-cache/cache/build/`);
await CloudRunnerSystem.Run( await CloudRunnerSystem.Run(
`rm -r ${workspace}/cloud-runner-cache/cache/build/build-${buildParameters.buildGuid}.tar${ `rm -r ${workspace}/cloud-runner-cache/cache/build/build-${buildParameters.buildGuid}.tar${
CloudRunner.buildParameters.useLz4Compression ? '.lz4' : '' CloudRunner.buildParameters.useCompressionStrategy ? '.lz4' : ''
}`, }`,
); );
} }
@@ -86,7 +88,7 @@ class LocalDockerCloudRunner implements ProviderInterface {
CloudRunnerLogger.log(commands); CloudRunnerLogger.log(commands);
const { workspace, actionFolder } = Action; const { workspace, actionFolder } = Action;
const content: any[] = []; const content: StringKeyValuePair[] = [];
for (const x of secrets) { for (const x of secrets) {
content.push({ name: x.EnvironmentVariable, value: x.ParameterValue }); content.push({ name: x.EnvironmentVariable, value: x.ParameterValue });
} }
@@ -117,7 +119,7 @@ set -e
mkdir -p /github/workspace/cloud-runner-cache mkdir -p /github/workspace/cloud-runner-cache
mkdir -p /data/cache mkdir -p /data/cache
cp -a /github/workspace/cloud-runner-cache/. ${sharedFolder} cp -a /github/workspace/cloud-runner-cache/. ${sharedFolder}
${commands} ${CommandHookService.ApplyHooksToCommands(commands, this.buildParameters)}
cp -a ${sharedFolder}. /github/workspace/cloud-runner-cache/ cp -a ${sharedFolder}. /github/workspace/cloud-runner-cache/
`; `;
writeFileSync(`${workspace}/${entrypointFilePath}`, fileContents, { writeFileSync(`${workspace}/${entrypointFilePath}`, fileContents, {
@@ -131,7 +133,7 @@ cp -a ${sharedFolder}. /github/workspace/cloud-runner-cache/
if (fs.existsSync(`${workspace}/cloud-runner-cache`)) { if (fs.existsSync(`${workspace}/cloud-runner-cache`)) {
await CloudRunnerSystem.Run(`ls ${workspace}/cloud-runner-cache && du -sh ${workspace}/cloud-runner-cache`); await CloudRunnerSystem.Run(`ls ${workspace}/cloud-runner-cache && du -sh ${workspace}/cloud-runner-cache`);
} }
await Docker.run( const exitCode = await Docker.run(
image, image,
{ workspace, actionFolder, ...this.buildParameters }, { workspace, actionFolder, ...this.buildParameters },
false, false,
@@ -150,6 +152,12 @@ cp -a ${sharedFolder}. /github/workspace/cloud-runner-cache/
true, true,
); );
// Docker doesn't exit on fail now so adding this to ensure behavior is unchanged
// TODO: Is there a helpful way to consume the exit code or is it best to except
if (exitCode !== 0) {
throw new Error(`Build failed with exit code ${exitCode}`);
}
return myOutput; return myOutput;
} }
} }

View File

@@ -2,19 +2,18 @@ import * as k8s from '@kubernetes/client-node';
import { BuildParameters } from '../../..'; import { BuildParameters } from '../../..';
import * as core from '@actions/core'; import * as core from '@actions/core';
import { ProviderInterface } from '../provider-interface'; import { ProviderInterface } from '../provider-interface';
import CloudRunnerSecret from '../../services/cloud-runner-secret'; import CloudRunnerSecret from '../../options/cloud-runner-secret';
import KubernetesStorage from './kubernetes-storage'; import KubernetesStorage from './kubernetes-storage';
import CloudRunnerEnvironmentVariable from '../../services/cloud-runner-environment-variable'; import CloudRunnerEnvironmentVariable from '../../options/cloud-runner-environment-variable';
import KubernetesTaskRunner from './kubernetes-task-runner'; import KubernetesTaskRunner from './kubernetes-task-runner';
import KubernetesSecret from './kubernetes-secret'; import KubernetesSecret from './kubernetes-secret';
import KubernetesJobSpecFactory from './kubernetes-job-spec-factory'; import KubernetesJobSpecFactory from './kubernetes-job-spec-factory';
import KubernetesServiceAccount from './kubernetes-service-account'; import KubernetesServiceAccount from './kubernetes-service-account';
import CloudRunnerLogger from '../../services/cloud-runner-logger'; import CloudRunnerLogger from '../../services/core/cloud-runner-logger';
import { CoreV1Api } from '@kubernetes/client-node'; import { CoreV1Api } from '@kubernetes/client-node';
import CloudRunner from '../../cloud-runner'; import CloudRunner from '../../cloud-runner';
import { ProviderResource } from '../provider-resource'; import { ProviderResource } from '../provider-resource';
import { ProviderWorkflow } from '../provider-workflow'; import { ProviderWorkflow } from '../provider-workflow';
import KubernetesPods from './kubernetes-pods';
class Kubernetes implements ProviderInterface { class Kubernetes implements ProviderInterface {
public static Instance: Kubernetes; public static Instance: Kubernetes;
@@ -94,16 +93,8 @@ class Kubernetes implements ProviderInterface {
) { ) {
try { try {
this.buildParameters = buildParameters; this.buildParameters = buildParameters;
const id = buildParameters.retainWorkspace ? CloudRunner.lockedWorkspace : buildParameters.buildGuid; this.cleanupCronJobName = `unity-builder-cronjob-${buildParameters.buildGuid}`;
this.pvcName = `unity-builder-pvc-${id}`;
this.cleanupCronJobName = `unity-builder-cronjob-${id}`;
this.serviceAccountName = `service-account-${buildParameters.buildGuid}`; this.serviceAccountName = `service-account-${buildParameters.buildGuid}`;
await KubernetesStorage.createPersistentVolumeClaim(
buildParameters,
this.pvcName,
this.kubeClient,
this.namespace,
);
await KubernetesServiceAccount.createServiceAccount(this.serviceAccountName, this.namespace, this.kubeClient); await KubernetesServiceAccount.createServiceAccount(this.serviceAccountName, this.namespace, this.kubeClient);
} catch (error) { } catch (error) {
@@ -124,74 +115,99 @@ class Kubernetes implements ProviderInterface {
CloudRunnerLogger.log('Cloud Runner K8s workflow!'); CloudRunnerLogger.log('Cloud Runner K8s workflow!');
// Setup // Setup
const id = BuildParameters.shouldUseRetainedWorkspaceMode(this.buildParameters)
? CloudRunner.lockedWorkspace
: this.buildParameters.buildGuid;
this.pvcName = `unity-builder-pvc-${id}`;
await KubernetesStorage.createPersistentVolumeClaim(
this.buildParameters,
this.pvcName,
this.kubeClient,
this.namespace,
);
this.buildGuid = buildGuid; this.buildGuid = buildGuid;
this.secretName = `build-credentials-${this.buildGuid}`; this.secretName = `build-credentials-${this.buildGuid}`;
this.jobName = `unity-builder-job-${this.buildGuid}`; this.jobName = `unity-builder-job-${this.buildGuid}`;
this.containerName = `main`; this.containerName = `main`;
await KubernetesSecret.createSecret(secrets, this.secretName, this.namespace, this.kubeClient); await KubernetesSecret.createSecret(secrets, this.secretName, this.namespace, this.kubeClient);
await this.createNamespacedJob(commands, image, mountdir, workingdir, environment, secrets);
this.setPodNameAndContainerName(await Kubernetes.findPodFromJob(this.kubeClient, this.jobName, this.namespace));
CloudRunnerLogger.log('Watching pod until running');
await KubernetesTaskRunner.watchUntilPodRunning(this.kubeClient, this.podName, this.namespace);
let output = ''; let output = '';
// eslint-disable-next-line no-constant-condition try {
while (true) { CloudRunnerLogger.log('Job does not exist');
try { await this.createJob(commands, image, mountdir, workingdir, environment, secrets);
CloudRunnerLogger.log('Pod running, streaming logs'); CloudRunnerLogger.log('Watching pod until running');
output = await KubernetesTaskRunner.runTask( await KubernetesTaskRunner.watchUntilPodRunning(this.kubeClient, this.podName, this.namespace);
this.kubeConfig,
this.kubeClient,
this.jobName,
this.podName,
'main',
this.namespace,
);
const running = await KubernetesPods.IsPodRunning(this.podName, this.namespace, this.kubeClient);
if (!running) { CloudRunnerLogger.log('Pod running, streaming logs');
CloudRunnerLogger.log(`Pod not found, assumed ended!`); CloudRunnerLogger.log(
break; `Starting logs follow for pod: ${this.podName} container: ${this.containerName} namespace: ${this.namespace} pvc: ${this.pvcName} ${CloudRunner.buildParameters.kubeVolumeSize}/${CloudRunner.buildParameters.containerCpu}/${CloudRunner.buildParameters.containerMemory}`,
} else { );
CloudRunnerLogger.log('Pod still running, recovering stream...'); output += await KubernetesTaskRunner.runTask(
} this.kubeConfig,
await this.cleanupTaskResources(); this.kubeClient,
} catch (error: any) { this.jobName,
let errorParsed; this.podName,
try { this.containerName,
errorParsed = JSON.parse(error); this.namespace,
} catch { );
errorParsed = error; } catch (error: any) {
} CloudRunnerLogger.log(`error running k8s workflow ${error}`);
await new Promise((resolve) => setTimeout(resolve, 3000));
const reason = errorParsed.reason || errorParsed.response?.body?.reason || ``; CloudRunnerLogger.log(
const errorMessage = errorParsed.message || reason; JSON.stringify(
(await this.kubeClient.listNamespacedEvent(this.namespace)).body.items
const continueStreaming = .map((x) => {
errorMessage.includes(`dial timeout, backstop`) || return {
errorMessage.includes(`HttpError: HTTP request failed`) || message: x.message || ``,
errorMessage.includes(`an error occurred when try to find container`) || name: x.metadata.name || ``,
errorMessage.includes(`not found`) || reason: x.reason || ``,
errorMessage.includes(`Not Found`); };
if (continueStreaming) { })
CloudRunnerLogger.log('Log Stream Container Not Found'); .filter((x) => x.name.includes(this.podName)),
await new Promise((resolve) => resolve(5000)); undefined,
continue; 4,
} else { ),
CloudRunnerLogger.log(`error running k8s workflow ${error}`); );
throw error; await this.cleanupTaskResources();
} throw error;
}
} }
await this.cleanupTaskResources();
return output; return output;
} catch (error) { } catch (error) {
CloudRunnerLogger.log('Running job failed'); CloudRunnerLogger.log('Running job failed');
core.error(JSON.stringify(error, undefined, 4)); core.error(JSON.stringify(error, undefined, 4));
await this.cleanupTaskResources();
// await this.cleanupTaskResources();
throw error; throw error;
} }
} }
private async createJob(
commands: string,
image: string,
mountdir: string,
workingdir: string,
environment: CloudRunnerEnvironmentVariable[],
secrets: CloudRunnerSecret[],
) {
await this.createNamespacedJob(commands, image, mountdir, workingdir, environment, secrets);
const find = await Kubernetes.findPodFromJob(this.kubeClient, this.jobName, this.namespace);
this.setPodNameAndContainerName(find);
}
private async doesJobExist(name: string) {
const jobs = await this.kubeClientBatch.listNamespacedJob(this.namespace);
return jobs.body.items.some((x) => x.metadata?.name === name);
}
private async doesFailedJobExist() {
const podStatus = await this.kubeClient.readNamespacedPodStatus(this.podName, this.namespace);
return podStatus.body.status?.phase === `Failed`;
}
private async createNamespacedJob( private async createNamespacedJob(
commands: string, commands: string,
image: string, image: string,
@@ -215,14 +231,15 @@ class Kubernetes implements ProviderInterface {
this.pvcName, this.pvcName,
this.jobName, this.jobName,
k8s, k8s,
this.containerName,
); );
await new Promise((promise) => setTimeout(promise, 15000)); await new Promise((promise) => setTimeout(promise, 15000));
await this.kubeClientBatch.createNamespacedJob(this.namespace, jobSpec); const result = await this.kubeClientBatch.createNamespacedJob(this.namespace, jobSpec);
CloudRunnerLogger.log(`Build job created`); CloudRunnerLogger.log(`Build job created`);
await new Promise((promise) => setTimeout(promise, 5000)); await new Promise((promise) => setTimeout(promise, 5000));
CloudRunnerLogger.log('Job created'); CloudRunnerLogger.log('Job created');
return; return result.body.metadata?.name;
} catch (error) { } catch (error) {
CloudRunnerLogger.log(`Error occured creating job: ${error}`); CloudRunnerLogger.log(`Error occured creating job: ${error}`);
throw error; throw error;
@@ -232,7 +249,7 @@ class Kubernetes implements ProviderInterface {
setPodNameAndContainerName(pod: k8s.V1Pod) { setPodNameAndContainerName(pod: k8s.V1Pod) {
this.podName = pod.metadata?.name || ''; this.podName = pod.metadata?.name || '';
this.containerName = pod.status?.containerStatuses?.[0].name || ''; this.containerName = pod.status?.containerStatuses?.[0].name || this.containerName;
} }
async cleanupTaskResources() { async cleanupTaskResources() {
@@ -265,7 +282,7 @@ class Kubernetes implements ProviderInterface {
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
defaultSecretsArray: { ParameterKey: string; EnvironmentVariable: string; ParameterValue: string }[], defaultSecretsArray: { ParameterKey: string; EnvironmentVariable: string; ParameterValue: string }[],
) { ) {
if (buildParameters.retainWorkspace) { if (BuildParameters.shouldUseRetainedWorkspaceMode(buildParameters)) {
return; return;
} }
CloudRunnerLogger.log(`deleting PVC`); CloudRunnerLogger.log(`deleting PVC`);

View File

@@ -1,8 +1,8 @@
import { V1EnvVar, V1EnvVarSource, V1SecretKeySelector } from '@kubernetes/client-node'; import { V1EnvVar, V1EnvVarSource, V1SecretKeySelector } from '@kubernetes/client-node';
import BuildParameters from '../../../build-parameters'; import BuildParameters from '../../../build-parameters';
import { CloudRunnerCustomHooks } from '../../services/cloud-runner-custom-hooks'; import { CommandHookService } from '../../services/hooks/command-hook-service';
import CloudRunnerEnvironmentVariable from '../../services/cloud-runner-environment-variable'; import CloudRunnerEnvironmentVariable from '../../options/cloud-runner-environment-variable';
import CloudRunnerSecret from '../../services/cloud-runner-secret'; import CloudRunnerSecret from '../../options/cloud-runner-secret';
import CloudRunner from '../../cloud-runner'; import CloudRunner from '../../cloud-runner';
class KubernetesJobSpecFactory { class KubernetesJobSpecFactory {
@@ -15,67 +15,12 @@ class KubernetesJobSpecFactory {
secrets: CloudRunnerSecret[], secrets: CloudRunnerSecret[],
buildGuid: string, buildGuid: string,
buildParameters: BuildParameters, buildParameters: BuildParameters,
secretName, secretName: string,
pvcName, pvcName: string,
jobName, jobName: string,
k8s, k8s: any,
containerName: string,
) { ) {
environment.push(
...[
{
name: 'GITHUB_SHA',
value: buildGuid,
},
{
name: 'GITHUB_WORKSPACE',
value: '/data/repo',
},
{
name: 'PROJECT_PATH',
value: buildParameters.projectPath,
},
{
name: 'BUILD_PATH',
value: buildParameters.buildPath,
},
{
name: 'BUILD_FILE',
value: buildParameters.buildFile,
},
{
name: 'BUILD_NAME',
value: buildParameters.buildName,
},
{
name: 'BUILD_METHOD',
value: buildParameters.buildMethod,
},
{
name: 'CUSTOM_PARAMETERS',
value: buildParameters.customParameters,
},
{
name: 'CHOWN_FILES_TO',
value: buildParameters.chownFilesTo,
},
{
name: 'BUILD_TARGET',
value: buildParameters.targetPlatform,
},
{
name: 'ANDROID_VERSION_CODE',
value: buildParameters.androidVersionCode.toString(),
},
{
name: 'ANDROID_KEYSTORE_NAME',
value: buildParameters.androidKeystoreName,
},
{
name: 'ANDROID_KEYALIAS_NAME',
value: buildParameters.androidKeyaliasName,
},
],
);
const job = new k8s.V1Job(); const job = new k8s.V1Job();
job.apiVersion = 'batch/v1'; job.apiVersion = 'batch/v1';
job.kind = 'Job'; job.kind = 'Job';
@@ -87,6 +32,7 @@ class KubernetesJobSpecFactory {
}, },
}; };
job.spec = { job.spec = {
ttlSecondsAfterFinished: 9999,
backoffLimit: 0, backoffLimit: 0,
template: { template: {
spec: { spec: {
@@ -100,16 +46,20 @@ class KubernetesJobSpecFactory {
], ],
containers: [ containers: [
{ {
name: 'main', ttlSecondsAfterFinished: 9999,
name: containerName,
image, image,
command: ['/bin/sh'], command: ['/bin/sh'],
args: ['-c', CloudRunnerCustomHooks.ApplyHooksToCommands(command, CloudRunner.buildParameters)], args: [
'-c',
`${CommandHookService.ApplyHooksToCommands(`${command}\nsleep 2m`, CloudRunner.buildParameters)}`,
],
workingDir: `${workingDirectory}`, workingDir: `${workingDirectory}`,
resources: { resources: {
requests: { requests: {
memory: buildParameters.cloudRunnerMemory || '750M', memory: `${Number.parseInt(buildParameters.containerMemory) / 1024}G` || '750M',
cpu: buildParameters.cloudRunnerCpu || '1', cpu: Number.parseInt(buildParameters.containerCpu) / 1024 || '1',
}, },
}, },
env: [ env: [
@@ -135,7 +85,7 @@ class KubernetesJobSpecFactory {
volumeMounts: [ volumeMounts: [
{ {
name: 'build-mount', name: 'build-mount',
mountPath: `/${mountdir}`, mountPath: `${mountdir}`,
}, },
], ],
lifecycle: { lifecycle: {
@@ -158,7 +108,7 @@ class KubernetesJobSpecFactory {
}, },
}; };
job.spec.template.spec.containers[0].resources.requests[`ephemeral-storage`] = '5Gi'; job.spec.template.spec.containers[0].resources.requests[`ephemeral-storage`] = '10Gi';
return job; return job;
} }

View File

@@ -1,4 +1,4 @@
import CloudRunnerLogger from '../../services/cloud-runner-logger'; import CloudRunnerLogger from '../../services/core/cloud-runner-logger';
import { CoreV1Api } from '@kubernetes/client-node'; import { CoreV1Api } from '@kubernetes/client-node';
class KubernetesPods { class KubernetesPods {
public static async IsPodRunning(podName: string, namespace: string, kubeClient: CoreV1Api) { public static async IsPodRunning(podName: string, namespace: string, kubeClient: CoreV1Api) {
@@ -12,6 +12,12 @@ class KubernetesPods {
return running; return running;
} }
public static async GetPodStatus(podName: string, namespace: string, kubeClient: CoreV1Api) {
const pods = (await kubeClient.listNamespacedPod(namespace)).body.items.find((x) => podName === x.metadata?.name);
const phase = pods?.status?.phase || 'undefined status';
return phase;
}
} }
export default KubernetesPods; export default KubernetesPods;

View File

@@ -1,8 +1,8 @@
import { CoreV1Api } from '@kubernetes/client-node'; import { CoreV1Api } from '@kubernetes/client-node';
import CloudRunnerSecret from '../../services/cloud-runner-secret'; import CloudRunnerSecret from '../../options/cloud-runner-secret';
import * as k8s from '@kubernetes/client-node'; import * as k8s from '@kubernetes/client-node';
import CloudRunnerLogger from '../../services/cloud-runner-logger'; import CloudRunnerLogger from '../../services/core/cloud-runner-logger';
const base64 = require('base-64'); import * as base64 from 'base-64';
class KubernetesSecret { class KubernetesSecret {
static async createSecret( static async createSecret(

View File

@@ -1,9 +1,9 @@
import waitUntil from 'async-wait-until'; import { waitUntil } from 'async-wait-until';
import * as core from '@actions/core'; import * as core from '@actions/core';
import * as k8s from '@kubernetes/client-node'; import * as k8s from '@kubernetes/client-node';
import BuildParameters from '../../../build-parameters'; import BuildParameters from '../../../build-parameters';
import CloudRunnerLogger from '../../services/cloud-runner-logger'; import CloudRunnerLogger from '../../services/core/cloud-runner-logger';
import { IncomingMessage } from 'http'; import { IncomingMessage } from 'node:http';
import GitHub from '../../../github'; import GitHub from '../../../github';
class KubernetesStorage { class KubernetesStorage {

View File

@@ -1,12 +1,16 @@
import { CoreV1Api, KubeConfig, Log } from '@kubernetes/client-node'; import { CoreV1Api, KubeConfig } from '@kubernetes/client-node';
import { Writable } from 'stream'; import CloudRunnerLogger from '../../services/core/cloud-runner-logger';
import CloudRunnerLogger from '../../services/cloud-runner-logger'; import { waitUntil } from 'async-wait-until';
import * as core from '@actions/core'; import { CloudRunnerSystem } from '../../services/core/cloud-runner-system';
import { CloudRunnerStatics } from '../../cloud-runner-statics'; import CloudRunner from '../../cloud-runner';
import waitUntil from 'async-wait-until'; import KubernetesPods from './kubernetes-pods';
import { FollowLogStreamService } from '../../services/follow-log-stream-service'; import { FollowLogStreamService } from '../../services/core/follow-log-stream-service';
class KubernetesTaskRunner { class KubernetesTaskRunner {
static lastReceivedTimestamp: number = 0;
static readonly maxRetry: number = 3;
static lastReceivedMessage: string = ``;
static async runTask( static async runTask(
kubeConfig: KubeConfig, kubeConfig: KubeConfig,
kubeClient: CoreV1Api, kubeClient: CoreV1Api,
@@ -15,84 +19,120 @@ class KubernetesTaskRunner {
containerName: string, containerName: string,
namespace: string, namespace: string,
) { ) {
CloudRunnerLogger.log(`Streaming logs from pod: ${podName} container: ${containerName} namespace: ${namespace}`);
const stream = new Writable();
let output = ''; let output = '';
let didStreamAnyLogs: boolean = false;
let shouldReadLogs = true; let shouldReadLogs = true;
let shouldCleanup = true; let shouldCleanup = true;
stream._write = (chunk, encoding, next) => { let sinceTime = ``;
didStreamAnyLogs = true; let retriesAfterFinish = 0;
let message = chunk.toString().trimRight(`\n`); // eslint-disable-next-line no-constant-condition
message = `[${CloudRunnerStatics.logPrefix}] ${message}`; while (true) {
({ shouldReadLogs, shouldCleanup, output } = FollowLogStreamService.handleIteration( await new Promise((resolve) => setTimeout(resolve, 3000));
message, const lastReceivedMessage =
shouldReadLogs, KubernetesTaskRunner.lastReceivedTimestamp > 0
shouldCleanup, ? `\nLast Log Message "${this.lastReceivedMessage}" ${this.lastReceivedTimestamp}`
output, : ``;
)); CloudRunnerLogger.log(
next(); `Streaming logs from pod: ${podName} container: ${containerName} namespace: ${namespace} ${CloudRunner.buildParameters.kubeVolumeSize}/${CloudRunner.buildParameters.containerCpu}/${CloudRunner.buildParameters.containerMemory}\n${lastReceivedMessage}`,
};
const logOptions = {
follow: true,
pretty: false,
previous: false,
};
try {
const resultError = await new Promise((resolve) =>
new Log(kubeConfig).log(namespace, podName, containerName, stream, resolve, logOptions),
); );
stream.destroy(); if (KubernetesTaskRunner.lastReceivedTimestamp > 0) {
if (resultError) { const currentDate = new Date(KubernetesTaskRunner.lastReceivedTimestamp);
throw resultError; const dateTimeIsoString = currentDate.toISOString();
sinceTime = ` --since-time="${dateTimeIsoString}"`;
} }
if (!didStreamAnyLogs) { let extraFlags = ``;
core.error('Failed to stream any logs, listing namespace events, check for an error with the container'); extraFlags += (await KubernetesPods.IsPodRunning(podName, namespace, kubeClient))
core.error( ? ` -f -c ${containerName}`
JSON.stringify( : ` --previous`;
{ let lastMessageSeenIncludedInChunk = false;
events: (await kubeClient.listNamespacedEvent(namespace)).body.items let lastMessageSeen = false;
.filter((x) => {
return x.involvedObject.name === podName || x.involvedObject.name === jobName; let logs;
})
.map((x) => { try {
return { logs = await CloudRunnerSystem.Run(
type: x.involvedObject.kind, `kubectl logs ${podName}${extraFlags} --timestamps${sinceTime}`,
name: x.involvedObject.name, false,
message: x.message, true,
};
}),
},
undefined,
4,
),
); );
throw new Error(`No logs streamed from k8s`); } catch (error: any) {
await new Promise((resolve) => setTimeout(resolve, 3000));
const continueStreaming = await KubernetesPods.IsPodRunning(podName, namespace, kubeClient);
CloudRunnerLogger.log(`K8s logging error ${error} ${continueStreaming}`);
if (continueStreaming) {
continue;
}
if (retriesAfterFinish < KubernetesTaskRunner.maxRetry) {
retriesAfterFinish++;
continue;
}
throw error;
} }
} catch (error) { const splitLogs = logs.split(`\n`);
if (stream) { for (const chunk of splitLogs) {
stream.destroy(); if (
chunk.replace(/\s/g, ``) === KubernetesTaskRunner.lastReceivedMessage.replace(/\s/g, ``) &&
KubernetesTaskRunner.lastReceivedMessage.replace(/\s/g, ``) !== ``
) {
CloudRunnerLogger.log(`Previous log message found ${chunk}`);
lastMessageSeenIncludedInChunk = true;
}
}
for (const chunk of splitLogs) {
const newDate = Date.parse(`${chunk.toString().split(`Z `)[0]}Z`);
if (chunk.replace(/\s/g, ``) === KubernetesTaskRunner.lastReceivedMessage.replace(/\s/g, ``)) {
lastMessageSeen = true;
}
if (lastMessageSeenIncludedInChunk && !lastMessageSeen) {
continue;
}
const message = CloudRunner.buildParameters.cloudRunnerDebug ? chunk : chunk.split(`Z `)[1];
KubernetesTaskRunner.lastReceivedMessage = chunk;
KubernetesTaskRunner.lastReceivedTimestamp = newDate;
({ shouldReadLogs, shouldCleanup, output } = FollowLogStreamService.handleIteration(
message,
shouldReadLogs,
shouldCleanup,
output,
));
}
if (FollowLogStreamService.DidReceiveEndOfTransmission) {
CloudRunnerLogger.log('end of log stream');
break;
} }
throw error;
} }
CloudRunnerLogger.log('end of log stream');
return output; return output;
} }
static async watchUntilPodRunning(kubeClient: CoreV1Api, podName: string, namespace: string) { static async watchUntilPodRunning(kubeClient: CoreV1Api, podName: string, namespace: string) {
let success: boolean = false; let success: boolean = false;
let message = ``;
CloudRunnerLogger.log(`Watching ${podName} ${namespace}`); CloudRunnerLogger.log(`Watching ${podName} ${namespace}`);
await waitUntil( await waitUntil(
async () => { async () => {
const status = await kubeClient.readNamespacedPodStatus(podName, namespace); const status = await kubeClient.readNamespacedPodStatus(podName, namespace);
const phase = status?.body.status?.phase; const phase = status?.body.status?.phase;
success = phase === 'Running'; success = phase === 'Running';
CloudRunnerLogger.log( message = `Phase:${status.body.status?.phase} \n Reason:${
`${status.body.status?.phase} ${status.body.status?.conditions?.[0].reason || ''} ${ status.body.status?.conditions?.[0].reason || ''
status.body.status?.conditions?.[0].message || '' } \n Message:${status.body.status?.conditions?.[0].message || ''}`;
}`,
); // CloudRunnerLogger.log(
// JSON.stringify(
// (await kubeClient.listNamespacedEvent(namespace)).body.items
// .map((x) => {
// return {
// message: x.message || ``,
// name: x.metadata.name || ``,
// reason: x.reason || ``,
// };
// })
// .filter((x) => x.name.includes(podName)),
// undefined,
// 4,
// ),
// );
if (success || phase !== 'Pending') return true; if (success || phase !== 'Pending') return true;
return false; return false;
@@ -102,6 +142,9 @@ class KubernetesTaskRunner {
intervalBetweenAttempts: 15000, intervalBetweenAttempts: 15000,
}, },
); );
if (!success) {
CloudRunnerLogger.log(message);
}
return success; return success;
} }

View File

@@ -1,9 +1,9 @@
import BuildParameters from '../../../build-parameters'; import BuildParameters from '../../../build-parameters';
import { CloudRunnerSystem } from '../../services/cloud-runner-system'; import { CloudRunnerSystem } from '../../services/core/cloud-runner-system';
import CloudRunnerEnvironmentVariable from '../../services/cloud-runner-environment-variable'; import CloudRunnerEnvironmentVariable from '../../options/cloud-runner-environment-variable';
import CloudRunnerLogger from '../../services/cloud-runner-logger'; import CloudRunnerLogger from '../../services/core/cloud-runner-logger';
import { ProviderInterface } from '../provider-interface'; import { ProviderInterface } from '../provider-interface';
import CloudRunnerSecret from '../../services/cloud-runner-secret'; import CloudRunnerSecret from '../../options/cloud-runner-secret';
import { ProviderResource } from '../provider-resource'; import { ProviderResource } from '../provider-resource';
import { ProviderWorkflow } from '../provider-workflow'; import { ProviderWorkflow } from '../provider-workflow';

View File

@@ -1,6 +1,6 @@
import BuildParameters from '../../build-parameters'; import BuildParameters from '../../build-parameters';
import CloudRunnerEnvironmentVariable from '../services/cloud-runner-environment-variable'; import CloudRunnerEnvironmentVariable from '../options/cloud-runner-environment-variable';
import CloudRunnerSecret from '../services/cloud-runner-secret'; import CloudRunnerSecret from '../options/cloud-runner-secret';
import { ProviderResource } from './provider-resource'; import { ProviderResource } from './provider-resource';
import { ProviderWorkflow } from './provider-workflow'; import { ProviderWorkflow } from './provider-workflow';
@@ -14,7 +14,7 @@ export interface ProviderInterface {
branchName: string, branchName: string,
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
defaultSecretsArray: { ParameterKey: string; EnvironmentVariable: string; ParameterValue: string }[], defaultSecretsArray: { ParameterKey: string; EnvironmentVariable: string; ParameterValue: string }[],
); ): any;
setupWorkflow( setupWorkflow(
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
buildGuid: string, buildGuid: string,
@@ -24,7 +24,7 @@ export interface ProviderInterface {
branchName: string, branchName: string,
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
defaultSecretsArray: { ParameterKey: string; EnvironmentVariable: string; ParameterValue: string }[], defaultSecretsArray: { ParameterKey: string; EnvironmentVariable: string; ParameterValue: string }[],
); ): any;
runTaskInWorkflow( runTaskInWorkflow(
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
buildGuid: string, buildGuid: string,

View File

@@ -1,8 +1,8 @@
import BuildParameters from '../../../build-parameters'; import BuildParameters from '../../../build-parameters';
import CloudRunnerEnvironmentVariable from '../../services/cloud-runner-environment-variable'; import CloudRunnerEnvironmentVariable from '../../options/cloud-runner-environment-variable';
import CloudRunnerLogger from '../../services/cloud-runner-logger'; import CloudRunnerLogger from '../../services/core/cloud-runner-logger';
import { ProviderInterface } from '../provider-interface'; import { ProviderInterface } from '../provider-interface';
import CloudRunnerSecret from '../../services/cloud-runner-secret'; import CloudRunnerSecret from '../../options/cloud-runner-secret';
import { ProviderResource } from '../provider-resource'; import { ProviderResource } from '../provider-resource';
import { ProviderWorkflow } from '../provider-workflow'; import { ProviderWorkflow } from '../provider-workflow';

View File

@@ -1,16 +1,16 @@
import { assert } from 'console'; import { assert } from 'node:console';
import fs from 'fs'; import fs from 'node:fs';
import path from 'path'; import path from 'node:path';
import CloudRunner from '../cloud-runner'; import CloudRunner from '../cloud-runner';
import CloudRunnerLogger from '../services/cloud-runner-logger'; import CloudRunnerLogger from '../services/core/cloud-runner-logger';
import { CloudRunnerFolders } from '../services/cloud-runner-folders'; import { CloudRunnerFolders } from '../options/cloud-runner-folders';
import { CloudRunnerSystem } from '../services/cloud-runner-system'; import { CloudRunnerSystem } from '../services/core/cloud-runner-system';
import { LfsHashing } from '../services/lfs-hashing'; import { LfsHashing } from '../services/utility/lfs-hashing';
import { RemoteClientLogger } from './remote-client-logger'; import { RemoteClientLogger } from './remote-client-logger';
import { Cli } from '../../cli/cli'; import { Cli } from '../../cli/cli';
import { CliFunction } from '../../cli/cli-functions-repository'; import { CliFunction } from '../../cli/cli-functions-repository';
// eslint-disable-next-line github/no-then // eslint-disable-next-line github/no-then
const fileExists = async (fpath) => !!(await fs.promises.stat(fpath).catch(() => false)); const fileExists = async (fpath: fs.PathLike) => !!(await fs.promises.stat(fpath).catch(() => false));
export class Caching { export class Caching {
@CliFunction(`cache-push`, `push to cache`) @CliFunction(`cache-push`, `push to cache`)
@@ -19,9 +19,9 @@ export class Caching {
const buildParameter = JSON.parse(process.env.BUILD_PARAMETERS || '{}'); const buildParameter = JSON.parse(process.env.BUILD_PARAMETERS || '{}');
CloudRunner.buildParameters = buildParameter; CloudRunner.buildParameters = buildParameter;
await Caching.PushToCache( await Caching.PushToCache(
Cli.options['cachePushTo'], Cli.options!['cachePushTo'],
Cli.options['cachePushFrom'], Cli.options!['cachePushFrom'],
Cli.options['artifactName'] || '', Cli.options!['artifactName'] || '',
); );
} catch (error: any) { } catch (error: any) {
CloudRunnerLogger.log(`${error}`); CloudRunnerLogger.log(`${error}`);
@@ -34,9 +34,9 @@ export class Caching {
const buildParameter = JSON.parse(process.env.BUILD_PARAMETERS || '{}'); const buildParameter = JSON.parse(process.env.BUILD_PARAMETERS || '{}');
CloudRunner.buildParameters = buildParameter; CloudRunner.buildParameters = buildParameter;
await Caching.PullFromCache( await Caching.PullFromCache(
Cli.options['cachePushFrom'], Cli.options!['cachePushFrom'],
Cli.options['cachePushTo'], Cli.options!['cachePushTo'],
Cli.options['artifactName'] || '', Cli.options!['artifactName'] || '',
); );
} catch (error: any) { } catch (error: any) {
CloudRunnerLogger.log(`${error}`); CloudRunnerLogger.log(`${error}`);
@@ -44,20 +44,21 @@ export class Caching {
} }
public static async PushToCache(cacheFolder: string, sourceFolder: string, cacheArtifactName: string) { public static async PushToCache(cacheFolder: string, sourceFolder: string, cacheArtifactName: string) {
CloudRunnerLogger.log(`Pushing to cache ${sourceFolder}`);
cacheArtifactName = cacheArtifactName.replace(' ', ''); cacheArtifactName = cacheArtifactName.replace(' ', '');
const startPath = process.cwd(); const startPath = process.cwd();
let compressionSuffix = ''; let compressionSuffix = '';
if (CloudRunner.buildParameters.useLz4Compression === true) { if (CloudRunner.buildParameters.useCompressionStrategy === true) {
compressionSuffix = `.lz4`; compressionSuffix = `.lz4`;
} }
CloudRunnerLogger.log(`Compression: ${CloudRunner.buildParameters.useLz4Compression} ${compressionSuffix}`); CloudRunnerLogger.log(`Compression: ${CloudRunner.buildParameters.useCompressionStrategy} ${compressionSuffix}`);
try { try {
if (!(await fileExists(cacheFolder))) { if (!(await fileExists(cacheFolder))) {
await CloudRunnerSystem.Run(`mkdir -p ${cacheFolder}`); await CloudRunnerSystem.Run(`mkdir -p ${cacheFolder}`);
} }
process.chdir(path.resolve(sourceFolder, '..')); process.chdir(path.resolve(sourceFolder, '..'));
if (CloudRunner.buildParameters.cloudRunnerDebug) { if (CloudRunner.buildParameters.cloudRunnerDebug === true) {
CloudRunnerLogger.log( CloudRunnerLogger.log(
`Hashed cache folder ${await LfsHashing.hashAllFiles(sourceFolder)} ${sourceFolder} ${path.basename( `Hashed cache folder ${await LfsHashing.hashAllFiles(sourceFolder)} ${sourceFolder} ${path.basename(
sourceFolder, sourceFolder,
@@ -69,11 +70,6 @@ export class Caching {
`There is ${contents.length} files/dir in the source folder ${path.basename(sourceFolder)}`, `There is ${contents.length} files/dir in the source folder ${path.basename(sourceFolder)}`,
); );
if (CloudRunner.buildParameters.cloudRunnerDebug) {
// await CloudRunnerSystem.Run(`tree -L 2 ./..`);
// await CloudRunnerSystem.Run(`tree -L 2`);
}
if (contents.length === 0) { if (contents.length === 0) {
CloudRunnerLogger.log( CloudRunnerLogger.log(
`Did not push source folder to cache because it was empty ${path.basename(sourceFolder)}`, `Did not push source folder to cache because it was empty ${path.basename(sourceFolder)}`,
@@ -102,9 +98,15 @@ export class Caching {
process.chdir(`${startPath}`); process.chdir(`${startPath}`);
} }
public static async PullFromCache(cacheFolder: string, destinationFolder: string, cacheArtifactName: string = ``) { public static async PullFromCache(cacheFolder: string, destinationFolder: string, cacheArtifactName: string = ``) {
CloudRunnerLogger.log(`Pulling from cache ${destinationFolder} ${CloudRunner.buildParameters.skipCache}`);
if (`${CloudRunner.buildParameters.skipCache}` === `true`) {
CloudRunnerLogger.log(`Skipping cache debugSkipCache is true`);
return;
}
cacheArtifactName = cacheArtifactName.replace(' ', ''); cacheArtifactName = cacheArtifactName.replace(' ', '');
let compressionSuffix = ''; let compressionSuffix = '';
if (CloudRunner.buildParameters.useLz4Compression === true) { if (CloudRunner.buildParameters.useCompressionStrategy === true) {
compressionSuffix = `.lz4`; compressionSuffix = `.lz4`;
} }
const startPath = process.cwd(); const startPath = process.cwd();
@@ -160,7 +162,6 @@ export class Caching {
RemoteClientLogger.logWarning( RemoteClientLogger.logWarning(
`cache item ${cacheArtifactName}.tar${compressionSuffix} doesn't exist ${destinationFolder}`, `cache item ${cacheArtifactName}.tar${compressionSuffix} doesn't exist ${destinationFolder}`,
); );
await CloudRunnerSystem.Run(`tree ${cacheFolder}`);
throw new Error(`Failed to get cache item, but cache hit was found: ${cacheSelection}`); throw new Error(`Failed to get cache item, but cache hit was found: ${cacheSelection}`);
} }
} }

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