Compare commits

...

7 Commits

Author SHA1 Message Date
Andrew Kahr
f4d2cceeb5 Hotfixes for 4.2.0 (#630)
* Fix mac env variables not getting skip activation

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

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

* Update packages and fix lint/test issues

* Use nullish coalescing operator

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

* fixes

* fixes

* fixes

* fixes

* check for startup message in workflows

* check for startup message in workflows

* check for startup message in workflows

* check for startup message in workflows

* check for startup message in workflows

* check for startup message in workflows

* Update cloud-runner-ci-pipeline.yml

* Update cloud-runner-ci-pipeline.yml

* no storage class specified

* log file path

* log file path

* log file path

* log file path

* log file path

* log file path

* log file path

* log file path

* updates

* log file path

* latest develop

* log file path

* log file path

* Update package.json

* log file path

* log file path

* log file path

* log file path

* log file path

* log file path

* log file path

* log file path

* log file path

* log file path

* log file path

* log file path

* log file path

* log file path

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

* update pipeline to use k3s

* version: 'latest'

* fixes

* disable aws pipe for now

* disable aws pipe for now

* disable aws pipe for now

* disable aws pipe for now

* disable aws pipe for now

* disable aws pipe for now

* disable aws pipe for now

* disable aws pipe for now

* disable aws pipe for now

* disable aws pipe for now

* push k8s logs to LOG SERVICE IP

* push k8s logs to LOG SERVICE IP

* push k8s logs to LOG SERVICE IP

* push k8s logs to LOG SERVICE IP

* push k8s logs to LOG SERVICE IP

* push k8s logs to LOG SERVICE IP

* push k8s logs to LOG SERVICE IP

* push k8s logs to LOG SERVICE IP

* tests

* tests

* tests

* tests

* tests

* tests

* tests

* tests

* tests

* tests

* tests

* tests

* tests

* tests

* tests

* tests

* tests

* podname logs for log service

* podname logs for log service

* podname logs for log service

* podname logs for log service

* podname logs for log service

* podname logs for log service

* podname logs for log service

* podname logs for log service

* podname logs for log service

* hashed logs

* hashed logs

* hashed logs

* hashed logs

* hashed logs

* hashed logs

* no wait, just repeat logs

* no wait, just repeat logs

* remove typo - double await

* test fix - kubernetes - name typo in github yaml

* test fix - kubernetes - name typo in github yaml

* check missing log file

* check missing log file

* Push to steam test

* Push to steam test

* Fix path

* k8s reliable log hashing

* k8s reliable log hashing

* k8s reliable log hashing

* hashed logging k8s

* hashed logging k8s

* hashed logging k8s

* hashed logging k8s

* hashed logging k8s

* hashed logging k8s

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

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

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

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

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

* Fix exit flow for k8s job

* hash comparison logging for log complete in k8s flow

* Interrupt k8s logs when logs found

* cleanup async parameter

* cleanup async parameter

* cleanup async parameter

* fixes

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

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

* Wrap quotes

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

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

* Additional cleanup

* Fix possible hang

* Don't mislead with activation server on windows

* Update node version
2023-12-12 22:10:57 -08:00
68 changed files with 6535 additions and 2897 deletions

View File

@@ -1,22 +1,11 @@
{ {
"plugins": [ "plugins": ["jest", "@typescript-eslint", "prettier", "unicorn"],
"jest", "extends": ["plugin:unicorn/recommended", "plugin:github/recommended", "plugin:prettier/recommended"],
"@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": [ "extraFileExtensions": [".mjs"],
".mjs"
],
"ecmaFeatures": { "ecmaFeatures": {
"impliedStrict": true "impliedStrict": true
}, },
@@ -33,10 +22,7 @@
// Namespaces or sometimes needed // Namespaces or sometimes needed
"import/no-namespace": "off", "import/no-namespace": "off",
// Properly format comments // Properly format comments
"spaced-comment": [ "spaced-comment": ["error", "always"],
"error",
"always"
],
"lines-around-comment": [ "lines-around-comment": [
"error", "error",
{ {
@@ -71,12 +57,7 @@
// Enforce camelCase // Enforce camelCase
"camelcase": "error", "camelcase": "error",
// Allow forOfStatements // Allow forOfStatements
"no-restricted-syntax": [ "no-restricted-syntax": ["error", "ForInStatement", "LabeledStatement", "WithStatement"],
"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

View File

@@ -12,6 +12,9 @@
#### Successful Workflow Run Link #### 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

View File

@@ -37,7 +37,7 @@ jobs:
########################### ###########################
# Cache # # Cache #
########################### ###########################
- uses: actions/cache@v3 - uses: actions/cache@v4
with: with:
path: ${{ matrix.projectPath }}/Library path: ${{ matrix.projectPath }}/Library
key: Library-${{ matrix.projectPath }}-macos-${{ matrix.targetPlatform }} key: Library-${{ matrix.projectPath }}-macos-${{ matrix.targetPlatform }}
@@ -45,13 +45,6 @@ jobs:
Library-${{ matrix.projectPath }}-macos- Library-${{ matrix.projectPath }}-macos-
Library- Library-
###########################
# Set Scripting Backend #
###########################
- name: Set Scripting Backend To il2cpp
run: |
mv -f "./test-project/ProjectSettings/ProjectSettingsIl2cpp.asset" "./test-project/ProjectSettings/ProjectSettings.asset"
########################### ###########################
# Build # # Build #
########################### ###########################
@@ -61,6 +54,7 @@ jobs:
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }} UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
with: with:
buildName: 'GameCI Test Build'
projectPath: ${{ matrix.projectPath }} projectPath: ${{ matrix.projectPath }}
unityVersion: ${{ matrix.unityVersion }} unityVersion: ${{ matrix.unityVersion }}
targetPlatform: ${{ matrix.targetPlatform }} targetPlatform: ${{ matrix.targetPlatform }}
@@ -71,8 +65,8 @@ jobs:
########################### ###########################
# Upload # # Upload #
########################### ###########################
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v4
with: with:
name: Build MacOS (${{ matrix.unityVersion }}) name: Build ${{ matrix.targetPlatform }} on MacOS (${{ matrix.unityVersion }})
path: build path: build
retention-days: 14 retention-days: 14

View File

@@ -59,6 +59,10 @@ jobs:
- Android # Build an Android .apk. - Android # Build an Android .apk.
- WebGL # WebGL. - WebGL # WebGL.
steps: steps:
- name: Clear Space for Android Build
if: matrix.targetPlatform == 'Android'
uses: jlumbroso/free-disk-space@v1.3.1
########################### ###########################
# Checkout # # Checkout #
########################### ###########################
@@ -69,7 +73,7 @@ jobs:
########################### ###########################
# 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 }}
@@ -88,6 +92,7 @@ jobs:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} 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 }}
@@ -108,6 +113,7 @@ jobs:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} 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 }}
@@ -128,6 +134,7 @@ jobs:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} 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 }}
@@ -138,8 +145,8 @@ jobs:
########################### ###########################
# 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

@@ -39,7 +39,7 @@ jobs:
########################### ###########################
# Cache # # Cache #
########################### ###########################
- uses: actions/cache@v3 - uses: actions/cache@v4
with: with:
path: ${{ matrix.projectPath }}/Library path: ${{ matrix.projectPath }}/Library
key: Library-${{ matrix.projectPath }}-windows-${{ matrix.targetPlatform }} key: Library-${{ matrix.projectPath }}-windows-${{ matrix.targetPlatform }}
@@ -47,13 +47,6 @@ jobs:
Library-${{ matrix.projectPath }}-windows- Library-${{ matrix.projectPath }}-windows-
Library- 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 # # Build #
########################### ###########################
@@ -67,6 +60,7 @@ jobs:
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }} UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
with: with:
buildName: 'GameCI Test Build'
projectPath: ${{ matrix.projectPath }} projectPath: ${{ matrix.projectPath }}
unityVersion: ${{ matrix.unityVersion }} unityVersion: ${{ matrix.unityVersion }}
targetPlatform: ${{ matrix.targetPlatform }} targetPlatform: ${{ matrix.targetPlatform }}
@@ -90,6 +84,7 @@ jobs:
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }} UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
with: with:
buildName: 'GameCI Test Build'
projectPath: ${{ matrix.projectPath }} projectPath: ${{ matrix.projectPath }}
unityVersion: ${{ matrix.unityVersion }} unityVersion: ${{ matrix.unityVersion }}
targetPlatform: ${{ matrix.targetPlatform }} targetPlatform: ${{ matrix.targetPlatform }}
@@ -112,6 +107,7 @@ jobs:
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }} UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
with: with:
buildName: 'GameCI Test Build'
projectPath: ${{ matrix.projectPath }} projectPath: ${{ matrix.projectPath }}
unityVersion: ${{ matrix.unityVersion }} unityVersion: ${{ matrix.unityVersion }}
targetPlatform: ${{ matrix.targetPlatform }} targetPlatform: ${{ matrix.targetPlatform }}
@@ -122,8 +118,8 @@ jobs:
########################### ###########################
# Upload # # Upload #
########################### ###########################
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v4
with: with:
name: Build Windows (${{ matrix.unityVersion }}) name: Build ${{ matrix.targetPlatform }} on Windows (${{ matrix.unityVersion }})
path: build path: build
retention-days: 14 retention-days: 14

View File

@@ -18,42 +18,37 @@ env:
GCP_PROJECT: unitykubernetesbuilder GCP_PROJECT: unitykubernetesbuilder
GCP_LOG_FILE: ${{ github.workspace }}/cloud-runner-logs.txt GCP_LOG_FILE: ${{ github.workspace }}/cloud-runner-logs.txt
AWS_REGION: eu-west-2 AWS_REGION: eu-west-2
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: eu-west-2 AWS_DEFAULT_REGION: eu-west-2
AWS_STACK_NAME: game-ci-team-pipelines AWS_STACK_NAME: game-ci-team-pipelines
CLOUD_RUNNER_BRANCH: ${{ github.ref }} CLOUD_RUNNER_BRANCH: ${{ github.ref }}
DEBUG: true DEBUG: true
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }} UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
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:
smokeTests: tests:
name: Smoke Tests name: 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:
test: test:
#- 'cloud-runner-async-workflow' - 'cloud-runner-end2end-locking'
- 'cloud-runner-end2end-caching'
- 'cloud-runner-end2end-retaining'
- 'cloud-runner-caching' - 'cloud-runner-caching'
# - 'cloud-runner-end2end-caching'
# - 'cloud-runner-end2end-retaining'
- 'cloud-runner-environment' - 'cloud-runner-environment'
- 'cloud-runner-image'
- 'cloud-runner-hooks' - 'cloud-runner-hooks'
- 'cloud-runner-local-persistence' - 'cloud-runner-local-persistence'
- 'cloud-runner-locking-core' - 'cloud-runner-locking-core'
- 'cloud-runner-locking-get-locked' - 'cloud-runner-locking-get-locked'
providerStrategy:
#- aws
- local-docker
#- k8s
steps: steps:
- name: Checkout (default) - name: Checkout (default)
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -65,58 +60,80 @@ jobs:
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 "${{ matrix.test }}" --detectOpenHandles --forceExit --runInBand - run: yarn run test "${{ matrix.test }}" --detectOpenHandles --forceExit --runInBand
timeout-minutes: 35 timeout-minutes: 60
env: env:
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }} UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
PROJECT_PATH: test-project PROJECT_PATH: test-project
TARGET_PLATFORM: StandaloneWindows64 TARGET_PLATFORM: StandaloneWindows64
cloudRunnerTests: true cloudRunnerTests: true
versioning: None versioning: None
CLOUD_RUNNER_CLUSTER: ${{ matrix.providerStrategy }} KUBE_STORAGE_CLASS: local-path
tests: PROVIDER_STRATEGY: local-docker
# needs: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
# - smokeTests AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
# - buildTargetTests GIT_PRIVATE_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }}
name: Integration Tests GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
k8sTests:
name: K8s Tests
if: github.event.event_type != 'pull_request_target'
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
test:
# - 'cloud-runner-async-workflow'
- 'cloud-runner-end2end-locking'
- 'cloud-runner-end2end-caching'
- 'cloud-runner-end2end-retaining'
- 'cloud-runner-kubernetes'
- 'cloud-runner-environment'
- 'cloud-runner-github-checks'
steps:
- name: Checkout (default)
uses: actions/checkout@v2
with:
lfs: false
- run: yarn
- name: actions-k3s
uses: debianmaster/actions-k3s@v1.0.5
with:
version: 'latest'
- run: yarn run test "${{ matrix.test }}" --detectOpenHandles --forceExit --runInBand
timeout-minutes: 60
env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
PROJECT_PATH: test-project
TARGET_PLATFORM: StandaloneWindows64
cloudRunnerTests: true
versioning: None
KUBE_STORAGE_CLASS: local-path
PROVIDER_STRATEGY: k8s
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
GIT_PRIVATE_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
awsTests:
name: AWS 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:
providerStrategy:
- aws
- local-docker
- k8s
test: test:
- 'cloud-runner-async-workflow'
#- 'cloud-runner-caching'
- 'cloud-runner-end2end-locking' - 'cloud-runner-end2end-locking'
- 'cloud-runner-end2end-caching' - 'cloud-runner-end2end-caching'
- 'cloud-runner-end2end-retaining' - 'cloud-runner-end2end-retaining'
- 'cloud-runner-environment' - 'cloud-runner-environment'
#- 'cloud-runner-hooks'
- 'cloud-runner-s3-steps' - 'cloud-runner-s3-steps'
#- 'cloud-runner-local-persistence'
#- 'cloud-runner-locking-core'
#- 'cloud-runner-locking-get-locked'
steps: steps:
- name: Checkout (default) - name: Checkout (default)
uses: actions/checkout@v4 uses: actions/checkout@v2
with: with:
lfs: false lfs: false
- name: Configure AWS Credentials - name: Configure AWS Credentials
@@ -125,29 +142,24 @@ jobs:
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 "${{ matrix.test }}" --detectOpenHandles --forceExit --runInBand - run: yarn run test "${{ matrix.test }}" --detectOpenHandles --forceExit --runInBand
timeout-minutes: 60 timeout-minutes: 60
env: env:
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }} UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
PROJECT_PATH: test-project PROJECT_PATH: test-project
TARGET_PLATFORM: StandaloneWindows64 TARGET_PLATFORM: StandaloneWindows64
cloudRunnerTests: true cloudRunnerTests: true
versioning: None versioning: None
PROVIDER_STRATEGY: ${{ matrix.providerStrategy }} KUBE_STORAGE_CLASS: local-path
PROVIDER_STRATEGY: aws
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
GIT_PRIVATE_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
buildTargetTests: buildTargetTests:
name: Local Build Target Tests name: Local Build Target Tests
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -164,7 +176,7 @@ jobs:
- StandaloneLinux64 # Build a Linux 64-bit standalone. - StandaloneLinux64 # Build a Linux 64-bit standalone.
- WebGL # WebGL. - WebGL # WebGL.
- iOS # Build an iOS player. - iOS # Build an iOS player.
- Android # Build an Android .apk. # - Android # Build an Android .apk.
steps: steps:
- name: Checkout (default) - name: Checkout (default)
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -175,7 +187,13 @@ jobs:
id: unity-build id: unity-build
timeout-minutes: 30 timeout-minutes: 30
env: env:
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }} UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
GIT_PRIVATE_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with: with:
cloudRunnerTests: true cloudRunnerTests: true
versioning: None versioning: None

View File

@@ -186,11 +186,11 @@ inputs:
description: description:
'[CloudRunner] Either local, k8s or aws can be used to run builds on a remote cluster. Additional parameters must '[CloudRunner] Either local, k8s or aws can be used to run builds on a remote cluster. Additional parameters must
be configured.' be configured.'
cloudRunnerCpu: containerCpu:
default: '' default: ''
required: false required: false
description: '[CloudRunner] Amount of CPU time to assign the remote build container' description: '[CloudRunner] Amount of CPU time to assign the remote build container'
cloudRunnerMemory: containerMemory:
default: '' default: ''
required: false required: false
description: '[CloudRunner] Amount of memory to assign the remote build container' description: '[CloudRunner] Amount of memory to assign the remote build container'
@@ -253,6 +253,10 @@ inputs:
description: description:
'The path to mount the workspace inside the docker container. For windows, leave out the drive letter. For example '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' c:/github/workspace should be defined as /github/workspace'
skipActivation:
default: 'false'
required: false
description: 'Skip the activation/deactivation of Unity. This assumes Unity is already activated.'
outputs: outputs:
volume: volume:
@@ -270,5 +274,5 @@ 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'

Binary file not shown.

6115
dist/index.js generated vendored

File diff suppressed because it is too large Load Diff

2
dist/index.js.map generated vendored

File diff suppressed because one or more lines are too long

25
dist/licenses.txt generated vendored
View File

@@ -2847,31 +2847,6 @@ PERFORMANCE OF THIS SOFTWARE.
is-plain-object
MIT
The MIT License (MIT)
Copyright (c) 2014-2017, Jon Schlinkert.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
is-stream is-stream
MIT MIT
MIT License MIT License

View File

@@ -1,32 +1,40 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# #
# Create directories for license activation # Perform Activation
# #
UNITY_LICENSE_PATH="/Library/Application Support/Unity" if [ "$SKIP_ACTIVATION" != "true" ]; then
UNITY_LICENSE_PATH="/Library/Application Support/Unity"
if [ ! -d "$UNITY_LICENSE_PATH" ]; then if [ ! -d "$UNITY_LICENSE_PATH" ]; then
echo "Creating Unity License Directory" echo "Creating Unity License Directory"
sudo mkdir -p "$UNITY_LICENSE_PATH" sudo mkdir -p "$UNITY_LICENSE_PATH"
sudo chmod -R 777 "$UNITY_LICENSE_PATH" sudo chmod -R 777 "$UNITY_LICENSE_PATH"
fi; fi;
ACTIVATE_LICENSE_PATH="$ACTION_FOLDER/BlankProject" ACTIVATE_LICENSE_PATH="$ACTION_FOLDER/BlankProject"
mkdir -p "$ACTIVATE_LICENSE_PATH" mkdir -p "$ACTIVATE_LICENSE_PATH"
source $ACTION_FOLDER/platforms/mac/steps/activate.sh
else
echo "Skipping activation"
fi
# #
# Run steps # Run Build
# #
source $ACTION_FOLDER/platforms/mac/steps/activate.sh
source $ACTION_FOLDER/platforms/mac/steps/build.sh source $ACTION_FOLDER/platforms/mac/steps/build.sh
source $ACTION_FOLDER/platforms/mac/steps/return_license.sh
# #
# Remove license activation directory # License Cleanup
# #
rm -r "$ACTIVATE_LICENSE_PATH" if [ "$SKIP_ACTIVATION" != "true" ]; then
source $ACTION_FOLDER/platforms/mac/steps/return_license.sh
rm -r "$ACTIVATE_LICENSE_PATH"
fi
# #
# Instructions for debugging # Instructions for debugging

View File

@@ -129,7 +129,7 @@ echo ""
/Applications/Unity/Hub/Editor/$UNITY_VERSION/Unity.app/Contents/MacOS/Unity \ /Applications/Unity/Hub/Editor/$UNITY_VERSION/Unity.app/Contents/MacOS/Unity \
-logFile - \ -logFile - \
-quit \ $( [ "${MANUAL_EXIT}" == "true" ] || echo "-quit" ) \
-batchmode \ -batchmode \
-nographics \ -nographics \
-username "$UNITY_EMAIL" \ -username "$UNITY_EMAIL" \

View File

@@ -1,5 +1,14 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# if blankproject folder doesn't exist create it
if [ ! -d "/BlankProject" ]; then
mkdir /BlankProject
fi
# if blankproject folder doesn't exist create it
if [ ! -d "/BlankProject/Assets" ]; then
mkdir /BlankProject/Assets
fi
if [[ -n "$UNITY_SERIAL" && -n "$UNITY_EMAIL" && -n "$UNITY_PASSWORD" ]]; then if [[ -n "$UNITY_SERIAL" && -n "$UNITY_EMAIL" && -n "$UNITY_PASSWORD" ]]; then
# #
# SERIAL LICENSE MODE # SERIAL LICENSE MODE
@@ -75,11 +84,12 @@ 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."
echo "::error ::No valid license activation strategy could be determined. Make sure to provide UNITY_EMAIL, UNITY_PASSWORD, and either a UNITY_SERIAL \ 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." 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. # Immediately exit as no UNITY_EXIT_CODE can be derived.
exit 1; exit 1;
@@ -98,6 +108,3 @@ else
echo "::error ::There was an error while trying to activate the Unity license." 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

@@ -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.
# #
@@ -25,6 +20,3 @@ elif [[ -n "$UNITY_SERIAL" ]]; then
-password "$UNITY_PASSWORD" \ -password "$UNITY_PASSWORD" \
-projectPath "/BlankProject" -projectPath "/BlankProject"
fi fi
# Return to previous working directory
popd

View File

@@ -5,15 +5,23 @@
# #
source /steps/set_extra_git_configs.sh source /steps/set_extra_git_configs.sh
source /steps/set_gitcredential.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 [ "$SKIP_ACTIVATION" != "true" ]; then
if [[ $UNITY_EXIT_CODE -ne 0 ]]; then source /steps/activate.sh
exit $UNITY_EXIT_CODE
# If we didn't activate successfully, exit with the exit code from the activation step.
if [[ $UNITY_EXIT_CODE -ne 0 ]]; then
exit $UNITY_EXIT_CODE
fi
else
echo "Skipping activation"
fi fi
source /steps/build.sh source /steps/build.sh
source /steps/return_license.sh
if [ "$SKIP_ACTIVATION" != "true" ]; then
source /steps/return_license.sh
fi
# #
# Instructions for debugging # Instructions for debugging

View File

@@ -6,11 +6,64 @@ Write-Output "# Activating #"
Write-Output "###########################" Write-Output "###########################"
Write-Output "" Write-Output ""
& "$Env:UNITY_PATH/Editor/Unity.exe" -batchmode -quit -nographics ` if ( ($null -ne ${env:UNITY_SERIAL}) -and ($null -ne ${env:UNITY_EMAIL}) -and ($null -ne ${env:UNITY_PASSWORD}) )
-username $Env:UNITY_EMAIL ` {
-password $Env:UNITY_PASSWORD ` #
-serial $Env:UNITY_SERIAL ` # SERIAL LICENSE MODE
-projectPath "c:/BlankProject" ` #
-logfile - | Out-Host # This will activate unity, using the serial activation process.
#
Write-Output "Requesting activation"
$ACTIVATION_EXIT_CODE = $LASTEXITCODE $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

@@ -137,29 +137,30 @@ $unityArgs = @(
"-batchmode", "-batchmode",
"-nographics", "-nographics",
"-silent-crashes", "-silent-crashes",
"-projectPath", $Env:UNITY_PROJECT_PATH, "-customBuildName", "`"$Env:BUILD_NAME`"",
"-executeMethod", $Env:BUILD_METHOD, "-projectPath", "`"$Env:UNITY_PROJECT_PATH`"",
"-buildTarget", $Env:BUILD_TARGET, "-executeMethod", "`"$Env:BUILD_METHOD`"",
"-customBuildTarget", $Env:BUILD_TARGET, "-buildTarget", "`"$Env:BUILD_TARGET`"",
"-customBuildPath", $Env:CUSTOM_BUILD_PATH, "-customBuildTarget", "`"$Env:BUILD_TARGET`"",
"-buildVersion", $Env:VERSION, "-customBuildPath", "`"$Env:CUSTOM_BUILD_PATH`"",
"-androidVersionCode", $Env:ANDROID_VERSION_CODE, "-buildVersion", "`"$Env:VERSION`"",
"-androidKeystorePass", $Env:ANDROID_KEYSTORE_PASS, "-androidVersionCode", "`"$Env:ANDROID_VERSION_CODE`"",
"-androidKeyaliasName", $Env:ANDROID_KEYALIAS_NAME, "-androidKeystorePass", "`"$Env:ANDROID_KEYSTORE_PASS`"",
"-androidKeyaliasPass", $Env:ANDROID_KEYALIAS_PASS, "-androidKeyaliasName", "`"$Env:ANDROID_KEYALIAS_NAME`"",
"-androidTargetSdkVersion", $Env:ANDROID_TARGET_SDK_VERSION, "-androidKeyaliasPass", "`"$Env:ANDROID_KEYALIAS_PASS`"",
"-androidExportType", $Env:ANDROID_EXPORT_TYPE, "-androidTargetSdkVersion", "`"$Env:ANDROID_TARGET_SDK_VERSION`"",
"-androidSymbolType", $Env:ANDROID_SYMBOL_TYPE, "-androidExportType", "`"$Env:ANDROID_EXPORT_TYPE`"",
"-androidSymbolType", "`"$Env:ANDROID_SYMBOL_TYPE`"",
"-logfile", "-" "-logfile", "-"
) + $customParametersArray ) + $customParametersArray
# Remove null items as that will fail the Start-Process call # Remove null items as that will fail the Start-Process call
$unityArgs = $unityArgs | Where-Object { $_ -ne $null } $unityArgs = $unityArgs | Where-Object { $_ -ne $null }
$unityProcess = Start-Process -FilePath "$Env:UNITY_PATH\Editor\Unity.exe" ` $unityProcess = Start-Process -FilePath "$Env:UNITY_PATH/Editor/Unity.exe" `
-ArgumentList $unityArgs ` -ArgumentList $unityArgs `
-PassThru ` -PassThru `
-NoNewWindow -NoNewWindow
# Cache the handle so exit code works properly # Cache the handle so exit code works properly
# https://stackoverflow.com/questions/10262231/obtaining-exitcode-using-start-process-and-waitforexit-instead-of-wait # https://stackoverflow.com/questions/10262231/obtaining-exitcode-using-start-process-and-waitforexit-instead-of-wait

View File

@@ -2,7 +2,7 @@ Get-Process
# 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-Object {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
@@ -14,18 +14,25 @@ Get-Process -Name regsvr32 | ForEach-Object { Stop-Process -Id $_.Id -Force }
. "c:\steps\set_gitcredential.ps1" . "c:\steps\set_gitcredential.ps1"
# Activate Unity # Activate Unity
. "c:\steps\activate.ps1" if ($env:SKIP_ACTIVATION -ne "true") {
. "c:\steps\activate.ps1"
# If we didn't activate successfully, exit with the exit code from the activation step. # If we didn't activate successfully, exit with the exit code from the activation step.
if ($ACTIVATION_EXIT_CODE -ne 0) { if ($ACTIVATION_EXIT_CODE -ne 0) {
exit $ACTIVATION_EXIT_CODE exit $ACTIVATION_EXIT_CODE
}
}
else {
Write-Host "Skipping activation"
} }
# 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" if ($env:SKIP_ACTIVATION -ne "true") {
. "c:\steps\return_license.ps1"
}
Get-Process Get-Process

View File

@@ -6,9 +6,47 @@ Write-Output "# Return License #"
Write-Output "###########################" Write-Output "###########################"
Write-Output "" Write-Output ""
& "$Env:UNITY_PATH\Editor\Unity.exe" -batchmode -quit -nographics ` if (($null -ne ${env:UNITY_SERIAL}) -and ($null -ne ${env:UNITY_EMAIL}) -and ($null -ne ${env:UNITY_PASSWORD}))
-username $Env:UNITY_EMAIL ` {
-password $Env:UNITY_PASSWORD ` #
-returnlicense ` # SERIAL LICENSE MODE
-projectPath "c:/BlankProject" ` #
-logfile - | Out-Host # 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

@@ -1,4 +1,4 @@
if ([string]::IsNullOrEmpty($env:GIT_PRIVATE_TOKEN)) { if ($null -eq ${env:GIT_PRIVATE_TOKEN}) {
Write-Host "GIT_PRIVATE_TOKEN unset skipping" Write-Host "GIT_PRIVATE_TOKEN unset skipping"
} }
else { else {
@@ -8,7 +8,7 @@ else {
git config --global --replace-all "url.https://token:$env:GIT_PRIVATE_TOKEN@github.com/".insteadOf "ssh://git@github.com/" 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 "git@github.com"
git config --global --add "url.https://token:$env:GIT_PRIVATE_TOKEN@github.com/".insteadOf "https://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://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:" git config --global "url.https://git:$env:GIT_PRIVATE_TOKEN@github.com/".insteadOf "git@github.com:"
} }

View File

@@ -7,14 +7,14 @@
"author": "Webber <webber@takken.io>", "author": "Webber <webber@takken.io>",
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
"prepare": "lefthook install && npx husky uninstall -y", "prepare": "lefthook install",
"build": "yarn && tsc && ncc build lib --source-map --license licenses.txt", "build": "yarn && tsc && ncc build lib --source-map --license licenses.txt",
"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 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-tests": "cross-env providerStrategy=aws cloudRunnerTests=true inputPullCommand=\"gcp-secret-manager\" populateOverride=true pullInputList=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 USE_IL2CPP=false inputPullCommand=\"gcp-secret-manager\" yarn ts-node src/index.ts -m cli --populateOverride true --pullInputList UNITY_EMAIL,UNITY_SERIAL,UNITY_PASSWORD",
"aws-secrets-cli": "cross-env 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 inputPullCommand=\"aws-secret-manager\" yarn ts-node src/index.ts -m cli --populateOverride true --pullInputList UNITY_EMAIL,UNITY_SERIAL,UNITY_PASSWORD",
"cli-aws": "cross-env providerStrategy=aws yarn run test-cli", "cli-aws": "cross-env providerStrategy=aws yarn run test-cli",
"cli-k8s": "cross-env providerStrategy=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",
@@ -28,27 +28,28 @@
"node": ">=18.x" "node": ">=18.x"
}, },
"dependencies": { "dependencies": {
"@actions/cache": "^3.1.3", "@actions/cache": "^3.2.4",
"@actions/core": "^1.10.0", "@actions/core": "^1.10.1",
"@actions/exec": "^1.1.0", "@actions/exec": "^1.1.1",
"@actions/github": "^5.0.0", "@actions/github": "^6.0.0",
"@kubernetes/client-node": "^0.16.3", "@kubernetes/client-node": "^0.16.3",
"@octokit/core": "^3.5.1", "@octokit/core": "^5.1.0",
"async-wait-until": "^2.0.12", "async-wait-until": "^2.0.12",
"aws-sdk": "^2.1081.0", "aws-sdk": "^2.1081.0",
"base-64": "^1.0.0", "base-64": "^1.0.0",
"commander": "^9.0.0", "commander": "^9.0.0",
"commander-ts": "^0.2.0", "commander-ts": "^0.2.0",
"kubernetes-client": "^9.0.0", "kubernetes-client": "^9.0.0",
"md5": "^2.3.0",
"nanoid": "^3.3.1", "nanoid": "^3.3.1",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"semver": "^7.5.2", "semver": "^7.5.2",
"ts-md5": "^1.3.1",
"unity-changeset": "^2.0.0", "unity-changeset": "^2.0.0",
"uuid": "^9.0.0", "uuid": "^9.0.0",
"yaml": "^2.2.2" "yaml": "^2.2.2"
}, },
"devDependencies": { "devDependencies": {
"@evilmartians/lefthook": "^1.2.9",
"@types/base-64": "^1.0.0", "@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",
@@ -67,9 +68,10 @@
"jest-circus": "^27.5.1", "jest-circus": "^27.5.1",
"jest-fail-on-console": "^3.0.2", "jest-fail-on-console": "^3.0.2",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"lefthook": "^1.6.1",
"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.8.1",
"typescript": "4.7.4", "typescript": "4.7.4",
"yarn-audit-fix": "^9.3.8" "yarn-audit-fix": "^9.3.8"
}, },

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

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

View File

@@ -34,6 +34,7 @@ async function runMain() {
}); });
} else { } else {
await CloudRunner.run(buildParameters, baseImage.toString()); await CloudRunner.run(buildParameters, baseImage.toString());
exitCode = 0;
} }
// Set output // Set output

View File

@@ -22,6 +22,7 @@ class BuildParameters {
public customImage!: string; public customImage!: string;
public unitySerial!: string; public unitySerial!: string;
public unityLicensingServer!: string; public unityLicensingServer!: string;
public skipActivation!: string;
public runnerTempPath!: string; public runnerTempPath!: string;
public targetPlatform!: string; public targetPlatform!: string;
public projectPath!: string; public projectPath!: string;
@@ -59,7 +60,7 @@ class BuildParameters {
public kubeVolumeSize!: string; public kubeVolumeSize!: string;
public kubeVolume!: string; public kubeVolume!: string;
public kubeStorageClass!: string; public kubeStorageClass!: string;
public runAsHostUser!: String; public runAsHostUser!: string;
public chownFilesTo!: string; public chownFilesTo!: string;
public commandHooks!: string; public commandHooks!: string;
public pullInputList!: string[]; public pullInputList!: string[];
@@ -146,6 +147,7 @@ class BuildParameters {
customImage: Input.customImage, customImage: Input.customImage,
unitySerial, unitySerial,
unityLicensingServer: Input.unityLicensingServer, unityLicensingServer: Input.unityLicensingServer,
skipActivation: Input.skipActivation,
runnerTempPath: Input.runnerTempPath, runnerTempPath: Input.runnerTempPath,
targetPlatform: Input.targetPlatform, targetPlatform: Input.targetPlatform,
projectPath: Input.projectPath, projectPath: Input.projectPath,
@@ -168,7 +170,7 @@ class BuildParameters {
customParameters: Input.customParameters, customParameters: Input.customParameters,
sshAgent: Input.sshAgent, sshAgent: Input.sshAgent,
sshPublicKeysDirectoryPath: Input.sshPublicKeysDirectoryPath, sshPublicKeysDirectoryPath: Input.sshPublicKeysDirectoryPath,
gitPrivateToken: Input.gitPrivateToken || (await GithubCliReader.GetGitHubAuthToken()), gitPrivateToken: Input.gitPrivateToken ?? (await GithubCliReader.GetGitHubAuthToken()),
runAsHostUser: Input.runAsHostUser, runAsHostUser: Input.runAsHostUser,
chownFilesTo: Input.chownFilesTo, chownFilesTo: Input.chownFilesTo,
dockerCpuLimit: Input.dockerCpuLimit, dockerCpuLimit: Input.dockerCpuLimit,
@@ -190,7 +192,7 @@ class BuildParameters {
branch: Input.branch.replace('/head', '') || (await GitRepoReader.GetBranch()), branch: Input.branch.replace('/head', '') || (await GitRepoReader.GetBranch()),
cloudRunnerBranch: CloudRunnerOptions.cloudRunnerBranch.split('/').reverse()[0], cloudRunnerBranch: CloudRunnerOptions.cloudRunnerBranch.split('/').reverse()[0],
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.awsStackName, awsStackName: CloudRunnerOptions.awsStackName,
gitSha: Input.gitSha, gitSha: Input.gitSha,

View File

@@ -10,8 +10,6 @@ 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/options/cloud-runner-options-reader'; import CloudRunnerOptionsReader from '../cloud-runner/options/cloud-runner-options-reader';
import GitHub from '../github'; import GitHub from '../github';
import { CloudRunnerFolders } from '../cloud-runner/options/cloud-runner-folders';
import { CloudRunnerSystem } from '../cloud-runner/services/core/cloud-runner-system';
import { OptionValues } from 'commander'; import { OptionValues } from 'commander';
import { InputKey } from '../input'; import { InputKey } from '../input';
@@ -54,6 +52,7 @@ export class Cli {
program.option('--cachePushTo <cachePushTo>', 'cache push to caching folder'); program.option('--cachePushTo <cachePushTo>', 'cache push to caching folder');
program.option('--artifactName <artifactName>', 'caching artifact name'); program.option('--artifactName <artifactName>', 'caching artifact name');
program.option('--select <select>', 'select a particular resource'); program.option('--select <select>', 'select a particular resource');
program.option('--logFile <logFile>', 'output to log file (log stream only)');
program.parse(process.argv); program.parse(process.argv);
Cli.options = program.opts(); Cli.options = program.opts();
@@ -110,7 +109,7 @@ export class Cli {
const buildParameter = await BuildParameters.create(); const buildParameter = await BuildParameters.create();
const baseImage = new ImageTag(buildParameter); const baseImage = new ImageTag(buildParameter);
return await CloudRunner.run(buildParameter, baseImage.toString()); return (await CloudRunner.run(buildParameter, baseImage.toString())).BuildResults;
} }
@CliFunction(`async-workflow`, `runs a cloud runner build`) @CliFunction(`async-workflow`, `runs a cloud runner build`)
@@ -119,7 +118,7 @@ export class Cli {
const baseImage = new ImageTag(buildParameter); const baseImage = new ImageTag(buildParameter);
await CloudRunner.setup(buildParameter); await CloudRunner.setup(buildParameter);
return await CloudRunner.run(buildParameter, baseImage.toString()); return (await CloudRunner.run(buildParameter, baseImage.toString())).BuildResults;
} }
@CliFunction(`checks-update`, `runs a cloud runner build`) @CliFunction(`checks-update`, `runs a cloud runner build`)
@@ -173,31 +172,4 @@ export class Cli {
return await CloudRunner.Provider.watchWorkflow(); return await CloudRunner.Provider.watchWorkflow();
} }
@CliFunction(`remote-cli-post-build`, `runs a cloud runner build`)
public static async PostCLIBuild(): Promise<string> {
core.info(`Running POST build tasks`);
await Caching.PushToCache(
CloudRunnerFolders.ToLinuxFolder(`${CloudRunnerFolders.cacheFolderForCacheKeyFull}/Library`),
CloudRunnerFolders.ToLinuxFolder(CloudRunnerFolders.libraryFolderAbsolute),
`lib-${CloudRunner.buildParameters.buildGuid}`,
);
await Caching.PushToCache(
CloudRunnerFolders.ToLinuxFolder(`${CloudRunnerFolders.cacheFolderForCacheKeyFull}/build`),
CloudRunnerFolders.ToLinuxFolder(CloudRunnerFolders.projectBuildFolderAbsolute),
`build-${CloudRunner.buildParameters.buildGuid}`,
);
if (!BuildParameters.shouldUseRetainedWorkspaceMode(CloudRunner.buildParameters)) {
await CloudRunnerSystem.Run(
`rm -r ${CloudRunnerFolders.ToLinuxFolder(CloudRunnerFolders.uniqueCloudRunnerJobFolderAbsolute)}`,
);
}
await RemoteClient.runCustomHookFiles(`after-build`);
return new Promise((result) => result(``));
}
} }

View File

@@ -16,6 +16,7 @@ import LocalDockerCloudRunner from './providers/docker';
import GitHub from '../github'; import GitHub from '../github';
import SharedWorkspaceLocking from './services/core/shared-workspace-locking'; import SharedWorkspaceLocking from './services/core/shared-workspace-locking';
import { FollowLogStreamService } from './services/core/follow-log-stream-service'; import { FollowLogStreamService } from './services/core/follow-log-stream-service';
import CloudRunnerResult from './services/core/cloud-runner-result';
class CloudRunner { class CloudRunner {
public static Provider: ProviderInterface; public static Provider: ProviderInterface;
@@ -83,15 +84,16 @@ class CloudRunner {
} }
static async run(buildParameters: BuildParameters, baseImage: string) { static async run(buildParameters: BuildParameters, baseImage: string) {
if (baseImage.includes(`undefined`)) {
throw new Error(`baseImage is undefined`);
}
await CloudRunner.setup(buildParameters); await CloudRunner.setup(buildParameters);
if (!CloudRunner.buildParameters.isCliMode) core.startGroup('Setup shared cloud runner resources');
await CloudRunner.Provider.setupWorkflow( await CloudRunner.Provider.setupWorkflow(
CloudRunner.buildParameters.buildGuid, CloudRunner.buildParameters.buildGuid,
CloudRunner.buildParameters, CloudRunner.buildParameters,
CloudRunner.buildParameters.branch, CloudRunner.buildParameters.branch,
CloudRunner.defaultSecrets, CloudRunner.defaultSecrets,
); );
if (!CloudRunner.buildParameters.isCliMode) core.endGroup();
try { try {
if (buildParameters.maxRetainedWorkspaces > 0) { if (buildParameters.maxRetainedWorkspaces > 0) {
CloudRunner.lockedWorkspace = SharedWorkspaceLocking.NewWorkspaceName(); CloudRunner.lockedWorkspace = SharedWorkspaceLocking.NewWorkspaceName();
@@ -114,11 +116,7 @@ class CloudRunner {
CloudRunner.lockedWorkspace = ``; CloudRunner.lockedWorkspace = ``;
} }
} }
const content = { ...CloudRunner.buildParameters }; await CloudRunner.updateStatusWithBuildParameters();
content.gitPrivateToken = ``;
content.unitySerial = ``;
const jsonContent = JSON.stringify(content, undefined, 4);
await GitHub.updateGitHubCheck(jsonContent, CloudRunner.buildParameters.buildGuid);
const output = await new WorkflowCompositionRoot().run( const output = await new WorkflowCompositionRoot().run(
new CloudRunnerStepParameters( new CloudRunnerStepParameters(
baseImage, baseImage,
@@ -126,16 +124,15 @@ class CloudRunner {
CloudRunner.defaultSecrets, CloudRunner.defaultSecrets,
), ),
); );
if (!CloudRunner.buildParameters.isCliMode) core.startGroup('Cleanup shared cloud runner resources');
await CloudRunner.Provider.cleanupWorkflow( await CloudRunner.Provider.cleanupWorkflow(
CloudRunner.buildParameters.buildGuid,
CloudRunner.buildParameters, CloudRunner.buildParameters,
CloudRunner.buildParameters.branch, CloudRunner.buildParameters.branch,
CloudRunner.defaultSecrets, CloudRunner.defaultSecrets,
); );
CloudRunnerLogger.log(`Cleanup complete`);
if (!CloudRunner.buildParameters.isCliMode) core.endGroup(); if (!CloudRunner.buildParameters.isCliMode) core.endGroup();
await GitHub.updateGitHubCheck(CloudRunner.buildParameters.buildGuid, `success`, `success`, `completed`); if (buildParameters.asyncWorkflow && this.isCloudRunnerEnvironment && this.isCloudRunnerAsyncEnvironment) {
await GitHub.updateGitHubCheck(CloudRunner.buildParameters.buildGuid, `success`, `success`, `completed`);
}
if (BuildParameters.shouldUseRetainedWorkspaceMode(buildParameters)) { if (BuildParameters.shouldUseRetainedWorkspaceMode(buildParameters)) {
const workspace = CloudRunner.lockedWorkspace || ``; const workspace = CloudRunner.lockedWorkspace || ``;
@@ -162,7 +159,7 @@ class CloudRunner {
CloudRunner.Provider.garbageCollect(``, true, buildParameters.garbageMaxAge, true, true); CloudRunner.Provider.garbageCollect(``, true, buildParameters.garbageMaxAge, true, true);
} }
return output; return new CloudRunnerResult(buildParameters, output, true, true, false);
} catch (error: any) { } catch (error: any) {
CloudRunnerLogger.log(JSON.stringify(error, undefined, 4)); CloudRunnerLogger.log(JSON.stringify(error, undefined, 4));
await GitHub.updateGitHubCheck( await GitHub.updateGitHubCheck(
@@ -176,5 +173,15 @@ class CloudRunner {
throw error; throw error;
} }
} }
private static async updateStatusWithBuildParameters() {
const content = { ...CloudRunner.buildParameters };
content.gitPrivateToken = ``;
content.unitySerial = ``;
content.unityEmail = ``;
content.unityPassword = ``;
const jsonContent = JSON.stringify(content, undefined, 4);
await GitHub.updateGitHubCheck(jsonContent, CloudRunner.buildParameters.buildGuid);
}
} }
export default CloudRunner; export default CloudRunner;

View File

@@ -9,12 +9,7 @@ export class CloudRunnerError {
CloudRunnerLogger.error(JSON.stringify(error, undefined, 4)); CloudRunnerLogger.error(JSON.stringify(error, undefined, 4));
core.setFailed('Cloud Runner failed'); core.setFailed('Cloud Runner failed');
if (CloudRunner.Provider !== undefined) { if (CloudRunner.Provider !== undefined) {
await CloudRunner.Provider.cleanupWorkflow( await CloudRunner.Provider.cleanupWorkflow(buildParameters, buildParameters.branch, secrets);
buildParameters.buildGuid,
buildParameters,
buildParameters.branch,
secrets,
);
} }
} }
} }

View File

@@ -103,14 +103,14 @@ class CloudRunnerOptions {
static get buildPlatform(): string { static get buildPlatform(): string {
const input = CloudRunnerOptions.getInput('buildPlatform'); const input = CloudRunnerOptions.getInput('buildPlatform');
if (input) { if (input && input !== '') {
return input; return input;
} }
if (CloudRunnerOptions.providerStrategy !== 'local') { if (CloudRunnerOptions.providerStrategy !== 'local') {
return 'linux'; return 'linux';
} }
return ``; return process.platform;
} }
static get cloudRunnerBranch(): string { static get cloudRunnerBranch(): string {

View File

@@ -1,6 +1,9 @@
import CloudRunner from '../../../cloud-runner';
export class TaskDefinitionFormation { export class TaskDefinitionFormation {
public static readonly description: string = `Game CI Cloud Runner Task Stack`; public static readonly description: string = `Game CI Cloud Runner Task Stack`;
public static readonly formation: string = `AWSTemplateFormatVersion: 2010-09-09 public static get formation(): string {
return `AWSTemplateFormatVersion: 2010-09-09
Description: ${TaskDefinitionFormation.description} Description: ${TaskDefinitionFormation.description}
Parameters: Parameters:
EnvironmentName: EnvironmentName:
@@ -26,11 +29,11 @@ Parameters:
Default: 80 Default: 80
Description: What port number the application inside the docker container is binding to Description: What port number the application inside the docker container is binding to
ContainerCpu: ContainerCpu:
Default: 1024 Default: ${CloudRunner.buildParameters.containerCpu}
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: 4096 Default: ${CloudRunner.buildParameters.containerMemory}
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:
@@ -92,7 +95,7 @@ Resources:
EFSVolumeConfiguration: EFSVolumeConfiguration:
FilesystemId: FilesystemId:
'Fn::ImportValue': !Sub '${'${EnvironmentName}'}:EfsFileStorageId' 'Fn::ImportValue': !Sub '${'${EnvironmentName}'}:EfsFileStorageId'
TransitEncryption: ENABLED TransitEncryption: DISABLED
RequiresCompatibilities: RequiresCompatibilities:
- FARGATE - FARGATE
ExecutionRoleArn: ExecutionRoleArn:
@@ -135,6 +138,7 @@ Resources:
DependsOn: DependsOn:
- LogGroup - LogGroup
`; `;
}
public static streamLogs = ` public static streamLogs = `
SubscriptionFilter: SubscriptionFilter:
Type: 'AWS::Logs::SubscriptionFilter' Type: 'AWS::Logs::SubscriptionFilter'

View File

@@ -57,8 +57,6 @@ class AWSBuildEnvironment implements ProviderInterface {
} }
async cleanupWorkflow( async cleanupWorkflow(
// eslint-disable-next-line no-unused-vars
buildGuid: string,
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
buildParameters: BuildParameters, buildParameters: BuildParameters,
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars

View File

@@ -41,7 +41,6 @@ class LocalDockerCloudRunner implements ProviderInterface {
return new Promise((result) => result(``)); return new Promise((result) => result(``));
} }
async cleanupWorkflow( async cleanupWorkflow(
buildGuid: string,
buildParameters: BuildParameters, buildParameters: BuildParameters,
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
branchName: string, branchName: string,

View File

@@ -14,12 +14,17 @@ 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 { RemoteClientLogger } from '../../remote-client/remote-client-logger';
import { KubernetesRole } from './kubernetes-role';
import { CloudRunnerSystem } from '../../services/core/cloud-runner-system';
class Kubernetes implements ProviderInterface { class Kubernetes implements ProviderInterface {
public static Instance: Kubernetes; public static Instance: Kubernetes;
public kubeConfig!: k8s.KubeConfig; public kubeConfig!: k8s.KubeConfig;
public kubeClient!: k8s.CoreV1Api; public kubeClient!: k8s.CoreV1Api;
public kubeClientApps!: k8s.AppsV1Api;
public kubeClientBatch!: k8s.BatchV1Api; public kubeClientBatch!: k8s.BatchV1Api;
public rbacAuthorizationV1Api!: k8s.RbacAuthorizationV1Api;
public buildGuid: string = ''; public buildGuid: string = '';
public buildParameters!: BuildParameters; public buildParameters!: BuildParameters;
public pvcName: string = ''; public pvcName: string = '';
@@ -30,6 +35,7 @@ class Kubernetes implements ProviderInterface {
public containerName: string = ''; public containerName: string = '';
public cleanupCronJobName: string = ''; public cleanupCronJobName: string = '';
public serviceAccountName: string = ''; public serviceAccountName: string = '';
public ip: string = '';
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
constructor(buildParameters: BuildParameters) { constructor(buildParameters: BuildParameters) {
@@ -37,11 +43,30 @@ class Kubernetes implements ProviderInterface {
this.kubeConfig = new k8s.KubeConfig(); this.kubeConfig = new k8s.KubeConfig();
this.kubeConfig.loadFromDefault(); this.kubeConfig.loadFromDefault();
this.kubeClient = this.kubeConfig.makeApiClient(k8s.CoreV1Api); this.kubeClient = this.kubeConfig.makeApiClient(k8s.CoreV1Api);
this.kubeClientApps = this.kubeConfig.makeApiClient(k8s.AppsV1Api);
this.kubeClientBatch = this.kubeConfig.makeApiClient(k8s.BatchV1Api); this.kubeClientBatch = this.kubeConfig.makeApiClient(k8s.BatchV1Api);
this.rbacAuthorizationV1Api = this.kubeConfig.makeApiClient(k8s.RbacAuthorizationV1Api);
this.namespace = 'default'; this.namespace = 'default';
CloudRunnerLogger.log('Loaded default Kubernetes configuration for this environment'); CloudRunnerLogger.log('Loaded default Kubernetes configuration for this environment');
} }
async PushLogUpdate(logs: string) {
// push logs to nginx file server via 'LOG_SERVICE_IP' env var
const ip = process.env[`LOG_SERVICE_IP`];
if (ip === undefined) {
RemoteClientLogger.logWarning(`LOG_SERVICE_IP not set, skipping log push`);
return;
}
const url = `http://${ip}/api/log`;
RemoteClientLogger.log(`Pushing logs to ${url}`);
// logs to base64
logs = Buffer.from(logs).toString('base64');
const response = await CloudRunnerSystem.Run(`curl -X POST -d "${logs}" ${url}`, false, true);
RemoteClientLogger.log(`Pushed logs to ${url} ${response}`);
}
async listResources(): Promise<ProviderResource[]> { async listResources(): Promise<ProviderResource[]> {
const pods = await this.kubeClient.listNamespacedPod(this.namespace); const pods = await this.kubeClient.listNamespacedPod(this.namespace);
const serviceAccounts = await this.kubeClient.listNamespacedServiceAccount(this.namespace); const serviceAccounts = await this.kubeClient.listNamespacedServiceAccount(this.namespace);
@@ -115,9 +140,10 @@ class Kubernetes implements ProviderInterface {
CloudRunnerLogger.log('Cloud Runner K8s workflow!'); CloudRunnerLogger.log('Cloud Runner K8s workflow!');
// Setup // Setup
const id = BuildParameters.shouldUseRetainedWorkspaceMode(this.buildParameters) const id =
? CloudRunner.lockedWorkspace BuildParameters && BuildParameters.shouldUseRetainedWorkspaceMode(this.buildParameters)
: this.buildParameters.buildGuid; ? CloudRunner.lockedWorkspace
: this.buildParameters.buildGuid;
this.pvcName = `unity-builder-pvc-${id}`; this.pvcName = `unity-builder-pvc-${id}`;
await KubernetesStorage.createPersistentVolumeClaim( await KubernetesStorage.createPersistentVolumeClaim(
this.buildParameters, this.buildParameters,
@@ -137,10 +163,7 @@ class Kubernetes implements ProviderInterface {
CloudRunnerLogger.log('Watching pod until running'); CloudRunnerLogger.log('Watching pod until running');
await KubernetesTaskRunner.watchUntilPodRunning(this.kubeClient, this.podName, this.namespace); await KubernetesTaskRunner.watchUntilPodRunning(this.kubeClient, this.podName, this.namespace);
CloudRunnerLogger.log('Pod running, streaming logs'); CloudRunnerLogger.log('Pod is running');
CloudRunnerLogger.log(
`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}`,
);
output += await KubernetesTaskRunner.runTask( output += await KubernetesTaskRunner.runTask(
this.kubeConfig, this.kubeConfig,
this.kubeClient, this.kubeClient,
@@ -232,8 +255,12 @@ class Kubernetes implements ProviderInterface {
this.jobName, this.jobName,
k8s, k8s,
this.containerName, this.containerName,
this.ip,
); );
await new Promise((promise) => setTimeout(promise, 15000)); await new Promise((promise) => setTimeout(promise, 15000));
// await KubernetesRole.createRole(this.serviceAccountName, this.namespace, this.rbacAuthorizationV1Api);
const result = 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));
@@ -257,6 +284,7 @@ class Kubernetes implements ProviderInterface {
try { try {
await this.kubeClientBatch.deleteNamespacedJob(this.jobName, this.namespace); await this.kubeClientBatch.deleteNamespacedJob(this.jobName, this.namespace);
await this.kubeClient.deleteNamespacedPod(this.podName, this.namespace); await this.kubeClient.deleteNamespacedPod(this.podName, this.namespace);
await KubernetesRole.deleteRole(this.serviceAccountName, this.namespace, this.rbacAuthorizationV1Api);
} catch (error: any) { } catch (error: any) {
CloudRunnerLogger.log(`Failed to cleanup`); CloudRunnerLogger.log(`Failed to cleanup`);
if (error.response.body.reason !== `NotFound`) { if (error.response.body.reason !== `NotFound`) {
@@ -275,14 +303,13 @@ class Kubernetes implements ProviderInterface {
} }
async cleanupWorkflow( async cleanupWorkflow(
buildGuid: string,
buildParameters: BuildParameters, buildParameters: BuildParameters,
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
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 }[],
) { ) {
if (BuildParameters.shouldUseRetainedWorkspaceMode(buildParameters)) { if (BuildParameters && BuildParameters.shouldUseRetainedWorkspaceMode(buildParameters)) {
return; return;
} }
CloudRunnerLogger.log(`deleting PVC`); CloudRunnerLogger.log(`deleting PVC`);

View File

@@ -20,6 +20,7 @@ class KubernetesJobSpecFactory {
jobName: string, jobName: string,
k8s: any, k8s: any,
containerName: string, containerName: string,
ip: string = '',
) { ) {
const job = new k8s.V1Job(); const job = new k8s.V1Job();
job.apiVersion = 'batch/v1'; job.apiVersion = 'batch/v1';
@@ -81,6 +82,7 @@ class KubernetesJobSpecFactory {
return environmentVariable; return environmentVariable;
}), }),
{ name: 'LOG_SERVICE_IP', value: ip },
], ],
volumeMounts: [ volumeMounts: [
{ {
@@ -92,9 +94,8 @@ class KubernetesJobSpecFactory {
preStop: { preStop: {
exec: { exec: {
command: [ command: [
'bin/bash', `wait 60s;
'-c', cd /data/builder/action/steps;
`cd /data/builder/action/steps;
chmod +x /return_license.sh; chmod +x /return_license.sh;
/return_license.sh;`, /return_license.sh;`,
], ],
@@ -108,6 +109,16 @@ class KubernetesJobSpecFactory {
}, },
}; };
if (process.env['CLOUD_RUNNER_MINIKUBE']) {
job.spec.template.spec.volumes[0] = {
name: 'build-mount',
hostPath: {
path: `/data`,
type: `Directory`,
},
};
}
job.spec.template.spec.containers[0].resources.requests[`ephemeral-storage`] = '10Gi'; job.spec.template.spec.containers[0].resources.requests[`ephemeral-storage`] = '10Gi';
return job; return job;

View File

@@ -0,0 +1,53 @@
import { RbacAuthorizationV1Api } from '@kubernetes/client-node';
class KubernetesRole {
static async createRole(serviceAccountName: string, namespace: string, rbac: RbacAuthorizationV1Api) {
// create admin kubernetes role and role binding
const roleBinding = {
apiVersion: 'rbac.authorization.k8s.io/v1',
kind: 'RoleBinding',
metadata: {
name: `${serviceAccountName}-admin`,
namespace,
},
subjects: [
{
kind: 'ServiceAccount',
name: serviceAccountName,
namespace,
},
],
roleRef: {
apiGroup: 'rbac.authorization.k8s.io',
kind: 'Role',
name: `${serviceAccountName}-admin`,
},
};
const role = {
apiVersion: 'rbac.authorization.k8s.io/v1',
kind: 'Role',
metadata: {
name: `${serviceAccountName}-admin`,
namespace,
},
rules: [
{
apiGroups: ['*'],
resources: ['*'],
verbs: ['*'],
},
],
};
const roleBindingResponse = await rbac.createNamespacedRoleBinding(namespace, roleBinding);
const roleResponse = await rbac.createNamespacedRole(namespace, role);
return { roleBindingResponse, roleResponse };
}
public static async deleteRole(serviceAccountName: string, namespace: string, rbac: RbacAuthorizationV1Api) {
await rbac.deleteNamespacedRoleBinding(`${serviceAccountName}-admin`, namespace);
await rbac.deleteNamespacedRole(`${serviceAccountName}-admin`, namespace);
}
}
export { KubernetesRole };

View File

@@ -9,7 +9,7 @@ class KubernetesServiceAccount {
serviceAccount.metadata = { serviceAccount.metadata = {
name: serviceAccountName, name: serviceAccountName,
}; };
serviceAccount.automountServiceAccountToken = false; serviceAccount.automountServiceAccountToken = true;
return kubeClient.createNamespacedServiceAccount(namespace, serviceAccount); return kubeClient.createNamespacedServiceAccount(namespace, serviceAccount);
} }

View File

@@ -7,7 +7,6 @@ import KubernetesPods from './kubernetes-pods';
import { FollowLogStreamService } from '../../services/core/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 readonly maxRetry: number = 3;
static lastReceivedMessage: string = ``; static lastReceivedMessage: string = ``;
@@ -22,38 +21,33 @@ class KubernetesTaskRunner {
let output = ''; let output = '';
let shouldReadLogs = true; let shouldReadLogs = true;
let shouldCleanup = true; let shouldCleanup = true;
let sinceTime = ``;
let retriesAfterFinish = 0; let retriesAfterFinish = 0;
// eslint-disable-next-line no-constant-condition // eslint-disable-next-line no-constant-condition
while (true) { while (true) {
await new Promise((resolve) => setTimeout(resolve, 3000)); await new Promise((resolve) => setTimeout(resolve, 3000));
const lastReceivedMessage =
KubernetesTaskRunner.lastReceivedTimestamp > 0
? `\nLast Log Message "${this.lastReceivedMessage}" ${this.lastReceivedTimestamp}`
: ``;
CloudRunnerLogger.log( CloudRunnerLogger.log(
`Streaming logs from pod: ${podName} container: ${containerName} namespace: ${namespace} ${CloudRunner.buildParameters.kubeVolumeSize}/${CloudRunner.buildParameters.containerCpu}/${CloudRunner.buildParameters.containerMemory}\n${lastReceivedMessage}`, `Streaming logs from pod: ${podName} container: ${containerName} namespace: ${namespace} ${CloudRunner.buildParameters.kubeVolumeSize}/${CloudRunner.buildParameters.containerCpu}/${CloudRunner.buildParameters.containerMemory}`,
); );
if (KubernetesTaskRunner.lastReceivedTimestamp > 0) {
const currentDate = new Date(KubernetesTaskRunner.lastReceivedTimestamp);
const dateTimeIsoString = currentDate.toISOString();
sinceTime = ` --since-time="${dateTimeIsoString}"`;
}
let extraFlags = ``; let extraFlags = ``;
extraFlags += (await KubernetesPods.IsPodRunning(podName, namespace, kubeClient)) extraFlags += (await KubernetesPods.IsPodRunning(podName, namespace, kubeClient))
? ` -f -c ${containerName}` ? ` -f -c ${containerName}`
: ` --previous`; : ` --previous`;
let lastMessageSeenIncludedInChunk = false;
let lastMessageSeen = false;
let logs; const callback = (outputChunk: string) => {
output += outputChunk;
// split output chunk and handle per line
for (const chunk of outputChunk.split(`\n`)) {
({ shouldReadLogs, shouldCleanup, output } = FollowLogStreamService.handleIteration(
chunk,
shouldReadLogs,
shouldCleanup,
output,
));
}
};
try { try {
logs = await CloudRunnerSystem.Run( await CloudRunnerSystem.Run(`kubectl logs ${podName}${extraFlags}`, false, true, callback);
`kubectl logs ${podName}${extraFlags} --timestamps${sinceTime}`,
false,
true,
);
} catch (error: any) { } catch (error: any) {
await new Promise((resolve) => setTimeout(resolve, 3000)); await new Promise((resolve) => setTimeout(resolve, 3000));
const continueStreaming = await KubernetesPods.IsPodRunning(podName, namespace, kubeClient); const continueStreaming = await KubernetesPods.IsPodRunning(podName, namespace, kubeClient);
@@ -68,34 +62,6 @@ class KubernetesTaskRunner {
} }
throw error; throw error;
} }
const splitLogs = logs.split(`\n`);
for (const chunk of splitLogs) {
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) { if (FollowLogStreamService.DidReceiveEndOfTransmission) {
CloudRunnerLogger.log('end of log stream'); CloudRunnerLogger.log('end of log stream');
break; break;
@@ -106,14 +72,14 @@ class KubernetesTaskRunner {
} }
static async watchUntilPodRunning(kubeClient: CoreV1Api, podName: string, namespace: string) { static async watchUntilPodRunning(kubeClient: CoreV1Api, podName: string, namespace: string) {
let success: boolean = false; let waitComplete: boolean = false;
let message = ``; 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'; waitComplete = phase !== 'Pending';
message = `Phase:${status.body.status?.phase} \n Reason:${ message = `Phase:${status.body.status?.phase} \n Reason:${
status.body.status?.conditions?.[0].reason || '' status.body.status?.conditions?.[0].reason || ''
} \n Message:${status.body.status?.conditions?.[0].message || ''}`; } \n Message:${status.body.status?.conditions?.[0].message || ''}`;
@@ -133,7 +99,7 @@ class KubernetesTaskRunner {
// 4, // 4,
// ), // ),
// ); // );
if (success || phase !== 'Pending') return true; if (waitComplete || phase !== 'Pending') return true;
return false; return false;
}, },
@@ -142,11 +108,11 @@ class KubernetesTaskRunner {
intervalBetweenAttempts: 15000, intervalBetweenAttempts: 15000,
}, },
); );
if (!success) { if (!waitComplete) {
CloudRunnerLogger.log(message); CloudRunnerLogger.log(message);
} }
return success; return waitComplete;
} }
} }

View File

@@ -32,8 +32,6 @@ class LocalCloudRunner implements ProviderInterface {
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }
cleanupWorkflow( cleanupWorkflow(
// eslint-disable-next-line no-unused-vars
buildGuid: string,
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
buildParameters: BuildParameters, buildParameters: BuildParameters,
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars

View File

@@ -6,8 +6,6 @@ import { ProviderWorkflow } from './provider-workflow';
export interface ProviderInterface { export interface ProviderInterface {
cleanupWorkflow( cleanupWorkflow(
// eslint-disable-next-line no-unused-vars
buildGuid: string,
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
buildParameters: BuildParameters, buildParameters: BuildParameters,
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars

View File

@@ -25,8 +25,6 @@ class TestCloudRunner implements ProviderInterface {
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }
cleanupWorkflow( cleanupWorkflow(
// eslint-disable-next-line no-unused-vars
buildGuid: string,
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
buildParameters: BuildParameters, buildParameters: BuildParameters,
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars

View File

@@ -80,7 +80,7 @@ export class Caching {
} }
await CloudRunnerSystem.Run( await CloudRunnerSystem.Run(
`tar -cf ${cacheArtifactName}.tar${compressionSuffix} ${path.basename(sourceFolder)}`, `tar -cf ${cacheArtifactName}.tar${compressionSuffix} "${path.basename(sourceFolder)}"`,
); );
await CloudRunnerSystem.Run(`du ${cacheArtifactName}.tar${compressionSuffix}`); await CloudRunnerSystem.Run(`du ${cacheArtifactName}.tar${compressionSuffix}`);
assert(await fileExists(`${cacheArtifactName}.tar${compressionSuffix}`), 'cache archive exists'); assert(await fileExists(`${cacheArtifactName}.tar${compressionSuffix}`), 'cache archive exists');

View File

@@ -12,16 +12,83 @@ import { CloudRunnerSystem } from '../services/core/cloud-runner-system';
import YAML from 'yaml'; import YAML from 'yaml';
import GitHub from '../../github'; import GitHub from '../../github';
import BuildParameters from '../../build-parameters'; import BuildParameters from '../../build-parameters';
import { Cli } from '../../cli/cli';
import CloudRunnerOptions from '../options/cloud-runner-options';
export class RemoteClient { export class RemoteClient {
@CliFunction(`remote-cli-pre-build`, `sets up a repository, usually before a game-ci build`) @CliFunction(`remote-cli-pre-build`, `sets up a repository, usually before a game-ci build`)
static async runRemoteClientJob() { static async setupRemoteClient() {
CloudRunnerLogger.log(`bootstrap game ci cloud runner...`); CloudRunnerLogger.log(`bootstrap game ci cloud runner...`);
if (!(await RemoteClient.handleRetainedWorkspace())) { if (!(await RemoteClient.handleRetainedWorkspace())) {
await RemoteClient.bootstrapRepository(); await RemoteClient.bootstrapRepository();
} }
await RemoteClient.replaceLargePackageReferencesWithSharedReferences();
await RemoteClient.runCustomHookFiles(`before-build`); await RemoteClient.runCustomHookFiles(`before-build`);
} }
@CliFunction('remote-cli-log-stream', `log stream from standard input`)
public static async remoteClientLogStream() {
const logFile = Cli.options!['logFile'];
process.stdin.resume();
process.stdin.setEncoding('utf8');
let lingeringLine = '';
process.stdin.on('data', (chunk) => {
const lines = chunk.toString().split('\n');
lines[0] = lingeringLine + lines[0];
lingeringLine = lines.pop() || '';
for (const element of lines) {
if (CloudRunnerOptions.providerStrategy !== 'k8s') {
CloudRunnerLogger.log(element);
} else {
fs.appendFileSync(logFile, element);
CloudRunnerLogger.log(element);
}
}
});
process.stdin.on('end', () => {
if (CloudRunnerOptions.providerStrategy !== 'k8s') {
CloudRunnerLogger.log(lingeringLine);
} else {
fs.appendFileSync(logFile, lingeringLine);
CloudRunnerLogger.log(lingeringLine);
}
});
}
@CliFunction(`remote-cli-post-build`, `runs a cloud runner build`)
public static async remoteClientPostBuild(): Promise<string> {
RemoteClientLogger.log(`Running POST build tasks`);
await Caching.PushToCache(
CloudRunnerFolders.ToLinuxFolder(`${CloudRunnerFolders.cacheFolderForCacheKeyFull}/Library`),
CloudRunnerFolders.ToLinuxFolder(CloudRunnerFolders.libraryFolderAbsolute),
`lib-${CloudRunner.buildParameters.buildGuid}`,
);
await Caching.PushToCache(
CloudRunnerFolders.ToLinuxFolder(`${CloudRunnerFolders.cacheFolderForCacheKeyFull}/build`),
CloudRunnerFolders.ToLinuxFolder(CloudRunnerFolders.projectBuildFolderAbsolute),
`build-${CloudRunner.buildParameters.buildGuid}`,
);
if (!BuildParameters.shouldUseRetainedWorkspaceMode(CloudRunner.buildParameters)) {
await CloudRunnerSystem.Run(
`rm -r ${CloudRunnerFolders.ToLinuxFolder(CloudRunnerFolders.uniqueCloudRunnerJobFolderAbsolute)}`,
);
}
await RemoteClient.runCustomHookFiles(`after-build`);
// WIP - need to give the pod permissions to create config map
await RemoteClientLogger.handleLogManagementPostJob();
return new Promise((result) => result(``));
}
static async runCustomHookFiles(hookLifecycle: string) { static async runCustomHookFiles(hookLifecycle: string) {
RemoteClientLogger.log(`RunCustomHookFiles: ${hookLifecycle}`); RemoteClientLogger.log(`RunCustomHookFiles: ${hookLifecycle}`);
const gameCiCustomHooksPath = path.join(CloudRunnerFolders.repoPathAbsolute, `game-ci`, `hooks`); const gameCiCustomHooksPath = path.join(CloudRunnerFolders.repoPathAbsolute, `game-ci`, `hooks`);
@@ -47,7 +114,6 @@ export class RemoteClient {
`mkdir -p ${CloudRunnerFolders.ToLinuxFolder(CloudRunnerFolders.cacheFolderForCacheKeyFull)}`, `mkdir -p ${CloudRunnerFolders.ToLinuxFolder(CloudRunnerFolders.cacheFolderForCacheKeyFull)}`,
); );
await RemoteClient.cloneRepoWithoutLFSFiles(); await RemoteClient.cloneRepoWithoutLFSFiles();
await RemoteClient.replaceLargePackageReferencesWithSharedReferences();
await RemoteClient.sizeOfFolder( await RemoteClient.sizeOfFolder(
'repo before lfs cache pull', 'repo before lfs cache pull',
CloudRunnerFolders.ToLinuxFolder(CloudRunnerFolders.repoPathAbsolute), CloudRunnerFolders.ToLinuxFolder(CloudRunnerFolders.repoPathAbsolute),

View File

@@ -1,8 +1,18 @@
import CloudRunnerLogger from '../services/core/cloud-runner-logger'; import CloudRunnerLogger from '../services/core/cloud-runner-logger';
import fs from 'node:fs';
import path from 'node:path';
import CloudRunner from '../cloud-runner';
import CloudRunnerOptions from '../options/cloud-runner-options';
export class RemoteClientLogger { export class RemoteClientLogger {
private static get LogFilePath() {
return path.join(`/home`, `job-log.txt`);
}
public static log(message: string) { public static log(message: string) {
CloudRunnerLogger.log(`[Client] ${message}`); const finalMessage = `[Client] ${message}`;
this.appendToFile(finalMessage);
CloudRunnerLogger.log(finalMessage);
} }
public static logCliError(message: string) { public static logCliError(message: string) {
@@ -16,4 +26,57 @@ export class RemoteClientLogger {
public static logWarning(message: string) { public static logWarning(message: string) {
CloudRunnerLogger.logWarning(message); CloudRunnerLogger.logWarning(message);
} }
public static appendToFile(message: string) {
if (CloudRunner.isCloudRunnerEnvironment) {
fs.appendFileSync(RemoteClientLogger.LogFilePath, `${message}\n`);
}
}
public static async handleLogManagementPostJob() {
if (CloudRunnerOptions.providerStrategy !== 'k8s') {
return;
}
CloudRunnerLogger.log(`Collected Logs`);
// check for log file not existing
if (!fs.existsSync(RemoteClientLogger.LogFilePath)) {
CloudRunnerLogger.log(`Log file does not exist`);
// check if CloudRunner.isCloudRunnerEnvironment is true, log
if (!CloudRunner.isCloudRunnerEnvironment) {
CloudRunnerLogger.log(`Cloud Runner is not running in a cloud environment, not collecting logs`);
}
return;
}
CloudRunnerLogger.log(`Log file exist`);
await new Promise((resolve) => setTimeout(resolve, 1));
// let hashedLogs = fs.readFileSync(RemoteClientLogger.LogFilePath).toString();
//
// hashedLogs = md5(hashedLogs);
//
// for (let index = 0; index < 3; index++) {
// CloudRunnerLogger.log(`LOGHASH: ${hashedLogs}`);
// const logs = fs.readFileSync(RemoteClientLogger.LogFilePath).toString();
// CloudRunnerLogger.log(`LOGS: ${Buffer.from(logs).toString('base64')}`);
// CloudRunnerLogger.log(
// `Game CI's "Cloud Runner System" will cancel the log when it has successfully received the log data to verify all logs have been received.`,
// );
//
// // wait for 15 seconds to allow the log to be sent
// await new Promise((resolve) => setTimeout(resolve, 15000));
// }
}
public static HandleLog(message: string): boolean {
if (RemoteClientLogger.value !== '') {
RemoteClientLogger.value += `\n`;
}
RemoteClientLogger.value += message;
return false;
}
static value: string = '';
} }

View File

@@ -0,0 +1,24 @@
import BuildParameters from '../../../build-parameters';
class CloudRunnerResult {
public BuildParameters: BuildParameters;
public BuildResults: string;
public BuildSucceeded: boolean;
public BuildFinished: boolean;
public LibraryCacheUsed: boolean;
public constructor(
buildParameters: BuildParameters,
buildResults: string,
buildSucceeded: boolean,
buildFinished: boolean,
libraryCacheUsed: boolean,
) {
this.BuildParameters = buildParameters;
this.BuildResults = buildResults;
this.BuildSucceeded = buildSucceeded;
this.BuildFinished = buildFinished;
this.LibraryCacheUsed = libraryCacheUsed;
}
}
export default CloudRunnerResult;

View File

@@ -16,7 +16,13 @@ export class CloudRunnerSystem {
}); });
} }
public static async Run(command: string, suppressError = false, suppressLogs = false) { public static async Run(
command: string,
suppressError = false,
suppressLogs = false,
// eslint-disable-next-line no-unused-vars
outputCallback?: (output: string) => void,
) {
for (const element of command.split(`\n`)) { for (const element of command.split(`\n`)) {
if (!suppressLogs) { if (!suppressLogs) {
RemoteClientLogger.log(element); RemoteClientLogger.log(element);
@@ -25,7 +31,7 @@ export class CloudRunnerSystem {
return await new Promise<string>((promise, throwError) => { return await new Promise<string>((promise, throwError) => {
let output = ''; let output = '';
const child = exec(command, (error, stdout, stderr) => { const child = exec(command, { maxBuffer: 1024 * 10000 }, (error, stdout, stderr) => {
if (!suppressError && error) { if (!suppressError && error) {
RemoteClientLogger.log(error.toString()); RemoteClientLogger.log(error.toString());
throwError(error); throwError(error);
@@ -38,6 +44,9 @@ export class CloudRunnerSystem {
output += diagnosticOutput; output += diagnosticOutput;
} }
const outputChunk = `${stdout}`; const outputChunk = `${stdout}`;
if (outputCallback) {
outputCallback(outputChunk);
}
output += outputChunk; output += outputChunk;
}); });
child.on('close', (code) => { child.on('close', (code) => {

View File

@@ -146,7 +146,8 @@ export class TaskParameterSerializer {
array = TaskParameterSerializer.tryAddInput(array, 'UNITY_SERIAL'); array = TaskParameterSerializer.tryAddInput(array, 'UNITY_SERIAL');
array = TaskParameterSerializer.tryAddInput(array, 'UNITY_EMAIL'); array = TaskParameterSerializer.tryAddInput(array, 'UNITY_EMAIL');
array = TaskParameterSerializer.tryAddInput(array, 'UNITY_PASSWORD'); array = TaskParameterSerializer.tryAddInput(array, 'UNITY_PASSWORD');
array = TaskParameterSerializer.tryAddInput(array, 'UNITY_LICENSE');
// array = TaskParameterSerializer.tryAddInput(array, 'UNITY_LICENSE');
array = TaskParameterSerializer.tryAddInput(array, 'GIT_PRIVATE_TOKEN'); array = TaskParameterSerializer.tryAddInput(array, 'GIT_PRIVATE_TOKEN');
return array; return array;

View File

@@ -1,6 +1,5 @@
import YAML from 'yaml'; import YAML from 'yaml';
import CloudRunner from '../../cloud-runner'; import CloudRunner from '../../cloud-runner';
import * as core from '@actions/core';
import { CustomWorkflow } from '../../workflows/custom-workflow'; import { CustomWorkflow } from '../../workflows/custom-workflow';
import { RemoteClientLogger } from '../../remote-client/remote-client-logger'; import { RemoteClientLogger } from '../../remote-client/remote-client-logger';
import path from 'node:path'; import path from 'node:path';
@@ -237,13 +236,11 @@ export class ContainerHookService {
]; ];
if (steps.length > 0) { if (steps.length > 0) {
if (!CloudRunner.buildParameters.isCliMode) core.startGroup('post build steps');
output += await CustomWorkflow.runContainerJob( output += await CustomWorkflow.runContainerJob(
steps, steps,
cloudRunnerStepState.environment, cloudRunnerStepState.environment,
cloudRunnerStepState.secrets, cloudRunnerStepState.secrets,
); );
if (!CloudRunner.buildParameters.isCliMode) core.endGroup();
} }
return output; return output;
@@ -256,13 +253,11 @@ export class ContainerHookService {
]; ];
if (steps.length > 0) { if (steps.length > 0) {
if (!CloudRunner.buildParameters.isCliMode) core.startGroup('pre build steps');
output += await CustomWorkflow.runContainerJob( output += await CustomWorkflow.runContainerJob(
steps, steps,
cloudRunnerStepState.environment, cloudRunnerStepState.environment,
cloudRunnerStepState.secrets, cloudRunnerStepState.secrets,
); );
if (!CloudRunner.buildParameters.isCliMode) core.endGroup();
} }
return output; return output;

View File

@@ -24,11 +24,17 @@ describe('Cloud Runner Async Workflows', () => {
unityVersion: UnityVersioning.read('test-project'), unityVersion: UnityVersioning.read('test-project'),
asyncCloudRunner: `true`, asyncCloudRunner: `true`,
githubChecks: `true`, githubChecks: `true`,
providerStrategy: 'k8s',
buildPlatform: 'linux',
targetPlatform: 'StandaloneLinux64',
}); });
const baseImage = new ImageTag(buildParameter); const baseImage = new ImageTag(buildParameter);
// Run the job // Run the job
await CloudRunner.run(buildParameter, baseImage.toString()); await CloudRunner.run(buildParameter, baseImage.toString());
// wait for 15 seconds
await new Promise((resolve) => setTimeout(resolve, 1000 * 60 * 12));
}, 1_000_000_000); }, 1_000_000_000);
} }
}); });

View File

@@ -34,6 +34,7 @@ describe('Cloud Runner Sync Environments', () => {
versioning: 'None', versioning: 'None',
projectPath: 'test-project', projectPath: 'test-project',
unityVersion: UnityVersioning.read('test-project'), unityVersion: UnityVersioning.read('test-project'),
targetPlatform: 'StandaloneWindows64',
customJob: ` customJob: `
- name: 'step 1' - name: 'step 1'
image: 'ubuntu' image: 'ubuntu'
@@ -44,9 +45,12 @@ describe('Cloud Runner Sync Environments', () => {
`, `,
}); });
const baseImage = new ImageTag(buildParameter); const baseImage = new ImageTag(buildParameter);
if (baseImage.toString().includes('undefined')) {
throw new Error(`Base image is undefined`);
}
// Run the job // Run the job
const file = await CloudRunner.run(buildParameter, baseImage.toString()); const file = (await CloudRunner.run(buildParameter, baseImage.toString())).BuildResults;
// Assert results // Assert results
// expect(file).toContain(JSON.stringify(buildParameter)); // expect(file).toContain(JSON.stringify(buildParameter));

View File

@@ -0,0 +1,59 @@
import { BuildParameters } from '../..';
import CloudRunner from '../cloud-runner';
import UnityVersioning from '../../unity-versioning';
import { Cli } from '../../cli/cli';
import CloudRunnerOptions from '../options/cloud-runner-options';
import setups from './cloud-runner-suite.test';
import { OptionValues } from 'commander';
import GitHub from '../../github';
export const TIMEOUT_INFINITE = 1e9;
async function CreateParameters(overrides: OptionValues | undefined) {
if (overrides) Cli.options = overrides;
return BuildParameters.create();
}
describe('Cloud Runner Github Checks', () => {
setups();
it('Responds', () => {});
if (CloudRunnerOptions.cloudRunnerDebug) {
it(
'Check Handling Direct',
async () => {
// Setup parameters
const buildParameter = await CreateParameters({
versioning: 'None',
projectPath: 'test-project',
unityVersion: UnityVersioning.read('test-project'),
asyncCloudRunner: `true`,
githubChecks: `true`,
});
await CloudRunner.setup(buildParameter);
CloudRunner.buildParameters.githubCheckId = await GitHub.createGitHubCheck(`direct create`);
await GitHub.updateGitHubCheck(`1 ${new Date().toISOString()}`, `direct`);
await GitHub.updateGitHubCheck(`2 ${new Date().toISOString()}`, `direct`, `success`, `completed`);
},
TIMEOUT_INFINITE,
);
it(
'Check Handling Via Async Workflow',
async () => {
// Setup parameters
const buildParameter = await CreateParameters({
versioning: 'None',
projectPath: 'test-project',
unityVersion: UnityVersioning.read('test-project'),
asyncCloudRunner: `true`,
githubChecks: `true`,
});
GitHub.forceAsyncTest = true;
await CloudRunner.setup(buildParameter);
CloudRunner.buildParameters.githubCheckId = await GitHub.createGitHubCheck(`async create`);
await GitHub.updateGitHubCheck(`1 ${new Date().toISOString()}`, `async`);
await GitHub.updateGitHubCheck(`2 ${new Date().toISOString()}`, `async`, `success`, `completed`);
GitHub.forceAsyncTest = false;
},
TIMEOUT_INFINITE,
);
}
});

View File

@@ -30,6 +30,7 @@ commands: echo "test"`;
projectPath: 'test-project', projectPath: 'test-project',
unityVersion: UnityVersioning.determineUnityVersion('test-project', UnityVersioning.read('test-project')), unityVersion: UnityVersioning.determineUnityVersion('test-project', UnityVersioning.read('test-project')),
targetPlatform: 'StandaloneLinux64', targetPlatform: 'StandaloneLinux64',
image: 'ubuntu',
cacheKey: `test-case-${uuidv4()}`, cacheKey: `test-case-${uuidv4()}`,
}; };
CloudRunner.setup(await CreateParameters(overrides)); CloudRunner.setup(await CreateParameters(overrides));
@@ -51,6 +52,7 @@ commands: echo "test"`;
it('Should be 1 before and 1 after hook', async () => { it('Should be 1 before and 1 after hook', async () => {
const overrides = { const overrides = {
versioning: 'None', versioning: 'None',
image: 'ubuntu',
projectPath: 'test-project', projectPath: 'test-project',
unityVersion: UnityVersioning.determineUnityVersion('test-project', UnityVersioning.read('test-project')), unityVersion: UnityVersioning.determineUnityVersion('test-project', UnityVersioning.read('test-project')),
targetPlatform: 'StandaloneLinux64', targetPlatform: 'StandaloneLinux64',
@@ -72,6 +74,7 @@ commands: echo "test"`;
unityVersion: UnityVersioning.determineUnityVersion('test-project', UnityVersioning.read('test-project')), unityVersion: UnityVersioning.determineUnityVersion('test-project', UnityVersioning.read('test-project')),
targetPlatform: 'StandaloneLinux64', targetPlatform: 'StandaloneLinux64',
cacheKey: `test-case-${uuidv4()}`, cacheKey: `test-case-${uuidv4()}`,
image: 'ubuntu',
containerHookFiles: `my-test-step-pre-build,my-test-step-post-build`, containerHookFiles: `my-test-step-pre-build,my-test-step-post-build`,
commandHookFiles: `my-test-hook-pre-build,my-test-hook-post-build`, commandHookFiles: `my-test-hook-pre-build,my-test-hook-post-build`,
}; };
@@ -94,7 +97,8 @@ commands: echo "test"`;
}; };
const buildParameter2 = await CreateParameters(overrides); const buildParameter2 = await CreateParameters(overrides);
const baseImage2 = new ImageTag(buildParameter2); const baseImage2 = new ImageTag(buildParameter2);
const results2 = await CloudRunner.run(buildParameter2, baseImage2.toString()); const results2Object = await CloudRunner.run(buildParameter2, baseImage2.toString());
const results2 = results2Object.BuildResults;
CloudRunnerLogger.log(`run 2 succeeded`); CloudRunnerLogger.log(`run 2 succeeded`);
const buildContainsBuildSucceeded = results2.includes('Build succeeded'); const buildContainsBuildSucceeded = results2.includes('Build succeeded');

View File

@@ -0,0 +1,51 @@
import { BuildParameters, ImageTag } from '../..';
import UnityVersioning from '../../unity-versioning';
import { Cli } from '../../cli/cli';
import GitHub from '../../github';
import setups from './cloud-runner-suite.test';
async function CreateParameters(overrides: any) {
if (overrides) {
Cli.options = overrides;
}
const originalValue = GitHub.githubInputEnabled;
GitHub.githubInputEnabled = false;
const results = await BuildParameters.create();
GitHub.githubInputEnabled = originalValue;
delete Cli.options;
return results;
}
describe('Cloud Runner Image', () => {
setups();
const testSecretName = 'testSecretName';
const testSecretValue = 'testSecretValue';
it('Can create valid image from normal config', async () => {
// Setup parameters
const buildParameter = await CreateParameters({
versioning: 'None',
projectPath: 'test-project',
unityVersion: UnityVersioning.read('test-project'),
targetPlatform: 'StandaloneWindows64',
customJob: `
- name: 'step 1'
image: 'ubuntu'
commands: 'printenv'
secrets:
- name: '${testSecretName}'
value: '${testSecretValue}'
`,
});
const baseImage = new ImageTag(buildParameter);
if (buildParameter.targetPlatform === undefined) {
throw new Error(`target platform includes undefined`);
}
if (baseImage.toString().includes('undefined')) {
throw new Error(`Base image ${baseImage.toString()} includes undefined`);
}
if (baseImage.toString().includes('NaN')) {
throw new Error(`Base image ${baseImage.toString()} includes nan`);
}
}, 1_000_000_000);
});

View File

@@ -32,7 +32,8 @@ describe('Cloud Runner pre-built S3 steps', () => {
}; };
const buildParameter2 = await CreateParameters(overrides); const buildParameter2 = await CreateParameters(overrides);
const baseImage2 = new ImageTag(buildParameter2); const baseImage2 = new ImageTag(buildParameter2);
const results2 = await CloudRunner.run(buildParameter2, baseImage2.toString()); const results2Object = await CloudRunner.run(buildParameter2, baseImage2.toString());
const results2 = results2Object.BuildResults;
CloudRunnerLogger.log(`run 2 succeeded`); CloudRunnerLogger.log(`run 2 succeeded`);
const build2ContainsBuildSucceeded = results2.includes('Build succeeded'); const build2ContainsBuildSucceeded = results2.includes('Build succeeded');

View File

@@ -24,11 +24,13 @@ describe('Cloud Runner Caching', () => {
it('Run one build it should not use cache, run subsequent build which should use cache', async () => { it('Run one build it should not use cache, run subsequent build which should use cache', async () => {
const overrides = { const overrides = {
versioning: 'None', versioning: 'None',
image: 'ubuntu',
projectPath: 'test-project', projectPath: 'test-project',
unityVersion: UnityVersioning.determineUnityVersion('test-project', UnityVersioning.read('test-project')), unityVersion: UnityVersioning.determineUnityVersion('test-project', UnityVersioning.read('test-project')),
targetPlatform: 'StandaloneLinux64', targetPlatform: 'StandaloneLinux64',
cacheKey: `test-case-${uuidv4()}`, cacheKey: `test-case-${uuidv4()}`,
containerHookFiles: `debug-cache`, containerHookFiles: `debug-cache`,
cloudRunnerBranch: `cloud-runner-develop`,
}; };
if (CloudRunnerOptions.providerStrategy === `k8s`) { if (CloudRunnerOptions.providerStrategy === `k8s`) {
overrides.containerHookFiles += `,aws-s3-pull-cache,aws-s3-upload-cache`; overrides.containerHookFiles += `,aws-s3-pull-cache,aws-s3-upload-cache`;
@@ -37,7 +39,8 @@ describe('Cloud Runner Caching', () => {
expect(buildParameter.projectPath).toEqual(overrides.projectPath); expect(buildParameter.projectPath).toEqual(overrides.projectPath);
const baseImage = new ImageTag(buildParameter); const baseImage = new ImageTag(buildParameter);
const results = await CloudRunner.run(buildParameter, baseImage.toString()); const resultsObject = await CloudRunner.run(buildParameter, baseImage.toString());
const results = resultsObject.BuildResults;
const libraryString = 'Rebuilding Library because the asset database could not be found!'; const libraryString = 'Rebuilding Library because the asset database could not be found!';
const cachePushFail = 'Did not push source folder to cache because it was empty Library'; const cachePushFail = 'Did not push source folder to cache because it was empty Library';
const buildSucceededString = 'Build succeeded'; const buildSucceededString = 'Build succeeded';
@@ -63,12 +66,12 @@ describe('Cloud Runner Caching', () => {
buildParameter2.cacheKey = buildParameter.cacheKey; buildParameter2.cacheKey = buildParameter.cacheKey;
const baseImage2 = new ImageTag(buildParameter2); const baseImage2 = new ImageTag(buildParameter2);
const results2 = await CloudRunner.run(buildParameter2, baseImage2.toString()); const results2Object = await CloudRunner.run(buildParameter2, baseImage2.toString());
const results2 = results2Object.BuildResults;
CloudRunnerLogger.log(`run 2 succeeded`); CloudRunnerLogger.log(`run 2 succeeded`);
const build2ContainsCacheKey = results2.includes(buildParameter.cacheKey); const build2ContainsCacheKey = results2.includes(buildParameter.cacheKey);
const build2ContainsBuildSucceeded = results2.includes(buildSucceededString); const build2ContainsBuildSucceeded = results2.includes(buildSucceededString);
const build2NotContainsNoLibraryMessage = !results2.includes(libraryString);
const build2NotContainsZeroLibraryCacheFilesMessage = !results2.includes( const build2NotContainsZeroLibraryCacheFilesMessage = !results2.includes(
'There is 0 files/dir in the cache pulled contents for Library', 'There is 0 files/dir in the cache pulled contents for Library',
); );
@@ -77,10 +80,13 @@ describe('Cloud Runner Caching', () => {
); );
expect(build2ContainsCacheKey).toBeTruthy(); expect(build2ContainsCacheKey).toBeTruthy();
expect(results2).toContain('Activation successful');
expect(build2ContainsBuildSucceeded).toBeTruthy(); expect(build2ContainsBuildSucceeded).toBeTruthy();
expect(results2).toContain(buildSucceededString);
const splitResults = results2.split('Activation successful');
expect(splitResults[splitResults.length - 1]).not.toContain(libraryString);
expect(build2NotContainsZeroLibraryCacheFilesMessage).toBeTruthy(); expect(build2NotContainsZeroLibraryCacheFilesMessage).toBeTruthy();
expect(build2NotContainsZeroLFSCacheFilesMessage).toBeTruthy(); expect(build2NotContainsZeroLFSCacheFilesMessage).toBeTruthy();
expect(build2NotContainsNoLibraryMessage).toBeTruthy();
}, 1_000_000_000); }, 1_000_000_000);
} }
}); });

View File

@@ -29,7 +29,8 @@ describe('Cloud Runner Retain Workspace', () => {
expect(buildParameter.projectPath).toEqual(overrides.projectPath); expect(buildParameter.projectPath).toEqual(overrides.projectPath);
const baseImage = new ImageTag(buildParameter); const baseImage = new ImageTag(buildParameter);
const results = await CloudRunner.run(buildParameter, baseImage.toString()); const resultsObject = await CloudRunner.run(buildParameter, baseImage.toString());
const results = resultsObject.BuildResults;
const libraryString = 'Rebuilding Library because the asset database could not be found!'; const libraryString = 'Rebuilding Library because the asset database could not be found!';
const cachePushFail = 'Did not push source folder to cache because it was empty Library'; const cachePushFail = 'Did not push source folder to cache because it was empty Library';
const buildSucceededString = 'Build succeeded'; const buildSucceededString = 'Build succeeded';
@@ -51,7 +52,8 @@ describe('Cloud Runner Retain Workspace', () => {
buildParameter2.cacheKey = buildParameter.cacheKey; buildParameter2.cacheKey = buildParameter.cacheKey;
const baseImage2 = new ImageTag(buildParameter2); const baseImage2 = new ImageTag(buildParameter2);
const results2 = await CloudRunner.run(buildParameter2, baseImage2.toString()); const results2Object = await CloudRunner.run(buildParameter2, baseImage2.toString());
const results2 = results2Object.BuildResults;
CloudRunnerLogger.log(`run 2 succeeded`); CloudRunnerLogger.log(`run 2 succeeded`);
const build2ContainsCacheKey = results2.includes(buildParameter.cacheKey); const build2ContainsCacheKey = results2.includes(buildParameter.cacheKey);
@@ -59,7 +61,6 @@ describe('Cloud Runner Retain Workspace', () => {
const build2ContainsRetainedWorkspacePhrase = results2.includes(`Retained Workspace:`); const build2ContainsRetainedWorkspacePhrase = results2.includes(`Retained Workspace:`);
const build2ContainsWorkspaceExistsAlreadyPhrase = results2.includes(`Retained Workspace Already Exists!`); const build2ContainsWorkspaceExistsAlreadyPhrase = results2.includes(`Retained Workspace Already Exists!`);
const build2ContainsBuildSucceeded = results2.includes(buildSucceededString); const build2ContainsBuildSucceeded = results2.includes(buildSucceededString);
const build2NotContainsNoLibraryMessage = !results2.includes(libraryString);
const build2NotContainsZeroLibraryCacheFilesMessage = !results2.includes( const build2NotContainsZeroLibraryCacheFilesMessage = !results2.includes(
'There is 0 files/dir in the cache pulled contents for Library', 'There is 0 files/dir in the cache pulled contents for Library',
); );
@@ -74,7 +75,8 @@ describe('Cloud Runner Retain Workspace', () => {
expect(build2ContainsBuildSucceeded).toBeTruthy(); expect(build2ContainsBuildSucceeded).toBeTruthy();
expect(build2NotContainsZeroLibraryCacheFilesMessage).toBeTruthy(); expect(build2NotContainsZeroLibraryCacheFilesMessage).toBeTruthy();
expect(build2NotContainsZeroLFSCacheFilesMessage).toBeTruthy(); expect(build2NotContainsZeroLFSCacheFilesMessage).toBeTruthy();
expect(build2NotContainsNoLibraryMessage).toBeTruthy(); const splitResults = results2.split('Activation successful');
expect(splitResults[splitResults.length - 1]).not.toContain(libraryString);
}, 1_000_000_000); }, 1_000_000_000);
afterAll(async () => { afterAll(async () => {
await SharedWorkspaceLocking.CleanupWorkspace(CloudRunner.lockedWorkspace || ``, CloudRunner.buildParameters); await SharedWorkspaceLocking.CleanupWorkspace(CloudRunner.lockedWorkspace || ``, CloudRunner.buildParameters);

View File

@@ -0,0 +1,56 @@
import CloudRunner from '../../cloud-runner';
import UnityVersioning from '../../../unity-versioning';
import { Cli } from '../../../cli/cli';
import CloudRunnerLogger from '../../services/core/cloud-runner-logger';
import { v4 as uuidv4 } from 'uuid';
import CloudRunnerOptions from '../../options/cloud-runner-options';
import setups from '../cloud-runner-suite.test';
import BuildParameters from '../../../build-parameters';
import ImageTag from '../../../image-tag';
async function CreateParameters(overrides: any) {
if (overrides) {
Cli.options = overrides;
}
return await BuildParameters.create();
}
describe('Cloud Runner Kubernetes', () => {
it('Responds', () => {});
setups();
if (CloudRunnerOptions.cloudRunnerDebug) {
it('Run one build it using K8s without error', async () => {
if (CloudRunnerOptions.providerStrategy !== `k8s`) {
return;
}
process.env.USE_IL2CPP = 'false';
const overrides = {
versioning: 'None',
projectPath: 'test-project',
unityVersion: UnityVersioning.determineUnityVersion('test-project', UnityVersioning.read('test-project')),
targetPlatform: 'StandaloneLinux64',
cacheKey: `test-case-${uuidv4()}`,
providerStrategy: 'k8s',
buildPlatform: 'linux',
};
const buildParameter = await CreateParameters(overrides);
expect(buildParameter.projectPath).toEqual(overrides.projectPath);
const baseImage = new ImageTag(buildParameter);
const resultsObject = await CloudRunner.run(buildParameter, baseImage.toString());
const results = resultsObject.BuildResults;
const libraryString = 'Rebuilding Library because the asset database could not be found!';
const cachePushFail = 'Did not push source folder to cache because it was empty Library';
const buildSucceededString = 'Build succeeded';
expect(results).toContain('Collected Logs');
expect(results).toContain(libraryString);
expect(results).toContain(buildSucceededString);
expect(results).not.toContain(cachePushFail);
CloudRunnerLogger.log(`run 1 succeeded`);
}, 1_000_000_000);
}
});

View File

@@ -41,6 +41,11 @@ node /builder/dist/index.js -m async-workflow`,
[ [
...secrets, ...secrets,
...[ ...[
{
ParameterKey: `GITHUB_TOKEN`,
EnvironmentVariable: `GITHUB_TOKEN`,
ParameterValue: process.env.GITHUB_TOKEN || ``,
},
{ {
ParameterKey: `AWS_ACCESS_KEY_ID`, ParameterKey: `AWS_ACCESS_KEY_ID`,
EnvironmentVariable: `AWS_ACCESS_KEY_ID`, EnvironmentVariable: `AWS_ACCESS_KEY_ID`,

View File

@@ -2,7 +2,6 @@ import CloudRunnerLogger from '../services/core/cloud-runner-logger';
import { CloudRunnerFolders } from '../options/cloud-runner-folders'; import { CloudRunnerFolders } from '../options/cloud-runner-folders';
import { CloudRunnerStepParameters } from '../options/cloud-runner-step-parameters'; import { CloudRunnerStepParameters } from '../options/cloud-runner-step-parameters';
import { WorkflowInterface } from './workflow-interface'; import { WorkflowInterface } from './workflow-interface';
import * as core from '@actions/core';
import { CommandHookService } from '../services/hooks/command-hook-service'; import { CommandHookService } from '../services/hooks/command-hook-service';
import path from 'node:path'; import path from 'node:path';
import CloudRunner from '../cloud-runner'; import CloudRunner from '../cloud-runner';
@@ -21,8 +20,6 @@ export class BuildAutomationWorkflow implements WorkflowInterface {
output += await ContainerHookService.RunPreBuildSteps(cloudRunnerStepState); output += await ContainerHookService.RunPreBuildSteps(cloudRunnerStepState);
CloudRunnerLogger.logWithTime('Configurable pre build step(s) time'); CloudRunnerLogger.logWithTime('Configurable pre build step(s) time');
if (!CloudRunner.buildParameters.isCliMode) core.startGroup('build');
CloudRunnerLogger.log(baseImage); CloudRunnerLogger.log(baseImage);
CloudRunnerLogger.logLine(` `); CloudRunnerLogger.logLine(` `);
CloudRunnerLogger.logLine('Starting build automation job'); CloudRunnerLogger.logLine('Starting build automation job');
@@ -36,7 +33,6 @@ export class BuildAutomationWorkflow implements WorkflowInterface {
cloudRunnerStepState.environment, cloudRunnerStepState.environment,
cloudRunnerStepState.secrets, cloudRunnerStepState.secrets,
); );
if (!CloudRunner.buildParameters.isCliMode) core.endGroup();
CloudRunnerLogger.logWithTime('Build time'); CloudRunnerLogger.logWithTime('Build time');
output += await ContainerHookService.RunPostBuildSteps(cloudRunnerStepState); output += await ContainerHookService.RunPostBuildSteps(cloudRunnerStepState);
@@ -58,11 +54,14 @@ export class BuildAutomationWorkflow implements WorkflowInterface {
path.join(CloudRunnerFolders.builderPathAbsolute, 'dist', `index.js`), path.join(CloudRunnerFolders.builderPathAbsolute, 'dist', `index.js`),
); );
return `apt-get update > /dev/null return `echo "cloud runner build workflow starting"
apt-get update > /dev/null
apt-get install -y curl tar tree npm git-lfs jq git > /dev/null apt-get install -y curl tar tree npm git-lfs jq git > /dev/null
npm i -g n > /dev/null
n 16.15.1 > /dev/null
npm --version npm --version
npm i -g n > /dev/null
npm i -g semver > /dev/null
npm install --global yarn > /dev/null
n 20.8.0
node --version node --version
${setupHooks.filter((x) => x.hook.includes(`before`)).map((x) => x.commands) || ' '} ${setupHooks.filter((x) => x.hook.includes(`before`)).map((x) => x.commands) || ' '}
export GITHUB_WORKSPACE="${CloudRunnerFolders.ToLinuxFolder(CloudRunnerFolders.repoPathAbsolute)}" export GITHUB_WORKSPACE="${CloudRunnerFolders.ToLinuxFolder(CloudRunnerFolders.repoPathAbsolute)}"
@@ -91,6 +90,7 @@ export class BuildAutomationWorkflow implements WorkflowInterface {
return `export GIT_DISCOVERY_ACROSS_FILESYSTEM=1 return `export GIT_DISCOVERY_ACROSS_FILESYSTEM=1
${cloneBuilderCommands} ${cloneBuilderCommands}
echo "log start" >> /home/job-log.txt
node ${builderPath} -m remote-cli-pre-build`; node ${builderPath} -m remote-cli-pre-build`;
} }
@@ -98,7 +98,7 @@ node ${builderPath} -m remote-cli-pre-build`;
const distFolder = path.join(CloudRunnerFolders.builderPathAbsolute, 'dist'); const distFolder = path.join(CloudRunnerFolders.builderPathAbsolute, 'dist');
const ubuntuPlatformsFolder = path.join(CloudRunnerFolders.builderPathAbsolute, 'dist', 'platforms', 'ubuntu'); const ubuntuPlatformsFolder = path.join(CloudRunnerFolders.builderPathAbsolute, 'dist', 'platforms', 'ubuntu');
return `echo "game ci cloud runner initalized" return `
mkdir -p ${`${CloudRunnerFolders.ToLinuxFolder(CloudRunnerFolders.projectBuildFolderAbsolute)}/build`} mkdir -p ${`${CloudRunnerFolders.ToLinuxFolder(CloudRunnerFolders.projectBuildFolderAbsolute)}/build`}
cd ${CloudRunnerFolders.ToLinuxFolder(CloudRunnerFolders.projectPathAbsolute)} cd ${CloudRunnerFolders.ToLinuxFolder(CloudRunnerFolders.projectPathAbsolute)}
cp -r "${CloudRunnerFolders.ToLinuxFolder(path.join(distFolder, 'default-build-script'))}" "/UnityBuilderAction" cp -r "${CloudRunnerFolders.ToLinuxFolder(path.join(distFolder, 'default-build-script'))}" "/UnityBuilderAction"
@@ -107,9 +107,8 @@ node ${builderPath} -m remote-cli-pre-build`;
chmod -R +x "/entrypoint.sh" chmod -R +x "/entrypoint.sh"
chmod -R +x "/steps" chmod -R +x "/steps"
echo "game ci start" echo "game ci start"
/entrypoint.sh echo "game ci start" >> /home/job-log.txt
echo "game ci caching results" /entrypoint.sh | node ${builderPath} -m remote-cli-log-stream --logFile /home/job-log.txt
chmod +x ${builderPath}
node ${builderPath} -m remote-cli-post-build`; node ${builderPath} -m remote-cli-post-build`;
} }
} }

View File

@@ -21,6 +21,9 @@ class Docker {
break; break;
case 'win32': case 'win32':
runCommand = this.getWindowsCommand(image, parameters); runCommand = this.getWindowsCommand(image, parameters);
break;
default:
throw new Error(`Operation system, ${process.platform}, is not supported yet.`);
} }
options.silent = silent; options.silent = silent;

View File

@@ -11,6 +11,7 @@ class GitHub {
private static startedDate: string; private static startedDate: string;
private static endedDate: string; private static endedDate: string;
static result: string = ``; static result: string = ``;
static forceAsyncTest: boolean;
private static get octokitDefaultToken() { private static get octokitDefaultToken() {
return new Octokit({ return new Octokit({
auth: process.env.GITHUB_TOKEN, auth: process.env.GITHUB_TOKEN,
@@ -51,7 +52,7 @@ class GitHub {
} }
GitHub.startedDate = new Date().toISOString(); GitHub.startedDate = new Date().toISOString();
CloudRunnerLogger.log(`Creating inital github check`); CloudRunnerLogger.log(`Creating github check`);
const data = { const data = {
owner: GitHub.owner, owner: GitHub.owner,
repo: GitHub.repo, repo: GitHub.repo,
@@ -78,6 +79,8 @@ class GitHub {
}; };
const result = await GitHub.createGitHubCheckRequest(data); const result = await GitHub.createGitHubCheckRequest(data);
CloudRunnerLogger.log(`Creating github check ${result.status}`);
return result.data.id.toString(); return result.data.id.toString();
} }
@@ -127,7 +130,7 @@ class GitHub {
data.conclusion = result; data.conclusion = result;
} }
await (CloudRunner.isCloudRunnerAsyncEnvironment await (CloudRunner.isCloudRunnerAsyncEnvironment || GitHub.forceAsyncTest
? GitHub.runUpdateAsyncChecksWorkflow(data, `update`) ? GitHub.runUpdateAsyncChecksWorkflow(data, `update`)
: GitHub.updateGitHubCheckRequest(data)); : GitHub.updateGitHubCheckRequest(data));
} }
@@ -174,38 +177,46 @@ class GitHub {
static async triggerWorkflowOnComplete(triggerWorkflowOnComplete: string[]) { static async triggerWorkflowOnComplete(triggerWorkflowOnComplete: string[]) {
const isLocalAsync = CloudRunner.buildParameters.asyncWorkflow && !CloudRunner.isCloudRunnerAsyncEnvironment; const isLocalAsync = CloudRunner.buildParameters.asyncWorkflow && !CloudRunner.isCloudRunnerAsyncEnvironment;
if (isLocalAsync) { if (isLocalAsync || triggerWorkflowOnComplete === undefined || triggerWorkflowOnComplete.length === 0) {
return; return;
} }
const workflowsResult = await GitHub.octokitPAT.request(`GET /repos/{owner}/{repo}/actions/workflows`, { try {
owner: GitHub.owner, const workflowsResult = await GitHub.octokitPAT.request(`GET /repos/{owner}/{repo}/actions/workflows`, {
repo: GitHub.repo,
});
const workflows = workflowsResult.data.workflows;
CloudRunnerLogger.log(`Got ${workflows.length} workflows`);
for (const element of triggerWorkflowOnComplete) {
let selectedId = ``;
for (let index = 0; index < workflowsResult.data.total_count; index++) {
if (workflows[index].name === element) {
selectedId = workflows[index].id.toString();
}
}
if (selectedId === ``) {
core.info(JSON.stringify(workflows));
throw new Error(`no workflow with name "${GitHub.asyncChecksApiWorkflowName}"`);
}
await GitHub.octokitPAT.request(`POST /repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches`, {
owner: GitHub.owner, owner: GitHub.owner,
repo: GitHub.repo, repo: GitHub.repo,
// eslint-disable-next-line camelcase
workflow_id: selectedId,
ref: CloudRunnerOptions.branch,
inputs: {
buildGuid: CloudRunner.buildParameters.buildGuid,
},
}); });
const workflows = workflowsResult.data.workflows;
CloudRunnerLogger.log(`Got ${workflows.length} workflows`);
for (const element of triggerWorkflowOnComplete) {
let selectedId = ``;
for (let index = 0; index < workflowsResult.data.total_count; index++) {
if (workflows[index].name === element) {
selectedId = workflows[index].id.toString();
}
}
if (selectedId === ``) {
core.info(JSON.stringify(workflows));
throw new Error(`no workflow with name "${GitHub.asyncChecksApiWorkflowName}"`);
}
await GitHub.octokitPAT.request(`POST /repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches`, {
owner: GitHub.owner,
repo: GitHub.repo,
// eslint-disable-next-line camelcase
workflow_id: selectedId,
ref: CloudRunnerOptions.branch,
inputs: {
buildGuid: CloudRunner.buildParameters.buildGuid,
},
});
}
} catch {
core.info(`github workflow complete hook not found`);
} }
} }
public static async getCheckStatus() {
return await GitHub.octokitDefaultToken.request(`GET /repos/{owner}/{repo}/check-runs/{check_run_id}`);
}
} }
export default GitHub; export default GitHub;

View File

@@ -29,6 +29,7 @@ class ImageEnvironmentFactory {
name: 'UNITY_LICENSING_SERVER', name: 'UNITY_LICENSING_SERVER',
value: parameters.unityLicensingServer, value: parameters.unityLicensingServer,
}, },
{ name: 'SKIP_ACTIVATION', value: parameters.skipActivation },
{ name: 'UNITY_VERSION', value: parameters.editorVersion }, { name: 'UNITY_VERSION', value: parameters.editorVersion },
{ {
name: 'USYM_UPLOAD_AUTH_TOKEN', name: 'USYM_UPLOAD_AUTH_TOKEN',
@@ -81,20 +82,12 @@ class ImageEnvironmentFactory {
]; ];
if (parameters.providerStrategy === 'local-docker') { if (parameters.providerStrategy === 'local-docker') {
for (const element of additionalVariables) { for (const element of additionalVariables) {
if ( if (!environmentVariables.some((x) => element?.name === x?.name)) {
environmentVariables.find(
(x) => element !== undefined && element.name !== undefined && x.name === element.name,
) === undefined
) {
environmentVariables.push(element); environmentVariables.push(element);
} }
} }
for (const variable of environmentVariables) { for (const variable of environmentVariables) {
if ( if (!environmentVariables.some((x) => variable?.name === x?.name)) {
environmentVariables.find(
(x) => variable !== undefined && variable.name !== undefined && x.name === variable.name,
) === undefined
) {
environmentVariables = environmentVariables.filter((x) => x !== variable); environmentVariables = environmentVariables.filter((x) => x !== variable);
} }
} }

View File

@@ -2,7 +2,6 @@ import Platform from './platform';
class ImageTag { class ImageTag {
public repository: string; public repository: string;
public cloudRunnerBuilderPlatform!: string;
public editorVersion: string; public editorVersion: string;
public targetPlatform: string; public targetPlatform: string;
public builderPlatform: string; public builderPlatform: string;
@@ -15,9 +14,10 @@ class ImageTag {
editorVersion, editorVersion,
targetPlatform, targetPlatform,
customImage, customImage,
cloudRunnerBuilderPlatform, buildPlatform,
containerRegistryRepository, containerRegistryRepository,
containerRegistryImageVersion, containerRegistryImageVersion,
providerStrategy,
} = imageProperties; } = imageProperties;
if (!ImageTag.versionPattern.test(editorVersion)) { if (!ImageTag.versionPattern.test(editorVersion)) {
@@ -32,12 +32,12 @@ class ImageTag {
this.repository = containerRegistryRepository; this.repository = containerRegistryRepository;
this.editorVersion = editorVersion; this.editorVersion = editorVersion;
this.targetPlatform = targetPlatform; this.targetPlatform = targetPlatform;
this.cloudRunnerBuilderPlatform = cloudRunnerBuilderPlatform; this.builderPlatform = ImageTag.getTargetPlatformToTargetPlatformSuffixMap(
const isCloudRunnerLocal = cloudRunnerBuilderPlatform === 'local' || cloudRunnerBuilderPlatform === undefined; targetPlatform,
this.builderPlatform = ImageTag.getTargetPlatformToTargetPlatformSuffixMap(targetPlatform, editorVersion); editorVersion,
this.imagePlatformPrefix = ImageTag.getImagePlatformPrefixes( providerStrategy,
isCloudRunnerLocal ? process.platform : cloudRunnerBuilderPlatform,
); );
this.imagePlatformPrefix = ImageTag.getImagePlatformPrefixes(buildPlatform);
this.imageRollingVersion = Number(containerRegistryImageVersion); // Will automatically roll to the latest non-breaking version. this.imageRollingVersion = Number(containerRegistryImageVersion); // Will automatically roll to the latest non-breaking version.
} }
@@ -63,6 +63,10 @@ class ImageTag {
} }
static getImagePlatformPrefixes(platform: string): string { static getImagePlatformPrefixes(platform: string): string {
if (!platform || platform === '') {
platform = process.platform;
}
switch (platform) { switch (platform) {
case 'win32': case 'win32':
return 'windows'; return 'windows';
@@ -73,7 +77,11 @@ class ImageTag {
} }
} }
static getTargetPlatformToTargetPlatformSuffixMap(platform: string, version: string): string { static getTargetPlatformToTargetPlatformSuffixMap(
platform: string,
version: string,
providerStrategy: string,
): string {
const { generic, webgl, mac, windows, windowsIl2cpp, wsaPlayer, linux, linuxIl2cpp, android, ios, tvos, facebook } = const { generic, webgl, mac, windows, windowsIl2cpp, wsaPlayer, linux, linuxIl2cpp, android, ios, tvos, facebook } =
ImageTag.targetPlatformSuffixes; ImageTag.targetPlatformSuffixes;
@@ -102,7 +110,11 @@ class ImageTag {
case Platform.types.StandaloneLinux64: { case Platform.types.StandaloneLinux64: {
// Unity versions before 2019.3 do not support il2cpp // Unity versions before 2019.3 do not support il2cpp
if (major >= 2020 || (major === 2019 && minor >= 3)) { if (major >= 2020 || (major === 2019 && minor >= 3)) {
return linuxIl2cpp; if (providerStrategy === 'local') {
return linuxIl2cpp;
} else {
return process.env.USE_IL2CPP === 'true' ? linuxIl2cpp : linux;
}
} }
return linux; return linux;
@@ -169,7 +181,7 @@ class ImageTag {
if (customImage) return customImage; if (customImage) return customImage;
return `${image}:${tag}`; // '0' here represents the docker repo version return `${image}:${tag}`;
} }
} }

View File

@@ -46,11 +46,11 @@ class Input {
} }
static get region(): string { static get region(): string {
return Input.getInput('region') || 'eu-west-2'; return Input.getInput('region') ?? 'eu-west-2';
} }
static get githubRepo(): string | undefined { static get githubRepo(): string | undefined {
return Input.getInput('GITHUB_REPOSITORY') || Input.getInput('GITHUB_REPO') || undefined; return Input.getInput('GITHUB_REPOSITORY') ?? Input.getInput('GITHUB_REPO') ?? undefined;
} }
static get branch(): string { static get branch(): string {
@@ -74,19 +74,19 @@ class Input {
} }
static get runNumber(): string { static get runNumber(): string {
return Input.getInput('GITHUB_RUN_NUMBER') || '0'; return Input.getInput('GITHUB_RUN_NUMBER') ?? '0';
} }
static get targetPlatform(): string { static get targetPlatform(): string {
return Input.getInput('targetPlatform') || Platform.default; return Input.getInput('targetPlatform') ?? Platform.default;
} }
static get unityVersion(): string { static get unityVersion(): string {
return Input.getInput('unityVersion') || 'auto'; return Input.getInput('unityVersion') ?? 'auto';
} }
static get customImage(): string { static get customImage(): string {
return Input.getInput('customImage') || ''; return Input.getInput('customImage') ?? '';
} }
static get projectPath(): string { static get projectPath(): string {
@@ -108,85 +108,85 @@ class Input {
} }
static get runnerTempPath(): string { static get runnerTempPath(): string {
return Input.getInput('RUNNER_TEMP') || ''; return Input.getInput('RUNNER_TEMP') ?? '';
} }
static get buildName(): string { static get buildName(): string {
return Input.getInput('buildName') || Input.targetPlatform; return Input.getInput('buildName') ?? Input.targetPlatform;
} }
static get buildsPath(): string { static get buildsPath(): string {
return Input.getInput('buildsPath') || 'build'; return Input.getInput('buildsPath') ?? 'build';
} }
static get unityLicensingServer(): string { static get unityLicensingServer(): string {
return Input.getInput('unityLicensingServer') || ''; return Input.getInput('unityLicensingServer') ?? '';
} }
static get buildMethod(): string { static get buildMethod(): string {
return Input.getInput('buildMethod') || ''; // Processed in docker file return Input.getInput('buildMethod') ?? ''; // Processed in docker file
} }
static get manualExit(): boolean { static get manualExit(): boolean {
const input = Input.getInput('manualExit') || false; const input = Input.getInput('manualExit') ?? false;
return input === 'true'; return input === 'true';
} }
static get customParameters(): string { static get customParameters(): string {
return Input.getInput('customParameters') || ''; return Input.getInput('customParameters') ?? '';
} }
static get versioningStrategy(): string { static get versioningStrategy(): string {
return Input.getInput('versioning') || 'Semantic'; return Input.getInput('versioning') ?? 'Semantic';
} }
static get specifiedVersion(): string { static get specifiedVersion(): string {
return Input.getInput('version') || ''; return Input.getInput('version') ?? '';
} }
static get androidVersionCode(): string { static get androidVersionCode(): string {
return Input.getInput('androidVersionCode') || ''; return Input.getInput('androidVersionCode') ?? '';
} }
static get androidExportType(): string { static get androidExportType(): string {
return Input.getInput('androidExportType') || 'androidPackage'; return Input.getInput('androidExportType') ?? 'androidPackage';
} }
static get androidKeystoreName(): string { static get androidKeystoreName(): string {
return Input.getInput('androidKeystoreName') || ''; return Input.getInput('androidKeystoreName') ?? '';
} }
static get androidKeystoreBase64(): string { static get androidKeystoreBase64(): string {
return Input.getInput('androidKeystoreBase64') || ''; return Input.getInput('androidKeystoreBase64') ?? '';
} }
static get androidKeystorePass(): string { static get androidKeystorePass(): string {
return Input.getInput('androidKeystorePass') || ''; return Input.getInput('androidKeystorePass') ?? '';
} }
static get androidKeyaliasName(): string { static get androidKeyaliasName(): string {
return Input.getInput('androidKeyaliasName') || ''; return Input.getInput('androidKeyaliasName') ?? '';
} }
static get androidKeyaliasPass(): string { static get androidKeyaliasPass(): string {
return Input.getInput('androidKeyaliasPass') || ''; return Input.getInput('androidKeyaliasPass') ?? '';
} }
static get androidTargetSdkVersion(): string { static get androidTargetSdkVersion(): string {
return Input.getInput('androidTargetSdkVersion') || ''; return Input.getInput('androidTargetSdkVersion') ?? '';
} }
static get androidSymbolType(): string { static get androidSymbolType(): string {
return Input.getInput('androidSymbolType') || 'none'; return Input.getInput('androidSymbolType') ?? 'none';
} }
static get sshAgent(): string { static get sshAgent(): string {
return Input.getInput('sshAgent') || ''; return Input.getInput('sshAgent') ?? '';
} }
static get sshPublicKeysDirectoryPath(): string { static get sshPublicKeysDirectoryPath(): string {
return Input.getInput('sshPublicKeysDirectoryPath') || ''; return Input.getInput('sshPublicKeysDirectoryPath') ?? '';
} }
static get gitPrivateToken(): string | undefined { static get gitPrivateToken(): string | undefined {
@@ -194,27 +194,27 @@ class Input {
} }
static get runAsHostUser(): string { static get runAsHostUser(): string {
return Input.getInput('runAsHostUser') || 'false'; return Input.getInput('runAsHostUser')?.toLowerCase() ?? 'false';
} }
static get chownFilesTo() { static get chownFilesTo() {
return Input.getInput('chownFilesTo') || ''; return Input.getInput('chownFilesTo') ?? '';
} }
static get allowDirtyBuild(): boolean { static get allowDirtyBuild(): boolean {
const input = Input.getInput('allowDirtyBuild') || false; const input = Input.getInput('allowDirtyBuild') ?? false;
return input === 'true'; return input === 'true';
} }
static get cacheUnityInstallationOnMac(): boolean { static get cacheUnityInstallationOnMac(): boolean {
const input = Input.getInput('cacheUnityInstallationOnMac') || false; const input = Input.getInput('cacheUnityInstallationOnMac') ?? false;
return input === 'true'; return input === 'true';
} }
static get unityHubVersionOnMac(): string { static get unityHubVersionOnMac(): string {
const input = Input.getInput('unityHubVersionOnMac') || ''; const input = Input.getInput('unityHubVersionOnMac') ?? '';
return input !== '' ? input : ''; return input !== '' ? input : '';
} }
@@ -228,11 +228,11 @@ class Input {
} }
static get dockerWorkspacePath(): string { static get dockerWorkspacePath(): string {
return Input.getInput('dockerWorkspacePath') || '/github/workspace'; return Input.getInput('dockerWorkspacePath') ?? '/github/workspace';
} }
static get dockerCpuLimit(): string { static get dockerCpuLimit(): string {
return Input.getInput('dockerCpuLimit') || os.cpus().length.toString(); return Input.getInput('dockerCpuLimit') ?? os.cpus().length.toString();
} }
static get dockerMemoryLimit(): string { static get dockerMemoryLimit(): string {
@@ -252,20 +252,24 @@ class Input {
} }
return ( return (
Input.getInput('dockerMemoryLimit') || `${Math.floor((os.totalmem() / bytesInMegabyte) * memoryMultiplier)}m` Input.getInput('dockerMemoryLimit') ?? `${Math.floor((os.totalmem() / bytesInMegabyte) * memoryMultiplier)}m`
); );
} }
static get dockerIsolationMode(): string { static get dockerIsolationMode(): string {
return Input.getInput('dockerIsolationMode') || 'default'; return Input.getInput('dockerIsolationMode') ?? 'default';
} }
static get containerRegistryRepository(): string { static get containerRegistryRepository(): string {
return Input.getInput('containerRegistryRepository')!; return Input.getInput('containerRegistryRepository') ?? 'unityci/editor';
} }
static get containerRegistryImageVersion(): string { static get containerRegistryImageVersion(): string {
return Input.getInput('containerRegistryImageVersion')!; return Input.getInput('containerRegistryImageVersion') ?? '3';
}
static get skipActivation(): string {
return Input.getInput('skipActivation')?.toLowerCase() ?? 'false';
} }
public static ToEnvVarFormat(input: string) { public static ToEnvVarFormat(input: string) {

View File

@@ -168,6 +168,7 @@ class SetupMac {
process.env.UNITY_VERSION = buildParameters.editorVersion; process.env.UNITY_VERSION = buildParameters.editorVersion;
process.env.UNITY_SERIAL = buildParameters.unitySerial; process.env.UNITY_SERIAL = buildParameters.unitySerial;
process.env.UNITY_LICENSING_SERVER = buildParameters.unityLicensingServer; process.env.UNITY_LICENSING_SERVER = buildParameters.unityLicensingServer;
process.env.SKIP_ACTIVATION = buildParameters.skipActivation;
process.env.PROJECT_PATH = buildParameters.projectPath; process.env.PROJECT_PATH = buildParameters.projectPath;
process.env.BUILD_TARGET = buildParameters.targetPlatform; process.env.BUILD_TARGET = buildParameters.targetPlatform;
process.env.BUILD_NAME = buildParameters.buildName; process.env.BUILD_NAME = buildParameters.buildName;
@@ -187,6 +188,7 @@ class SetupMac {
process.env.ANDROID_SYMBOL_TYPE = buildParameters.androidSymbolType; process.env.ANDROID_SYMBOL_TYPE = buildParameters.androidSymbolType;
process.env.CUSTOM_PARAMETERS = buildParameters.customParameters; process.env.CUSTOM_PARAMETERS = buildParameters.customParameters;
process.env.CHOWN_FILES_TO = buildParameters.chownFilesTo; process.env.CHOWN_FILES_TO = buildParameters.chownFilesTo;
process.env.MANUAL_EXIT = buildParameters.manualExit.toString();
} }
} }

View File

@@ -1,7 +1,7 @@
{ {
"m_Name": "Settings", "m_Name": "Settings",
"m_Path": "ProjectSettings/Packages/com.unity.testtools.codecoverage/Settings.json", "m_Path": "ProjectSettings/Packages/com.unity.testtools.codecoverage/Settings.json",
"m_Dictionary": { "m_Dictionary": {
"m_DictionaryValues": [] "m_DictionaryValues": []
} }
} }

View File

@@ -806,6 +806,8 @@ PlayerSettings:
platformArchitecture: {} platformArchitecture: {}
scriptingBackend: scriptingBackend:
Android: 1 Android: 1
Server: 1
Standalone: 1
il2cppCompilerConfiguration: {} il2cppCompilerConfiguration: {}
managedStrippingLevel: {} managedStrippingLevel: {}
incrementalIl2cppBuild: {} incrementalIl2cppBuild: {}
@@ -821,8 +823,8 @@ PlayerSettings:
apiCompatibilityLevelPerPlatform: {} apiCompatibilityLevelPerPlatform: {}
m_RenderingPath: 1 m_RenderingPath: 1
m_MobileRenderingPath: 1 m_MobileRenderingPath: 1
metroPackageName: Template_3D metroPackageName: Template3D
metroPackageVersion: metroPackageVersion: 1.0.0.0
metroCertificatePath: metroCertificatePath:
metroCertificatePassword: metroCertificatePassword:
metroCertificateSubject: metroCertificateSubject:
@@ -830,7 +832,7 @@ PlayerSettings:
metroCertificateNotAfter: 0000000000000000 metroCertificateNotAfter: 0000000000000000
metroApplicationDescription: Template_3D metroApplicationDescription: Template_3D
wsaImages: {} wsaImages: {}
metroTileShortName: metroTileShortName: TestProject
metroTileShowName: 0 metroTileShowName: 0
metroMediumTileShowName: 0 metroMediumTileShowName: 0
metroLargeTileShowName: 0 metroLargeTileShowName: 0

View File

@@ -1,904 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!129 &1
PlayerSettings:
m_ObjectHideFlags: 0
serializedVersion: 23
productGUID: f3f6a917a3bba0046bb55998f8678f8c
AndroidProfiler: 0
AndroidFilterTouchesWhenObscured: 0
AndroidEnableSustainedPerformanceMode: 0
defaultScreenOrientation: 4
targetDevice: 2
useOnDemandResources: 0
accelerometerFrequency: 60
companyName: GameCI
productName: TestProject
defaultCursor: {fileID: 0}
cursorHotspot: {x: 0, y: 0}
m_SplashScreenBackgroundColor: {r: 0.13725491, g: 0.12156863, b: 0.1254902, a: 1}
m_ShowUnitySplashScreen: 1
m_ShowUnitySplashLogo: 1
m_SplashScreenOverlayOpacity: 1
m_SplashScreenAnimation: 1
m_SplashScreenLogoStyle: 1
m_SplashScreenDrawMode: 0
m_SplashScreenBackgroundAnimationZoom: 1
m_SplashScreenLogoAnimationZoom: 1
m_SplashScreenBackgroundLandscapeAspect: 1
m_SplashScreenBackgroundPortraitAspect: 1
m_SplashScreenBackgroundLandscapeUvs:
serializedVersion: 2
x: 0
y: 0
width: 1
height: 1
m_SplashScreenBackgroundPortraitUvs:
serializedVersion: 2
x: 0
y: 0
width: 1
height: 1
m_SplashScreenLogos: []
m_VirtualRealitySplashScreen: {fileID: 0}
m_HolographicTrackingLossScreen: {fileID: 0}
defaultScreenWidth: 1920
defaultScreenHeight: 1080
defaultScreenWidthWeb: 960
defaultScreenHeightWeb: 600
m_StereoRenderingPath: 0
m_ActiveColorSpace: 0
m_MTRendering: 1
mipStripping: 0
numberOfMipsStripped: 0
m_StackTraceTypes: 010000000100000001000000010000000100000001000000
iosShowActivityIndicatorOnLoading: -1
androidShowActivityIndicatorOnLoading: -1
iosUseCustomAppBackgroundBehavior: 0
iosAllowHTTPDownload: 1
allowedAutorotateToPortrait: 1
allowedAutorotateToPortraitUpsideDown: 1
allowedAutorotateToLandscapeRight: 1
allowedAutorotateToLandscapeLeft: 1
useOSAutorotation: 1
use32BitDisplayBuffer: 1
preserveFramebufferAlpha: 0
disableDepthAndStencilBuffers: 0
androidStartInFullscreen: 1
androidRenderOutsideSafeArea: 1
androidUseSwappy: 1
androidBlitType: 0
androidResizableWindow: 0
androidDefaultWindowWidth: 1920
androidDefaultWindowHeight: 1080
androidMinimumWindowWidth: 400
androidMinimumWindowHeight: 300
androidFullscreenMode: 1
defaultIsNativeResolution: 1
macRetinaSupport: 1
runInBackground: 1
captureSingleScreen: 0
muteOtherAudioSources: 0
Prepare IOS For Recording: 0
Force IOS Speakers When Recording: 0
deferSystemGesturesMode: 0
hideHomeButton: 0
submitAnalytics: 1
usePlayerLog: 1
bakeCollisionMeshes: 0
forceSingleInstance: 0
useFlipModelSwapchain: 1
resizableWindow: 0
useMacAppStoreValidation: 0
macAppStoreCategory: public.app-category.games
gpuSkinning: 1
xboxPIXTextureCapture: 0
xboxEnableAvatar: 0
xboxEnableKinect: 0
xboxEnableKinectAutoTracking: 0
xboxEnableFitness: 0
visibleInBackground: 1
allowFullscreenSwitch: 1
fullscreenMode: 1
xboxSpeechDB: 0
xboxEnableHeadOrientation: 0
xboxEnableGuest: 0
xboxEnablePIXSampling: 0
metalFramebufferOnly: 0
xboxOneResolution: 0
xboxOneSResolution: 0
xboxOneXResolution: 3
xboxOneMonoLoggingLevel: 0
xboxOneLoggingLevel: 1
xboxOneDisableEsram: 0
xboxOneEnableTypeOptimization: 0
xboxOnePresentImmediateThreshold: 0
switchQueueCommandMemory: 0
switchQueueControlMemory: 16384
switchQueueComputeMemory: 262144
switchNVNShaderPoolsGranularity: 33554432
switchNVNDefaultPoolsGranularity: 16777216
switchNVNOtherPoolsGranularity: 16777216
switchNVNMaxPublicTextureIDCount: 0
switchNVNMaxPublicSamplerIDCount: 0
stadiaPresentMode: 0
stadiaTargetFramerate: 0
vulkanNumSwapchainBuffers: 3
vulkanEnableSetSRGBWrite: 0
vulkanEnablePreTransform: 1
vulkanEnableLateAcquireNextImage: 0
vulkanEnableCommandBufferRecycling: 1
m_SupportedAspectRatios:
4:3: 1
5:4: 1
16:10: 1
16:9: 1
Others: 1
bundleVersion: 0.1
preloadedAssets: []
metroInputSource: 0
wsaTransparentSwapchain: 0
m_HolographicPauseOnTrackingLoss: 1
xboxOneDisableKinectGpuReservation: 1
xboxOneEnable7thCore: 1
vrSettings:
enable360StereoCapture: 0
isWsaHolographicRemotingEnabled: 0
enableFrameTimingStats: 0
enableOpenGLProfilerGPURecorders: 1
useHDRDisplay: 0
D3DHDRBitDepth: 0
m_ColorGamuts: 00000000
targetPixelDensity: 30
resolutionScalingMode: 0
resetResolutionOnWindowResize: 0
androidSupportedAspectRatio: 1
androidMaxAspectRatio: 2.1
applicationIdentifier:
Android: com.GameCI.TestProject
Standalone: com.GameCI.TestProject
iPhone: com.GameCI.TestProject
buildNumber:
Standalone: 0
iPhone: 0
tvOS: 0
overrideDefaultApplicationIdentifier: 0
AndroidBundleVersionCode: 1
AndroidMinSdkVersion: 22
AndroidTargetSdkVersion: 33
AndroidPreferredInstallLocation: 1
aotOptions:
stripEngineCode: 1
iPhoneStrippingLevel: 0
iPhoneScriptCallOptimization: 0
ForceInternetPermission: 0
ForceSDCardPermission: 0
CreateWallpaper: 0
APKExpansionFiles: 0
keepLoadedShadersAlive: 0
StripUnusedMeshComponents: 1
VertexChannelCompressionMask: 4054
iPhoneSdkVersion: 988
iOSTargetOSVersionString: 11.0
tvOSSdkVersion: 0
tvOSRequireExtendedGameController: 0
tvOSTargetOSVersionString: 11.0
uIPrerenderedIcon: 0
uIRequiresPersistentWiFi: 0
uIRequiresFullScreen: 1
uIStatusBarHidden: 1
uIExitOnSuspend: 0
uIStatusBarStyle: 0
appleTVSplashScreen: {fileID: 0}
appleTVSplashScreen2x: {fileID: 0}
tvOSSmallIconLayers: []
tvOSSmallIconLayers2x: []
tvOSLargeIconLayers: []
tvOSLargeIconLayers2x: []
tvOSTopShelfImageLayers: []
tvOSTopShelfImageLayers2x: []
tvOSTopShelfImageWideLayers: []
tvOSTopShelfImageWideLayers2x: []
iOSLaunchScreenType: 0
iOSLaunchScreenPortrait: {fileID: 0}
iOSLaunchScreenLandscape: {fileID: 0}
iOSLaunchScreenBackgroundColor:
serializedVersion: 2
rgba: 0
iOSLaunchScreenFillPct: 100
iOSLaunchScreenSize: 100
iOSLaunchScreenCustomXibPath:
iOSLaunchScreeniPadType: 0
iOSLaunchScreeniPadImage: {fileID: 0}
iOSLaunchScreeniPadBackgroundColor:
serializedVersion: 2
rgba: 0
iOSLaunchScreeniPadFillPct: 100
iOSLaunchScreeniPadSize: 100
iOSLaunchScreeniPadCustomXibPath:
iOSLaunchScreenCustomStoryboardPath:
iOSLaunchScreeniPadCustomStoryboardPath:
iOSDeviceRequirements: []
iOSURLSchemes: []
macOSURLSchemes: []
iOSBackgroundModes: 0
iOSMetalForceHardShadows: 0
metalEditorSupport: 1
metalAPIValidation: 1
iOSRenderExtraFrameOnPause: 0
iosCopyPluginsCodeInsteadOfSymlink: 0
appleDeveloperTeamID:
iOSManualSigningProvisioningProfileID:
tvOSManualSigningProvisioningProfileID:
iOSManualSigningProvisioningProfileType: 0
tvOSManualSigningProvisioningProfileType: 0
appleEnableAutomaticSigning: 0
iOSRequireARKit: 0
iOSAutomaticallyDetectAndAddCapabilities: 1
appleEnableProMotion: 0
shaderPrecisionModel: 0
clonedFromGUID: c0afd0d1d80e3634a9dac47e8a0426ea
templatePackageId: com.unity.template.3d@8.1.0
templateDefaultScene: Assets/Scenes/SampleScene.unity
useCustomMainManifest: 0
useCustomLauncherManifest: 0
useCustomMainGradleTemplate: 0
useCustomLauncherGradleManifest: 0
useCustomBaseGradleTemplate: 0
useCustomGradlePropertiesTemplate: 0
useCustomProguardFile: 0
AndroidTargetArchitectures: 3
AndroidTargetDevices: 0
AndroidSplashScreenScale: 0
androidSplashScreen: {fileID: 0}
AndroidKeystoreName:
AndroidKeyaliasName:
AndroidBuildApkPerCpuArchitecture: 0
AndroidTVCompatibility: 0
AndroidIsGame: 1
AndroidEnableTango: 0
androidEnableBanner: 1
androidUseLowAccuracyLocation: 0
androidUseCustomKeystore: 0
m_AndroidBanners:
- width: 320
height: 180
banner: {fileID: 0}
androidGamepadSupportLevel: 0
chromeosInputEmulation: 1
AndroidMinifyWithR8: 0
AndroidMinifyRelease: 0
AndroidMinifyDebug: 0
AndroidValidateAppBundleSize: 1
AndroidAppBundleSizeToValidate: 150
m_BuildTargetIcons: []
m_BuildTargetPlatformIcons:
- m_BuildTarget: Android
m_Icons:
- m_Textures: []
m_Width: 432
m_Height: 432
m_Kind: 2
m_SubKind:
- m_Textures: []
m_Width: 324
m_Height: 324
m_Kind: 2
m_SubKind:
- m_Textures: []
m_Width: 216
m_Height: 216
m_Kind: 2
m_SubKind:
- m_Textures: []
m_Width: 162
m_Height: 162
m_Kind: 2
m_SubKind:
- m_Textures: []
m_Width: 108
m_Height: 108
m_Kind: 2
m_SubKind:
- m_Textures: []
m_Width: 81
m_Height: 81
m_Kind: 2
m_SubKind:
- m_Textures: []
m_Width: 192
m_Height: 192
m_Kind: 1
m_SubKind:
- m_Textures: []
m_Width: 144
m_Height: 144
m_Kind: 1
m_SubKind:
- m_Textures: []
m_Width: 96
m_Height: 96
m_Kind: 1
m_SubKind:
- m_Textures: []
m_Width: 72
m_Height: 72
m_Kind: 1
m_SubKind:
- m_Textures: []
m_Width: 48
m_Height: 48
m_Kind: 1
m_SubKind:
- m_Textures: []
m_Width: 36
m_Height: 36
m_Kind: 1
m_SubKind:
- m_Textures: []
m_Width: 192
m_Height: 192
m_Kind: 0
m_SubKind:
- m_Textures: []
m_Width: 144
m_Height: 144
m_Kind: 0
m_SubKind:
- m_Textures: []
m_Width: 96
m_Height: 96
m_Kind: 0
m_SubKind:
- m_Textures: []
m_Width: 72
m_Height: 72
m_Kind: 0
m_SubKind:
- m_Textures: []
m_Width: 48
m_Height: 48
m_Kind: 0
m_SubKind:
- m_Textures: []
m_Width: 36
m_Height: 36
m_Kind: 0
m_SubKind:
- m_BuildTarget: iPhone
m_Icons:
- m_Textures: []
m_Width: 180
m_Height: 180
m_Kind: 0
m_SubKind: iPhone
- m_Textures: []
m_Width: 120
m_Height: 120
m_Kind: 0
m_SubKind: iPhone
- m_Textures: []
m_Width: 167
m_Height: 167
m_Kind: 0
m_SubKind: iPad
- m_Textures: []
m_Width: 152
m_Height: 152
m_Kind: 0
m_SubKind: iPad
- m_Textures: []
m_Width: 76
m_Height: 76
m_Kind: 0
m_SubKind: iPad
- m_Textures: []
m_Width: 120
m_Height: 120
m_Kind: 3
m_SubKind: iPhone
- m_Textures: []
m_Width: 80
m_Height: 80
m_Kind: 3
m_SubKind: iPhone
- m_Textures: []
m_Width: 80
m_Height: 80
m_Kind: 3
m_SubKind: iPad
- m_Textures: []
m_Width: 40
m_Height: 40
m_Kind: 3
m_SubKind: iPad
- m_Textures: []
m_Width: 87
m_Height: 87
m_Kind: 1
m_SubKind: iPhone
- m_Textures: []
m_Width: 58
m_Height: 58
m_Kind: 1
m_SubKind: iPhone
- m_Textures: []
m_Width: 29
m_Height: 29
m_Kind: 1
m_SubKind: iPhone
- m_Textures: []
m_Width: 58
m_Height: 58
m_Kind: 1
m_SubKind: iPad
- m_Textures: []
m_Width: 29
m_Height: 29
m_Kind: 1
m_SubKind: iPad
- m_Textures: []
m_Width: 60
m_Height: 60
m_Kind: 2
m_SubKind: iPhone
- m_Textures: []
m_Width: 40
m_Height: 40
m_Kind: 2
m_SubKind: iPhone
- m_Textures: []
m_Width: 40
m_Height: 40
m_Kind: 2
m_SubKind: iPad
- m_Textures: []
m_Width: 20
m_Height: 20
m_Kind: 2
m_SubKind: iPad
- m_Textures: []
m_Width: 1024
m_Height: 1024
m_Kind: 4
m_SubKind: App Store
m_BuildTargetBatching:
- m_BuildTarget: Standalone
m_StaticBatching: 1
m_DynamicBatching: 0
- m_BuildTarget: tvOS
m_StaticBatching: 1
m_DynamicBatching: 0
- m_BuildTarget: Android
m_StaticBatching: 1
m_DynamicBatching: 0
- m_BuildTarget: iPhone
m_StaticBatching: 1
m_DynamicBatching: 0
- m_BuildTarget: WebGL
m_StaticBatching: 0
m_DynamicBatching: 0
m_BuildTargetGraphicsJobs:
- m_BuildTarget: MacStandaloneSupport
m_GraphicsJobs: 0
- m_BuildTarget: Switch
m_GraphicsJobs: 1
- m_BuildTarget: MetroSupport
m_GraphicsJobs: 1
- m_BuildTarget: AppleTVSupport
m_GraphicsJobs: 0
- m_BuildTarget: BJMSupport
m_GraphicsJobs: 1
- m_BuildTarget: LinuxStandaloneSupport
m_GraphicsJobs: 1
- m_BuildTarget: PS4Player
m_GraphicsJobs: 1
- m_BuildTarget: iOSSupport
m_GraphicsJobs: 0
- m_BuildTarget: WindowsStandaloneSupport
m_GraphicsJobs: 1
- m_BuildTarget: XboxOnePlayer
m_GraphicsJobs: 1
- m_BuildTarget: LuminSupport
m_GraphicsJobs: 0
- m_BuildTarget: AndroidPlayer
m_GraphicsJobs: 0
- m_BuildTarget: WebGLSupport
m_GraphicsJobs: 0
m_BuildTargetGraphicsJobMode:
- m_BuildTarget: PS4Player
m_GraphicsJobMode: 0
- m_BuildTarget: XboxOnePlayer
m_GraphicsJobMode: 0
m_BuildTargetGraphicsAPIs:
- m_BuildTarget: AndroidPlayer
m_APIs: 150000000b000000
m_Automatic: 1
- m_BuildTarget: iOSSupport
m_APIs: 10000000
m_Automatic: 1
- m_BuildTarget: AppleTVSupport
m_APIs: 10000000
m_Automatic: 1
- m_BuildTarget: WebGLSupport
m_APIs: 0b000000
m_Automatic: 1
m_BuildTargetVRSettings:
- m_BuildTarget: Standalone
m_Enabled: 0
m_Devices:
- Oculus
- OpenVR
openGLRequireES31: 0
openGLRequireES31AEP: 0
openGLRequireES32: 0
m_TemplateCustomTags: {}
mobileMTRendering:
Android: 1
iPhone: 1
tvOS: 1
m_BuildTargetGroupLightmapEncodingQuality:
- m_BuildTarget: Android
m_EncodingQuality: 1
- m_BuildTarget: iPhone
m_EncodingQuality: 1
- m_BuildTarget: tvOS
m_EncodingQuality: 1
m_BuildTargetGroupLightmapSettings: []
m_BuildTargetNormalMapEncoding:
- m_BuildTarget: Android
m_Encoding: 1
- m_BuildTarget: iPhone
m_Encoding: 1
- m_BuildTarget: tvOS
m_Encoding: 1
m_BuildTargetDefaultTextureCompressionFormat:
- m_BuildTarget: Android
m_Format: 3
playModeTestRunnerEnabled: 0
runPlayModeTestAsEditModeTest: 0
actionOnDotNetUnhandledException: 1
enableInternalProfiler: 0
logObjCUncaughtExceptions: 1
enableCrashReportAPI: 0
cameraUsageDescription:
locationUsageDescription:
microphoneUsageDescription:
bluetoothUsageDescription:
switchNMETAOverride:
switchNetLibKey:
switchSocketMemoryPoolSize: 6144
switchSocketAllocatorPoolSize: 128
switchSocketConcurrencyLimit: 14
switchScreenResolutionBehavior: 2
switchUseCPUProfiler: 0
switchUseGOLDLinker: 0
switchLTOSetting: 0
switchApplicationID: 0x01004b9000490000
switchNSODependencies:
switchTitleNames_0:
switchTitleNames_1:
switchTitleNames_2:
switchTitleNames_3:
switchTitleNames_4:
switchTitleNames_5:
switchTitleNames_6:
switchTitleNames_7:
switchTitleNames_8:
switchTitleNames_9:
switchTitleNames_10:
switchTitleNames_11:
switchTitleNames_12:
switchTitleNames_13:
switchTitleNames_14:
switchTitleNames_15:
switchPublisherNames_0:
switchPublisherNames_1:
switchPublisherNames_2:
switchPublisherNames_3:
switchPublisherNames_4:
switchPublisherNames_5:
switchPublisherNames_6:
switchPublisherNames_7:
switchPublisherNames_8:
switchPublisherNames_9:
switchPublisherNames_10:
switchPublisherNames_11:
switchPublisherNames_12:
switchPublisherNames_13:
switchPublisherNames_14:
switchPublisherNames_15:
switchIcons_0: {fileID: 0}
switchIcons_1: {fileID: 0}
switchIcons_2: {fileID: 0}
switchIcons_3: {fileID: 0}
switchIcons_4: {fileID: 0}
switchIcons_5: {fileID: 0}
switchIcons_6: {fileID: 0}
switchIcons_7: {fileID: 0}
switchIcons_8: {fileID: 0}
switchIcons_9: {fileID: 0}
switchIcons_10: {fileID: 0}
switchIcons_11: {fileID: 0}
switchIcons_12: {fileID: 0}
switchIcons_13: {fileID: 0}
switchIcons_14: {fileID: 0}
switchIcons_15: {fileID: 0}
switchSmallIcons_0: {fileID: 0}
switchSmallIcons_1: {fileID: 0}
switchSmallIcons_2: {fileID: 0}
switchSmallIcons_3: {fileID: 0}
switchSmallIcons_4: {fileID: 0}
switchSmallIcons_5: {fileID: 0}
switchSmallIcons_6: {fileID: 0}
switchSmallIcons_7: {fileID: 0}
switchSmallIcons_8: {fileID: 0}
switchSmallIcons_9: {fileID: 0}
switchSmallIcons_10: {fileID: 0}
switchSmallIcons_11: {fileID: 0}
switchSmallIcons_12: {fileID: 0}
switchSmallIcons_13: {fileID: 0}
switchSmallIcons_14: {fileID: 0}
switchSmallIcons_15: {fileID: 0}
switchManualHTML:
switchAccessibleURLs:
switchLegalInformation:
switchMainThreadStackSize: 1048576
switchPresenceGroupId:
switchLogoHandling: 0
switchReleaseVersion: 0
switchDisplayVersion: 1.0.0
switchStartupUserAccount: 0
switchTouchScreenUsage: 0
switchSupportedLanguagesMask: 0
switchLogoType: 0
switchApplicationErrorCodeCategory:
switchUserAccountSaveDataSize: 0
switchUserAccountSaveDataJournalSize: 0
switchApplicationAttribute: 0
switchCardSpecSize: -1
switchCardSpecClock: -1
switchRatingsMask: 0
switchRatingsInt_0: 0
switchRatingsInt_1: 0
switchRatingsInt_2: 0
switchRatingsInt_3: 0
switchRatingsInt_4: 0
switchRatingsInt_5: 0
switchRatingsInt_6: 0
switchRatingsInt_7: 0
switchRatingsInt_8: 0
switchRatingsInt_9: 0
switchRatingsInt_10: 0
switchRatingsInt_11: 0
switchRatingsInt_12: 0
switchLocalCommunicationIds_0:
switchLocalCommunicationIds_1:
switchLocalCommunicationIds_2:
switchLocalCommunicationIds_3:
switchLocalCommunicationIds_4:
switchLocalCommunicationIds_5:
switchLocalCommunicationIds_6:
switchLocalCommunicationIds_7:
switchParentalControl: 0
switchAllowsScreenshot: 1
switchAllowsVideoCapturing: 1
switchAllowsRuntimeAddOnContentInstall: 0
switchDataLossConfirmation: 0
switchUserAccountLockEnabled: 0
switchSystemResourceMemory: 16777216
switchSupportedNpadStyles: 22
switchNativeFsCacheSize: 32
switchIsHoldTypeHorizontal: 0
switchSupportedNpadCount: 8
switchSocketConfigEnabled: 0
switchTcpInitialSendBufferSize: 32
switchTcpInitialReceiveBufferSize: 64
switchTcpAutoSendBufferSizeMax: 256
switchTcpAutoReceiveBufferSizeMax: 256
switchUdpSendBufferSize: 9
switchUdpReceiveBufferSize: 42
switchSocketBufferEfficiency: 4
switchSocketInitializeEnabled: 1
switchNetworkInterfaceManagerInitializeEnabled: 1
switchPlayerConnectionEnabled: 1
switchUseNewStyleFilepaths: 0
switchUseMicroSleepForYield: 1
switchEnableRamDiskSupport: 0
switchMicroSleepForYieldTime: 25
switchRamDiskSpaceSize: 12
ps4NPAgeRating: 12
ps4NPTitleSecret:
ps4NPTrophyPackPath:
ps4ParentalLevel: 11
ps4ContentID: ED1633-NPXX51362_00-0000000000000000
ps4Category: 0
ps4MasterVersion: 01.00
ps4AppVersion: 01.00
ps4AppType: 0
ps4ParamSfxPath:
ps4VideoOutPixelFormat: 0
ps4VideoOutInitialWidth: 1920
ps4VideoOutBaseModeInitialWidth: 1920
ps4VideoOutReprojectionRate: 60
ps4PronunciationXMLPath:
ps4PronunciationSIGPath:
ps4BackgroundImagePath:
ps4StartupImagePath:
ps4StartupImagesFolder:
ps4IconImagesFolder:
ps4SaveDataImagePath:
ps4SdkOverride:
ps4BGMPath:
ps4ShareFilePath:
ps4ShareOverlayImagePath:
ps4PrivacyGuardImagePath:
ps4ExtraSceSysFile:
ps4NPtitleDatPath:
ps4RemotePlayKeyAssignment: -1
ps4RemotePlayKeyMappingDir:
ps4PlayTogetherPlayerCount: 0
ps4EnterButtonAssignment: 1
ps4ApplicationParam1: 0
ps4ApplicationParam2: 0
ps4ApplicationParam3: 0
ps4ApplicationParam4: 0
ps4DownloadDataSize: 0
ps4GarlicHeapSize: 2048
ps4ProGarlicHeapSize: 2560
playerPrefsMaxSize: 32768
ps4Passcode: frAQBc8Wsa1xVPfvJcrgRYwTiizs2trQ
ps4pnSessions: 1
ps4pnPresence: 1
ps4pnFriends: 1
ps4pnGameCustomData: 1
playerPrefsSupport: 0
enableApplicationExit: 0
resetTempFolder: 1
restrictedAudioUsageRights: 0
ps4UseResolutionFallback: 0
ps4ReprojectionSupport: 0
ps4UseAudio3dBackend: 0
ps4UseLowGarlicFragmentationMode: 1
ps4SocialScreenEnabled: 0
ps4ScriptOptimizationLevel: 0
ps4Audio3dVirtualSpeakerCount: 14
ps4attribCpuUsage: 0
ps4PatchPkgPath:
ps4PatchLatestPkgPath:
ps4PatchChangeinfoPath:
ps4PatchDayOne: 0
ps4attribUserManagement: 0
ps4attribMoveSupport: 0
ps4attrib3DSupport: 0
ps4attribShareSupport: 0
ps4attribExclusiveVR: 0
ps4disableAutoHideSplash: 0
ps4videoRecordingFeaturesUsed: 0
ps4contentSearchFeaturesUsed: 0
ps4CompatibilityPS5: 0
ps4GPU800MHz: 1
ps4attribEyeToEyeDistanceSettingVR: 0
ps4IncludedModules: []
ps4attribVROutputEnabled: 0
monoEnv:
splashScreenBackgroundSourceLandscape: {fileID: 0}
splashScreenBackgroundSourcePortrait: {fileID: 0}
blurSplashScreenBackground: 1
spritePackerPolicy:
webGLMemorySize: 16
webGLExceptionSupport: 1
webGLNameFilesAsHashes: 0
webGLDataCaching: 1
webGLDebugSymbols: 0
webGLEmscriptenArgs:
webGLModulesDirectory:
webGLTemplate: APPLICATION:Default
webGLAnalyzeBuildSize: 0
webGLUseEmbeddedResources: 0
webGLCompressionFormat: 1
webGLWasmArithmeticExceptions: 0
webGLLinkerTarget: 1
webGLThreadsSupport: 0
webGLDecompressionFallback: 0
scriptingDefineSymbols: {}
additionalCompilerArguments: {}
platformArchitecture: {}
scriptingBackend:
Android: 1
Standalone: 1
il2cppCompilerConfiguration: {}
managedStrippingLevel: {}
incrementalIl2cppBuild: {}
suppressCommonWarnings: 1
allowUnsafeCode: 0
useDeterministicCompilation: 1
enableRoslynAnalyzers: 1
additionalIl2CppArgs:
scriptingRuntimeVersion: 1
gcIncremental: 1
assemblyVersionValidation: 1
gcWBarrierValidation: 0
apiCompatibilityLevelPerPlatform: {}
m_RenderingPath: 1
m_MobileRenderingPath: 1
metroPackageName: Template_3D
metroPackageVersion:
metroCertificatePath:
metroCertificatePassword:
metroCertificateSubject:
metroCertificateIssuer:
metroCertificateNotAfter: 0000000000000000
metroApplicationDescription: Template_3D
wsaImages: {}
metroTileShortName:
metroTileShowName: 0
metroMediumTileShowName: 0
metroLargeTileShowName: 0
metroWideTileShowName: 0
metroSupportStreamingInstall: 0
metroLastRequiredScene: 0
metroDefaultTileSize: 1
metroTileForegroundText: 2
metroTileBackgroundColor: {r: 0.13333334, g: 0.17254902, b: 0.21568628, a: 0}
metroSplashScreenBackgroundColor: {r: 0.12941177, g: 0.17254902, b: 0.21568628, a: 1}
metroSplashScreenUseBackgroundColor: 0
platformCapabilities: {}
metroTargetDeviceFamilies: {}
metroFTAName:
metroFTAFileTypes: []
metroProtocolName:
vcxProjDefaultLanguage:
XboxOneProductId:
XboxOneUpdateKey:
XboxOneSandboxId:
XboxOneContentId:
XboxOneTitleId:
XboxOneSCId:
XboxOneGameOsOverridePath:
XboxOnePackagingOverridePath:
XboxOneAppManifestOverridePath:
XboxOneVersion: 1.0.0.0
XboxOnePackageEncryption: 0
XboxOnePackageUpdateGranularity: 2
XboxOneDescription:
XboxOneLanguage:
- enus
XboxOneCapability: []
XboxOneGameRating: {}
XboxOneIsContentPackage: 0
XboxOneEnhancedXboxCompatibilityMode: 0
XboxOneEnableGPUVariability: 1
XboxOneSockets: {}
XboxOneSplashScreen: {fileID: 0}
XboxOneAllowedProductIds: []
XboxOnePersistentLocalStorageSize: 0
XboxOneXTitleMemory: 8
XboxOneOverrideIdentityName:
XboxOneOverrideIdentityPublisher:
vrEditorSettings: {}
cloudServicesEnabled:
UNet: 1
luminIcon:
m_Name:
m_ModelFolderPath:
m_PortalFolderPath:
luminCert:
m_CertPath:
m_SignPackage: 1
luminIsChannelApp: 0
luminVersion:
m_VersionCode: 1
m_VersionName:
apiCompatibilityLevel: 6
activeInputHandler: 0
cloudProjectId:
framebufferDepthMemorylessMode: 0
qualitySettingsNames: []
projectName:
organizationId:
cloudEnabled: 0
legacyClampBlendShapeWeights: 0
playerDataPath:
forceSRGBBlit: 1
virtualTexturingSupportEnabled: 0

935
yarn.lock

File diff suppressed because it is too large Load Diff