mirror of
https://github.com/game-ci/unity-builder.git
synced 2026-01-30 21:09:07 +08:00
Compare commits
1 Commits
cloud-runn
...
davidmfino
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1e53060e37 |
@@ -14,8 +14,7 @@
|
|||||||
"env": {
|
"env": {
|
||||||
"node": true,
|
"node": true,
|
||||||
"es6": true,
|
"es6": true,
|
||||||
"jest/globals": true,
|
"jest/globals": true
|
||||||
"es2020": true
|
|
||||||
},
|
},
|
||||||
"rules": {
|
"rules": {
|
||||||
// Error out for code formatting errors
|
// Error out for code formatting errors
|
||||||
@@ -39,21 +38,9 @@
|
|||||||
// Mandatory spacing
|
// Mandatory spacing
|
||||||
"padding-line-between-statements": [
|
"padding-line-between-statements": [
|
||||||
"error",
|
"error",
|
||||||
{
|
{ "blankLine": "always", "prev": "*", "next": "return" },
|
||||||
"blankLine": "always",
|
{ "blankLine": "always", "prev": "directive", "next": "*" },
|
||||||
"prev": "*",
|
{ "blankLine": "any", "prev": "directive", "next": "directive" }
|
||||||
"next": "return"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"blankLine": "always",
|
|
||||||
"prev": "directive",
|
|
||||||
"next": "*"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"blankLine": "any",
|
|
||||||
"prev": "directive",
|
|
||||||
"next": "directive"
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
// Enforce camelCase
|
// Enforce camelCase
|
||||||
"camelcase": "error",
|
"camelcase": "error",
|
||||||
@@ -66,25 +53,8 @@
|
|||||||
// Unused vars are useful to keep method signatures consistent and documented
|
// Unused vars are useful to keep method signatures consistent and documented
|
||||||
"@typescript-eslint/no-unused-vars": "off",
|
"@typescript-eslint/no-unused-vars": "off",
|
||||||
// For this project only use kebab-case
|
// For this project only use kebab-case
|
||||||
"unicorn/filename-case": [
|
"unicorn/filename-case": ["error", { "cases": { "kebabCase": true } }],
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"cases": {
|
|
||||||
"kebabCase": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
// Allow Array.from(set) mitigate TS2569 which would require '--downlevelIteration'
|
// Allow Array.from(set) mitigate TS2569 which would require '--downlevelIteration'
|
||||||
"unicorn/prefer-spread": "off",
|
"unicorn/prefer-spread": "off"
|
||||||
// Temp disable to prevent mixing changes with other PRs
|
}
|
||||||
"i18n-text/no-en": "off"
|
|
||||||
},
|
|
||||||
"overrides": [
|
|
||||||
{
|
|
||||||
"files": ["jest.setup.js"],
|
|
||||||
"rules": {
|
|
||||||
"import/no-commonjs": "off"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|||||||
20
.github/pull_request_template.md
vendored
20
.github/pull_request_template.md
vendored
@@ -2,28 +2,10 @@
|
|||||||
|
|
||||||
- ...
|
- ...
|
||||||
|
|
||||||
#### Related Issues
|
|
||||||
|
|
||||||
- ...
|
|
||||||
|
|
||||||
#### Related PRs
|
|
||||||
|
|
||||||
- ...
|
|
||||||
|
|
||||||
#### Successful Workflow Run Link
|
|
||||||
|
|
||||||
PRs don't have access to secrets so you will need to provide a link to a successful run of the workflows from your own
|
|
||||||
repo.
|
|
||||||
|
|
||||||
- ...
|
|
||||||
|
|
||||||
#### Checklist
|
#### Checklist
|
||||||
|
|
||||||
<!-- please check all items and add your own -->
|
<!-- please check all items and add your own -->
|
||||||
|
|
||||||
- [x] Read the contribution [guide](https://github.com/game-ci/unity-builder/blob/main/CONTRIBUTING.md) and accept the
|
- [x] Read the contribution [guide](../CONTRIBUTING.md) and accept the [code](../CODE_OF_CONDUCT.md) of conduct
|
||||||
[code](https://github.com/game-ci/unity-builder/blob/main/CODE_OF_CONDUCT.md) of conduct
|
|
||||||
- [ ] Docs (If new inputs or outputs have been added or changes to behavior that should be documented. Please make a PR
|
|
||||||
in the [documentation repo](https://github.com/game-ci/documentation))
|
|
||||||
- [ ] Readme (updated or not needed)
|
- [ ] Readme (updated or not needed)
|
||||||
- [ ] Tests (added, updated or not needed)
|
- [ ] Tests (added, updated or not needed)
|
||||||
|
|||||||
2
.github/workflows/activation.yml
vendored
2
.github/workflows/activation.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
|||||||
id: requestActivationFile
|
id: requestActivationFile
|
||||||
uses: game-ci/unity-request-activation-file@v2.0-alpha-1
|
uses: game-ci/unity-request-activation-file@v2.0-alpha-1
|
||||||
- name: Upload activation file
|
- name: Upload activation file
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: ${{ steps.requestActivationFile.outputs.filePath }}
|
name: ${{ steps.requestActivationFile.outputs.filePath }}
|
||||||
path: ${{ steps.requestActivationFile.outputs.filePath }}
|
path: ${{ steps.requestActivationFile.outputs.filePath }}
|
||||||
|
|||||||
87
.github/workflows/build-tests-mac.yml
vendored
87
.github/workflows/build-tests-mac.yml
vendored
@@ -1,87 +0,0 @@
|
|||||||
name: Builds - MacOS
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
push:
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
buildForAllPlatformsMacOS:
|
|
||||||
name: ${{ matrix.targetPlatform }} on ${{ matrix.unityVersion }}
|
|
||||||
runs-on: macos-latest
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
projectPath:
|
|
||||||
- test-project
|
|
||||||
unityVersion:
|
|
||||||
- 2021.3.45f1
|
|
||||||
- 2022.3.13f1
|
|
||||||
- 2023.2.2f1
|
|
||||||
targetPlatform:
|
|
||||||
- StandaloneOSX # Build a MacOS executable
|
|
||||||
- iOS # Build an iOS executable
|
|
||||||
include:
|
|
||||||
# Additionally test enableGpu build for a standalone windows target
|
|
||||||
- unityVersion: 6000.0.36f1
|
|
||||||
targetPlatform: StandaloneOSX
|
|
||||||
- unityVersion: 6000.0.36f1
|
|
||||||
targetPlatform: StandaloneOSX
|
|
||||||
buildProfile: 'Assets/Settings/Build Profiles/Sample macOS Build Profile.asset'
|
|
||||||
|
|
||||||
steps:
|
|
||||||
###########################
|
|
||||||
# Checkout #
|
|
||||||
###########################
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
lfs: true
|
|
||||||
|
|
||||||
###########################
|
|
||||||
# Cache #
|
|
||||||
###########################
|
|
||||||
- uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: ${{ matrix.projectPath }}/Library
|
|
||||||
key: Library-${{ matrix.projectPath }}-macos-${{ matrix.targetPlatform }}
|
|
||||||
restore-keys: |
|
|
||||||
Library-${{ matrix.projectPath }}-macos-
|
|
||||||
Library-
|
|
||||||
|
|
||||||
###########################
|
|
||||||
# Set Scripting Backend #
|
|
||||||
###########################
|
|
||||||
- name: Set Scripting Backend To il2cpp
|
|
||||||
run: |
|
|
||||||
mv -f "./test-project/ProjectSettings/ProjectSettingsIl2cpp.asset" "./test-project/ProjectSettings/ProjectSettings.asset"
|
|
||||||
|
|
||||||
###########################
|
|
||||||
# Build #
|
|
||||||
###########################
|
|
||||||
- uses: ./
|
|
||||||
env:
|
|
||||||
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
|
|
||||||
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
|
|
||||||
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
|
|
||||||
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
|
|
||||||
with:
|
|
||||||
buildName: 'GameCI Test Build'
|
|
||||||
projectPath: ${{ matrix.projectPath }}
|
|
||||||
unityVersion: ${{ matrix.unityVersion }}
|
|
||||||
targetPlatform: ${{ matrix.targetPlatform }}
|
|
||||||
buildProfile: ${{ matrix.buildProfile }}
|
|
||||||
customParameters: -profile SomeProfile -someBoolean -someValue exampleValue
|
|
||||||
# We use dirty build because we are replacing the default project settings file above
|
|
||||||
allowDirtyBuild: true
|
|
||||||
|
|
||||||
###########################
|
|
||||||
# Upload #
|
|
||||||
###########################
|
|
||||||
- uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: Build ${{ matrix.targetPlatform }} on MacOS (${{ matrix.unityVersion }})${{ matrix.buildProfile && ' With Build Profile' || '' }}
|
|
||||||
path: build
|
|
||||||
retention-days: 14
|
|
||||||
206
.github/workflows/build-tests-ubuntu.yml
vendored
206
.github/workflows/build-tests-ubuntu.yml
vendored
@@ -1,206 +0,0 @@
|
|||||||
name: Builds - Ubuntu
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
push:
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
env:
|
|
||||||
UNITY_LICENSE:
|
|
||||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?><root>\n <License
|
|
||||||
id=\"Terms\">\n <MachineBindings>\n <Binding Key=\"1\"
|
|
||||||
Value=\"576562626572264761624c65526f7578\"/>\n <Binding Key=\"2\"
|
|
||||||
Value=\"576562626572264761624c65526f7578\"/>\n </MachineBindings>\n <MachineID
|
|
||||||
Value=\"D7nTUnjNAmtsUMcnoyrqkgIbYdM=\"/>\n <SerialHash
|
|
||||||
Value=\"2033b8ac3e6faa3742ca9f0bfae44d18f2a96b80\"/>\n <Features>\n <Feature
|
|
||||||
Value=\"33\"/>\n <Feature Value=\"1\"/>\n <Feature Value=\"12\"/>\n <Feature
|
|
||||||
Value=\"2\"/>\n <Feature Value=\"24\"/>\n <Feature Value=\"3\"/>\n <Feature
|
|
||||||
Value=\"36\"/>\n <Feature Value=\"17\"/>\n <Feature Value=\"19\"/>\n <Feature
|
|
||||||
Value=\"62\"/>\n </Features>\n <DeveloperData
|
|
||||||
Value=\"AQAAAEY0LUJHUlgtWEQ0RS1aQ1dWLUM1SlctR0RIQg==\"/>\n <SerialMasked
|
|
||||||
Value=\"F4-BGRX-XD4E-ZCWV-C5JW-XXXX\"/>\n <StartDate Value=\"2021-02-08T00:00:00\"/>\n <UpdateDate
|
|
||||||
Value=\"2021-02-09T00:34:57\"/>\n <InitialActivationDate
|
|
||||||
Value=\"2021-02-08T00:34:56\"/>\n <LicenseVersion Value=\"6.x\"/>\n <ClientProvidedVersion
|
|
||||||
Value=\"2018.4.30f1\"/>\n <AlwaysOnline Value=\"false\"/>\n <Entitlements>\n <Entitlement
|
|
||||||
Ns=\"unity_editor\" Tag=\"UnityPersonal\" Type=\"EDITOR\"
|
|
||||||
ValidTo=\"9999-12-31T00:00:00\"/>\n <Entitlement Ns=\"unity_editor\" Tag=\"DarkSkin\"
|
|
||||||
Type=\"EDITOR_FEATURE\" ValidTo=\"9999-12-31T00:00:00\"/>\n </Entitlements>\n </License>\n<Signature
|
|
||||||
xmlns=\"http://www.w3.org/2000/09/xmldsig#\"><SignedInfo><CanonicalizationMethod
|
|
||||||
Algorithm=\"http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments\"/><SignatureMethod
|
|
||||||
Algorithm=\"http://www.w3.org/2000/09/xmldsig#rsa-sha1\"/><Reference URI=\"#Terms\"><Transforms><Transform
|
|
||||||
Algorithm=\"http://www.w3.org/2000/09/xmldsig#enveloped-signature\"/></Transforms><DigestMethod
|
|
||||||
Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\"/><DigestValue>m0Db8UK+ktnOLJBtHybkfetpcKo=</DigestValue></Reference></SignedInfo><SignatureValue>o/pUbSQAukz7+ZYAWhnA0AJbIlyyCPL7bKVEM2lVqbrXt7cyey+umkCXamuOgsWPVUKBMkXtMH8L\n5etLmD0getWIhTGhzOnDCk+gtIPfL4jMo9tkEuOCROQAXCci23VFscKcrkB+3X6h4wEOtA2APhOY\nB+wvC794o8/82ffjP79aVAi57rp3Wmzx+9pe9yMwoJuljAy2sc2tIMgdQGWVmOGBpQm3JqsidyzI\nJWG2kjnc7pDXK9pwYzXoKiqUqqrut90d+kQqRyv7MSZXR50HFqD/LI69h68b7P8Bjo3bPXOhNXGR\n9YCoemH6EkfCJxp2gIjzjWW+l2Hj2EsFQi8YXw==</SignatureValue></Signature></root>"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
buildForAllPlatformsUbuntu:
|
|
||||||
name:
|
|
||||||
"${{ matrix.targetPlatform }} on ${{ matrix.unityVersion}}${{startsWith(matrix.buildProfile, 'Assets') && ' (via Build Profile)' || '' }}"
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
providerStrategy:
|
|
||||||
# - local-docker
|
|
||||||
- local
|
|
||||||
projectPath:
|
|
||||||
- test-project
|
|
||||||
unityVersion:
|
|
||||||
- 2021.3.32f1
|
|
||||||
- 2022.3.13f1
|
|
||||||
- 2023.2.2f1
|
|
||||||
targetPlatform:
|
|
||||||
- StandaloneOSX # Build a macOS standalone (Intel 64-bit) with mono backend.
|
|
||||||
- StandaloneWindows64 # Build a Windows 64-bit standalone with mono backend.
|
|
||||||
- StandaloneLinux64 # Build a Linux 64-bit standalone with mono/il2cpp backend.
|
|
||||||
- iOS # Build an iOS project.
|
|
||||||
- Android # Build an Android .apk.
|
|
||||||
- WebGL # WebGL.
|
|
||||||
buildWithIl2cpp:
|
|
||||||
- false
|
|
||||||
- true
|
|
||||||
additionalParameters:
|
|
||||||
- -param value
|
|
||||||
- -standaloneBuildSubtarget Server
|
|
||||||
# Skipping configurations that are not supported
|
|
||||||
exclude:
|
|
||||||
# No il2cpp support on Linux Host
|
|
||||||
- targetPlatform: StandaloneOSX
|
|
||||||
buildWithIl2cpp: true
|
|
||||||
- targetPlatform: StandaloneWindows64
|
|
||||||
buildWithIl2cpp: true
|
|
||||||
# Only builds with Il2cpp
|
|
||||||
- targetPlatform: iOS
|
|
||||||
buildWithIl2cpp: false
|
|
||||||
- targetPlatform: Android
|
|
||||||
buildWithIl2cpp: false
|
|
||||||
- targetPlatform: WebGL
|
|
||||||
buildWithIl2cpp: false
|
|
||||||
# No dedicated server support
|
|
||||||
- targetPlatform: WebGL
|
|
||||||
additionalParameters: -standaloneBuildSubtarget Server
|
|
||||||
- targetPlatform: Android
|
|
||||||
additionalParameters: -standaloneBuildSubtarget Server
|
|
||||||
- targetPlatform: iOS
|
|
||||||
additionalParameters: -standaloneBuildSubtarget Server
|
|
||||||
# No dedicated server support on Linux Host
|
|
||||||
- targetPlatform: StandaloneOSX
|
|
||||||
additionalParameters: -standaloneBuildSubtarget Server
|
|
||||||
# No il2cpp dedicated server support on Linux Host
|
|
||||||
- targetPlatform: StandaloneWindows64
|
|
||||||
additionalParameters: -standaloneBuildSubtarget Server
|
|
||||||
buildWithIl2cpp: true
|
|
||||||
include:
|
|
||||||
- unityVersion: 6000.0.36f1
|
|
||||||
targetPlatform: WebGL
|
|
||||||
- unityVersion: 6000.0.36f1
|
|
||||||
targetPlatform: WebGL
|
|
||||||
buildProfile: 'Assets/Settings/Build Profiles/Sample WebGL Build Profile.asset'
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Clear Space for Android Build
|
|
||||||
if: matrix.targetPlatform == 'Android'
|
|
||||||
uses: jlumbroso/free-disk-space@v1.3.1
|
|
||||||
|
|
||||||
###########################
|
|
||||||
# Checkout #
|
|
||||||
###########################
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
lfs: true
|
|
||||||
|
|
||||||
###########################
|
|
||||||
# Cache #
|
|
||||||
###########################
|
|
||||||
- uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: ${{ matrix.projectPath }}/Library
|
|
||||||
key: Library-${{ matrix.projectPath }}-ubuntu-${{ matrix.targetPlatform }}
|
|
||||||
restore-keys: |
|
|
||||||
Library-${{ matrix.projectPath }}-ubuntu-
|
|
||||||
Library-
|
|
||||||
|
|
||||||
###########################
|
|
||||||
# Set Scripting Backend #
|
|
||||||
###########################
|
|
||||||
- name: Set Scripting Backend To il2cpp
|
|
||||||
if: matrix.buildWithIl2cpp == true
|
|
||||||
run: |
|
|
||||||
mv -f "./test-project/ProjectSettings/ProjectSettingsIl2cpp.asset" "./test-project/ProjectSettings/ProjectSettings.asset"
|
|
||||||
|
|
||||||
###########################
|
|
||||||
# Build #
|
|
||||||
###########################
|
|
||||||
- name: Build
|
|
||||||
uses: ./
|
|
||||||
id: build-1
|
|
||||||
continue-on-error: true
|
|
||||||
env:
|
|
||||||
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
|
|
||||||
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
|
|
||||||
with:
|
|
||||||
buildName: 'GameCI Test Build'
|
|
||||||
projectPath: ${{ matrix.projectPath }}
|
|
||||||
buildProfile: ${{ matrix.buildProfile }}
|
|
||||||
unityVersion: ${{ matrix.unityVersion }}
|
|
||||||
targetPlatform: ${{ matrix.targetPlatform }}
|
|
||||||
customParameters: -profile SomeProfile -someBoolean -someValue exampleValue ${{ matrix.additionalParameters }}
|
|
||||||
providerStrategy: ${{ matrix.providerStrategy }}
|
|
||||||
allowDirtyBuild: true
|
|
||||||
|
|
||||||
- name: Sleep for Retry
|
|
||||||
if: ${{ steps.build-1.outcome == 'failure' }}
|
|
||||||
run: |
|
|
||||||
sleep 60
|
|
||||||
|
|
||||||
- name: Build (Retry 1)
|
|
||||||
uses: ./
|
|
||||||
id: build-2
|
|
||||||
if: ${{ steps.build-1.outcome == 'failure' }}
|
|
||||||
continue-on-error: true
|
|
||||||
env:
|
|
||||||
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
|
|
||||||
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
|
|
||||||
with:
|
|
||||||
buildName: 'GameCI Test Build'
|
|
||||||
projectPath: ${{ matrix.projectPath }}
|
|
||||||
buildProfile: ${{ matrix.buildProfile }}
|
|
||||||
unityVersion: ${{ matrix.unityVersion }}
|
|
||||||
targetPlatform: ${{ matrix.targetPlatform }}
|
|
||||||
customParameters: -profile SomeProfile -someBoolean -someValue exampleValue ${{ matrix.additionalParameters }}
|
|
||||||
providerStrategy: ${{ matrix.providerStrategy }}
|
|
||||||
allowDirtyBuild: true
|
|
||||||
|
|
||||||
- name: Sleep for Retry
|
|
||||||
if: ${{ steps.build-2.outcome == 'failure' }}
|
|
||||||
run: |
|
|
||||||
sleep 240
|
|
||||||
|
|
||||||
- name: Build (Retry 2)
|
|
||||||
uses: ./
|
|
||||||
id: build-3
|
|
||||||
if: ${{ steps.build-2.outcome == 'failure' }}
|
|
||||||
env:
|
|
||||||
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
|
|
||||||
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
|
|
||||||
with:
|
|
||||||
buildName: 'GameCI Test Build'
|
|
||||||
projectPath: ${{ matrix.projectPath }}
|
|
||||||
buildProfile: ${{ matrix.buildProfile }}
|
|
||||||
unityVersion: ${{ matrix.unityVersion }}
|
|
||||||
targetPlatform: ${{ matrix.targetPlatform }}
|
|
||||||
customParameters: -profile SomeProfile -someBoolean -someValue exampleValue ${{ matrix.additionalParameters }}
|
|
||||||
providerStrategy: ${{ matrix.providerStrategy }}
|
|
||||||
allowDirtyBuild: true
|
|
||||||
|
|
||||||
###########################
|
|
||||||
# Upload #
|
|
||||||
###########################
|
|
||||||
- uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name:
|
|
||||||
"Build ${{ matrix.targetPlatform }}${{ startsWith(matrix.buildProfile, 'Assets') && ' (via Build Profile)' || '' }} on Ubuntu (${{ matrix.unityVersion }}_il2cpp_${{ matrix.buildWithIl2cpp }}_params_${{ matrix.additionalParameters }})"
|
|
||||||
path: build
|
|
||||||
retention-days: 14
|
|
||||||
151
.github/workflows/build-tests-windows.yml
vendored
151
.github/workflows/build-tests-windows.yml
vendored
@@ -1,151 +0,0 @@
|
|||||||
name: Builds - Windows
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
push:
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
buildForAllPlatformsWindows:
|
|
||||||
name: ${{ matrix.targetPlatform }} on ${{ matrix.unityVersion }}
|
|
||||||
runs-on: windows-2022
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
projectPath:
|
|
||||||
- test-project
|
|
||||||
unityVersion:
|
|
||||||
- 2021.3.32f1
|
|
||||||
- 2022.3.13f1
|
|
||||||
- 2023.2.2f1
|
|
||||||
targetPlatform:
|
|
||||||
- Android # Build an Android apk.
|
|
||||||
- StandaloneWindows64 # Build a Windows 64-bit standalone.
|
|
||||||
- WSAPlayer # Build a UWP App
|
|
||||||
- tvOS # Build an Apple TV XCode project
|
|
||||||
enableGpu:
|
|
||||||
- false
|
|
||||||
include:
|
|
||||||
# Additionally test enableGpu build for a standalone windows target
|
|
||||||
- projectPath: test-project
|
|
||||||
unityVersion: 2023.2.2f1
|
|
||||||
targetPlatform: StandaloneWindows64
|
|
||||||
enableGpu: true
|
|
||||||
- unityVersion: 6000.0.36f1
|
|
||||||
targetPlatform: StandaloneWindows64
|
|
||||||
- unityVersion: 6000.0.36f1
|
|
||||||
targetPlatform: StandaloneWindows64
|
|
||||||
buildProfile: 'Assets/Settings/Build Profiles/Sample Windows Build Profile.asset'
|
|
||||||
|
|
||||||
steps:
|
|
||||||
###########################
|
|
||||||
# Checkout #
|
|
||||||
###########################
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
lfs: true
|
|
||||||
|
|
||||||
###########################
|
|
||||||
# Cache #
|
|
||||||
###########################
|
|
||||||
- uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: ${{ matrix.projectPath }}/Library
|
|
||||||
key: Library-${{ matrix.projectPath }}-windows-${{ matrix.targetPlatform }}
|
|
||||||
restore-keys: |
|
|
||||||
Library-${{ matrix.projectPath }}-windows-
|
|
||||||
Library-
|
|
||||||
|
|
||||||
###########################
|
|
||||||
# Set Scripting Backend #
|
|
||||||
###########################
|
|
||||||
- name: Set Scripting Backend To il2cpp
|
|
||||||
run: |
|
|
||||||
Move-Item -Path "./test-project/ProjectSettings/ProjectSettingsIl2cpp.asset" -Destination "./test-project/ProjectSettings/ProjectSettings.asset" -Force
|
|
||||||
|
|
||||||
###########################
|
|
||||||
# Build #
|
|
||||||
###########################
|
|
||||||
- name: Build
|
|
||||||
uses: ./
|
|
||||||
id: build-1
|
|
||||||
continue-on-error: true
|
|
||||||
timeout-minutes: 30
|
|
||||||
env:
|
|
||||||
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
|
|
||||||
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
|
|
||||||
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
|
|
||||||
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
|
|
||||||
with:
|
|
||||||
buildName: 'GameCI Test Build'
|
|
||||||
projectPath: ${{ matrix.projectPath }}
|
|
||||||
unityVersion: ${{ matrix.unityVersion }}
|
|
||||||
targetPlatform: ${{ matrix.targetPlatform }}
|
|
||||||
buildProfile: ${{ matrix.buildProfile }}
|
|
||||||
enableGpu: ${{ matrix.enableGpu }}
|
|
||||||
customParameters: -profile SomeProfile -someBoolean -someValue exampleValue
|
|
||||||
allowDirtyBuild: true
|
|
||||||
# We use dirty build because we are replacing the default project settings file above
|
|
||||||
|
|
||||||
- name: Sleep for Retry
|
|
||||||
if: ${{ steps.build-1.outcome == 'failure' }}
|
|
||||||
run: |
|
|
||||||
Start-Sleep -s 120
|
|
||||||
|
|
||||||
- name: Build Retry 1
|
|
||||||
uses: ./
|
|
||||||
id: build-2
|
|
||||||
continue-on-error: true
|
|
||||||
timeout-minutes: 30
|
|
||||||
if: steps.build-1.outcome == 'failure'
|
|
||||||
env:
|
|
||||||
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
|
|
||||||
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
|
|
||||||
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
|
|
||||||
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
|
|
||||||
with:
|
|
||||||
buildName: 'GameCI Test Build'
|
|
||||||
projectPath: ${{ matrix.projectPath }}
|
|
||||||
unityVersion: ${{ matrix.unityVersion }}
|
|
||||||
targetPlatform: ${{ matrix.targetPlatform }}
|
|
||||||
enableGpu: ${{ matrix.enableGpu }}
|
|
||||||
customParameters: -profile SomeProfile -someBoolean -someValue exampleValue
|
|
||||||
allowDirtyBuild: true
|
|
||||||
# We use dirty build because we are replacing the default project settings file above
|
|
||||||
|
|
||||||
- name: Sleep for Retry
|
|
||||||
if: ${{ steps.build-1.outcome == 'failure' && steps.build-2.outcome == 'failure' }}
|
|
||||||
run: |
|
|
||||||
Start-Sleep -s 240
|
|
||||||
|
|
||||||
- name: Build Retry 2
|
|
||||||
uses: ./
|
|
||||||
id: build-3
|
|
||||||
timeout-minutes: 30
|
|
||||||
if: ${{ steps.build-1.outcome == 'failure' && steps.build-2.outcome == 'failure' }}
|
|
||||||
env:
|
|
||||||
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
|
|
||||||
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
|
|
||||||
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
|
|
||||||
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
|
|
||||||
with:
|
|
||||||
buildName: 'GameCI Test Build'
|
|
||||||
projectPath: ${{ matrix.projectPath }}
|
|
||||||
unityVersion: ${{ matrix.unityVersion }}
|
|
||||||
targetPlatform: ${{ matrix.targetPlatform }}
|
|
||||||
enableGpu: ${{ matrix.enableGpu }}
|
|
||||||
customParameters: -profile SomeProfile -someBoolean -someValue exampleValue
|
|
||||||
allowDirtyBuild: true
|
|
||||||
# We use dirty build because we are replacing the default project settings file above
|
|
||||||
|
|
||||||
###########################
|
|
||||||
# Upload #
|
|
||||||
###########################
|
|
||||||
- uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: Build ${{ matrix.targetPlatform }} on Windows (${{ matrix.unityVersion }})${{ matrix.enableGpu && ' With GPU' || '' }}${{ matrix.buildProfile && ' With Build Profile' || '' }}
|
|
||||||
path: build
|
|
||||||
retention-days: 14
|
|
||||||
100
.github/workflows/build-tests.yml
vendored
Normal file
100
.github/workflows/build-tests.yml
vendored
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
name: Builds
|
||||||
|
|
||||||
|
on:
|
||||||
|
push: { branches: [main] }
|
||||||
|
pull_request:
|
||||||
|
paths-ignore:
|
||||||
|
- '.github/**'
|
||||||
|
|
||||||
|
env:
|
||||||
|
UNITY_LICENSE:
|
||||||
|
"<?xml version=\"1.0\" encoding=\"UTF-8\"?><root>\n <License
|
||||||
|
id=\"Terms\">\n <MachineBindings>\n <Binding Key=\"1\"
|
||||||
|
Value=\"576562626572264761624c65526f7578\"/>\n <Binding Key=\"2\"
|
||||||
|
Value=\"576562626572264761624c65526f7578\"/>\n </MachineBindings>\n <MachineID
|
||||||
|
Value=\"D7nTUnjNAmtsUMcnoyrqkgIbYdM=\"/>\n <SerialHash
|
||||||
|
Value=\"2033b8ac3e6faa3742ca9f0bfae44d18f2a96b80\"/>\n <Features>\n <Feature
|
||||||
|
Value=\"33\"/>\n <Feature Value=\"1\"/>\n <Feature Value=\"12\"/>\n <Feature
|
||||||
|
Value=\"2\"/>\n <Feature Value=\"24\"/>\n <Feature Value=\"3\"/>\n <Feature
|
||||||
|
Value=\"36\"/>\n <Feature Value=\"17\"/>\n <Feature Value=\"19\"/>\n <Feature
|
||||||
|
Value=\"62\"/>\n </Features>\n <DeveloperData
|
||||||
|
Value=\"AQAAAEY0LUJHUlgtWEQ0RS1aQ1dWLUM1SlctR0RIQg==\"/>\n <SerialMasked
|
||||||
|
Value=\"F4-BGRX-XD4E-ZCWV-C5JW-XXXX\"/>\n <StartDate Value=\"2021-02-08T00:00:00\"/>\n <UpdateDate
|
||||||
|
Value=\"2021-02-09T00:34:57\"/>\n <InitialActivationDate
|
||||||
|
Value=\"2021-02-08T00:34:56\"/>\n <LicenseVersion Value=\"6.x\"/>\n <ClientProvidedVersion
|
||||||
|
Value=\"2018.4.30f1\"/>\n <AlwaysOnline Value=\"false\"/>\n <Entitlements>\n <Entitlement
|
||||||
|
Ns=\"unity_editor\" Tag=\"UnityPersonal\" Type=\"EDITOR\"
|
||||||
|
ValidTo=\"9999-12-31T00:00:00\"/>\n <Entitlement Ns=\"unity_editor\" Tag=\"DarkSkin\"
|
||||||
|
Type=\"EDITOR_FEATURE\" ValidTo=\"9999-12-31T00:00:00\"/>\n </Entitlements>\n </License>\n<Signature
|
||||||
|
xmlns=\"http://www.w3.org/2000/09/xmldsig#\"><SignedInfo><CanonicalizationMethod
|
||||||
|
Algorithm=\"http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments\"/><SignatureMethod
|
||||||
|
Algorithm=\"http://www.w3.org/2000/09/xmldsig#rsa-sha1\"/><Reference URI=\"#Terms\"><Transforms><Transform
|
||||||
|
Algorithm=\"http://www.w3.org/2000/09/xmldsig#enveloped-signature\"/></Transforms><DigestMethod
|
||||||
|
Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\"/><DigestValue>m0Db8UK+ktnOLJBtHybkfetpcKo=</DigestValue></Reference></SignedInfo><SignatureValue>o/pUbSQAukz7+ZYAWhnA0AJbIlyyCPL7bKVEM2lVqbrXt7cyey+umkCXamuOgsWPVUKBMkXtMH8L\n5etLmD0getWIhTGhzOnDCk+gtIPfL4jMo9tkEuOCROQAXCci23VFscKcrkB+3X6h4wEOtA2APhOY\nB+wvC794o8/82ffjP79aVAi57rp3Wmzx+9pe9yMwoJuljAy2sc2tIMgdQGWVmOGBpQm3JqsidyzI\nJWG2kjnc7pDXK9pwYzXoKiqUqqrut90d+kQqRyv7MSZXR50HFqD/LI69h68b7P8Bjo3bPXOhNXGR\n9YCoemH6EkfCJxp2gIjzjWW+l2Hj2EsFQi8YXw==</SignatureValue></Signature></root>"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
buildForAllPlatformsUbuntu:
|
||||||
|
name: Build for ${{ matrix.targetPlatform }} on version ${{ matrix.unityVersion }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
cloudRunnerCluster:
|
||||||
|
# - local-docker
|
||||||
|
- local
|
||||||
|
projectPath:
|
||||||
|
- test-project
|
||||||
|
unityVersion:
|
||||||
|
- 2019.2.11f1
|
||||||
|
- 2019.3.15f1
|
||||||
|
targetPlatform:
|
||||||
|
- StandaloneOSX # Build a macOS standalone (Intel 64-bit).
|
||||||
|
- StandaloneWindows64 # Build a Windows 64-bit standalone.
|
||||||
|
- StandaloneLinux64 # Build a Linux 64-bit standalone.
|
||||||
|
- iOS # Build an iOS player.
|
||||||
|
- Android # Build an Android .apk.
|
||||||
|
- WebGL # WebGL.
|
||||||
|
# - StandaloneWindows # Build a Windows standalone.
|
||||||
|
# - WSAPlayer # Build an Windows Store Apps player.
|
||||||
|
# - PS4 # Build a PS4 Standalone.
|
||||||
|
# - XboxOne # Build a Xbox One Standalone.
|
||||||
|
# - tvOS # Build to Apple's tvOS platform.
|
||||||
|
# - Switch # Build a Nintendo Switch player
|
||||||
|
steps:
|
||||||
|
###########################
|
||||||
|
# Checkout #
|
||||||
|
###########################
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
lfs: true
|
||||||
|
|
||||||
|
###########################
|
||||||
|
# Cache #
|
||||||
|
###########################
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: ${{ matrix.projectPath }}/Library
|
||||||
|
key: Library-${{ matrix.projectPath }}-ubuntu-${{ matrix.targetPlatform }}
|
||||||
|
restore-keys: |
|
||||||
|
Library-${{ matrix.projectPath }}-ubuntu-
|
||||||
|
Library-
|
||||||
|
|
||||||
|
###########################
|
||||||
|
# Build #
|
||||||
|
###########################
|
||||||
|
- uses: ./
|
||||||
|
with:
|
||||||
|
projectPath: ${{ matrix.projectPath }}
|
||||||
|
unityVersion: ${{ matrix.unityVersion }}
|
||||||
|
targetPlatform: ${{ matrix.targetPlatform }}
|
||||||
|
customParameters: -profile SomeProfile -someBoolean -someValue exampleValue
|
||||||
|
cloudRunnerCluster: ${{ matrix.cloudRunnerCluster }}
|
||||||
|
|
||||||
|
###########################
|
||||||
|
# Upload #
|
||||||
|
###########################
|
||||||
|
- uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: Build Ubuntu (${{ matrix.unityVersion }})
|
||||||
|
path: build
|
||||||
|
retention-days: 14
|
||||||
37
.github/workflows/cleanup.yml
vendored
Normal file
37
.github/workflows/cleanup.yml
vendored
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
name: Cleanup (cron)
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '30 10 * * SUN' # every sunday at 10:30
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deleteArtifacts:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Delete old artifacts
|
||||||
|
uses: kolpav/purge-artifacts-action@v1
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
expire-in: 21 days
|
||||||
|
cleanupCloudRunner:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
if: github.event.event_type != 'pull_request_target'
|
||||||
|
with:
|
||||||
|
lfs: true
|
||||||
|
- uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: 16.x
|
||||||
|
- run: yarn
|
||||||
|
- run: yarn run cli --help
|
||||||
|
env:
|
||||||
|
AWS_REGION: eu-west-2
|
||||||
|
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||||
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||||
|
AWS_DEFAULT_REGION: eu-west-2
|
||||||
|
- run: yarn run cli -m list-resources
|
||||||
|
env:
|
||||||
|
AWS_REGION: eu-west-2
|
||||||
|
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||||
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||||
|
AWS_DEFAULT_REGION: eu-west-2
|
||||||
29
.github/workflows/cloud-runner-async-checks.yml
vendored
29
.github/workflows/cloud-runner-async-checks.yml
vendored
@@ -19,12 +19,11 @@ env:
|
|||||||
GCP_LOGGING: true
|
GCP_LOGGING: true
|
||||||
GCP_PROJECT: unitykubernetesbuilder
|
GCP_PROJECT: unitykubernetesbuilder
|
||||||
GCP_LOG_FILE: ${{ github.workspace }}/cloud-runner-logs.txt
|
GCP_LOG_FILE: ${{ github.workspace }}/cloud-runner-logs.txt
|
||||||
# Commented out: Using LocalStack tests instead of real AWS
|
AWS_REGION: eu-west-2
|
||||||
# AWS_REGION: eu-west-2
|
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||||
# AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||||
# AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
AWS_DEFAULT_REGION: eu-west-2
|
||||||
# AWS_DEFAULT_REGION: eu-west-2
|
AWS_BASE_STACK_NAME: game-ci-github-pipelines
|
||||||
# AWS_STACK_NAME: game-ci-github-pipelines
|
|
||||||
CLOUD_RUNNER_BRANCH: ${{ github.ref }}
|
CLOUD_RUNNER_BRANCH: ${{ github.ref }}
|
||||||
CLOUD_RUNNER_DEBUG: true
|
CLOUD_RUNNER_DEBUG: true
|
||||||
CLOUD_RUNNER_DEBUG_TREE: true
|
CLOUD_RUNNER_DEBUG_TREE: true
|
||||||
@@ -40,22 +39,20 @@ jobs:
|
|||||||
if: github.event.event_type != 'pull_request_target'
|
if: github.event.event_type != 'pull_request_target'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- timeout-minutes: 180
|
- name: Checkout (default)
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
lfs: false
|
||||||
|
- run: yarn
|
||||||
|
- run: yarn run cli -m checks-update
|
||||||
|
timeout-minutes: 180
|
||||||
env:
|
env:
|
||||||
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
|
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
|
||||||
PROJECT_PATH: test-project
|
PROJECT_PATH: test-project
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
GIT_PRIVATE_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
TARGET_PLATFORM: StandaloneWindows64
|
TARGET_PLATFORM: StandaloneWindows64
|
||||||
cloudRunnerTests: true
|
cloudRunnerTests: true
|
||||||
versioning: None
|
versioning: None
|
||||||
CLOUD_RUNNER_CLUSTER: local-docker
|
CLOUD_RUNNER_CLUSTER: local-docker
|
||||||
# Commented out: Using LocalStack tests instead of real AWS
|
AWS_BASE_STACK_NAME: game-ci-github-pipelines
|
||||||
# AWS_STACK_NAME: game-ci-github-pipelines
|
|
||||||
CHECKS_UPDATE: ${{ github.event.inputs.checksObject }}
|
CHECKS_UPDATE: ${{ github.event.inputs.checksObject }}
|
||||||
run: |
|
|
||||||
git clone -b cloud-runner-develop https://github.com/game-ci/unity-builder
|
|
||||||
cd unity-builder
|
|
||||||
yarn
|
|
||||||
ls
|
|
||||||
yarn run cli -m checks-update
|
|
||||||
|
|||||||
1109
.github/workflows/cloud-runner-integrity.yml
vendored
1109
.github/workflows/cloud-runner-integrity.yml
vendored
File diff suppressed because it is too large
Load Diff
133
.github/workflows/cloud-runner-pipeline.yml
vendored
Normal file
133
.github/workflows/cloud-runner-pipeline.yml
vendored
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
name: Cloud Runner CI Pipeline
|
||||||
|
|
||||||
|
on:
|
||||||
|
push: { branches: [cloud-runner-develop, cloud-runner-preview, main] }
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
checks: write
|
||||||
|
contents: read
|
||||||
|
actions: write
|
||||||
|
|
||||||
|
env:
|
||||||
|
GKE_ZONE: 'us-central1'
|
||||||
|
GKE_REGION: 'us-central1'
|
||||||
|
GKE_PROJECT: 'unitykubernetesbuilder'
|
||||||
|
GKE_CLUSTER: 'game-ci-github-pipelines'
|
||||||
|
GCP_LOGGING: true
|
||||||
|
GCP_PROJECT: unitykubernetesbuilder
|
||||||
|
GCP_LOG_FILE: ${{ github.workspace }}/cloud-runner-logs.txt
|
||||||
|
AWS_REGION: eu-west-2
|
||||||
|
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||||
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||||
|
AWS_DEFAULT_REGION: eu-west-2
|
||||||
|
AWS_BASE_STACK_NAME: game-ci-team-pipelines
|
||||||
|
CLOUD_RUNNER_BRANCH: ${{ github.ref }}
|
||||||
|
CLOUD_RUNNER_DEBUG: true
|
||||||
|
CLOUD_RUNNER_DEBUG_TREE: true
|
||||||
|
DEBUG: true
|
||||||
|
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
|
||||||
|
PROJECT_PATH: test-project
|
||||||
|
UNITY_VERSION: 2019.3.15f1
|
||||||
|
USE_IL2CPP: false
|
||||||
|
USE_GKE_GCLOUD_AUTH_PLUGIN: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
integrationTests:
|
||||||
|
name: Integration Tests
|
||||||
|
if: github.event.event_type != 'pull_request_target'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
cloudRunnerCluster:
|
||||||
|
- aws
|
||||||
|
- local-docker
|
||||||
|
- k8s
|
||||||
|
steps:
|
||||||
|
- name: Checkout (default)
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
lfs: false
|
||||||
|
- uses: google-github-actions/auth@v1
|
||||||
|
with:
|
||||||
|
credentials_json: ${{ secrets.GOOGLE_SERVICE_ACCOUNT_KEY }}
|
||||||
|
- name: 'Set up Cloud SDK'
|
||||||
|
uses: 'google-github-actions/setup-gcloud@v1'
|
||||||
|
- name: Get GKE cluster credentials
|
||||||
|
run: |
|
||||||
|
export USE_GKE_GCLOUD_AUTH_PLUGIN=True
|
||||||
|
gcloud components install gke-gcloud-auth-plugin
|
||||||
|
gcloud container clusters get-credentials $GKE_CLUSTER --zone $GKE_ZONE --project $GKE_PROJECT
|
||||||
|
- name: Configure AWS Credentials
|
||||||
|
uses: aws-actions/configure-aws-credentials@v1
|
||||||
|
with:
|
||||||
|
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||||
|
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||||
|
aws-region: eu-west-2
|
||||||
|
- run: yarn
|
||||||
|
- run: yarn run test "cloud-runner-async-workflow" --detectOpenHandles --forceExit --runInBand
|
||||||
|
if: matrix.CloudRunnerCluster != 'local-docker'
|
||||||
|
timeout-minutes: 180
|
||||||
|
env:
|
||||||
|
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
|
||||||
|
PROJECT_PATH: test-project
|
||||||
|
GIT_PRIVATE_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
TARGET_PLATFORM: StandaloneWindows64
|
||||||
|
cloudRunnerTests: true
|
||||||
|
versioning: None
|
||||||
|
CLOUD_RUNNER_CLUSTER: ${{ matrix.cloudRunnerCluster }}
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- run: yarn run test-i --detectOpenHandles --forceExit --runInBand
|
||||||
|
if: matrix.CloudRunnerCluster == 'local-docker'
|
||||||
|
timeout-minutes: 180
|
||||||
|
env:
|
||||||
|
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
|
||||||
|
PROJECT_PATH: test-project
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
TARGET_PLATFORM: StandaloneWindows64
|
||||||
|
cloudRunnerTests: true
|
||||||
|
versioning: None
|
||||||
|
CLOUD_RUNNER_CLUSTER: ${{ matrix.cloudRunnerCluster }}
|
||||||
|
localBuildTests:
|
||||||
|
name: Local Build Target Tests
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
cloudRunnerCluster:
|
||||||
|
#- aws
|
||||||
|
- local-docker
|
||||||
|
#- k8s
|
||||||
|
targetPlatform:
|
||||||
|
- StandaloneOSX # Build a macOS standalone (Intel 64-bit).
|
||||||
|
- StandaloneWindows64 # Build a Windows 64-bit standalone.
|
||||||
|
- StandaloneLinux64 # Build a Linux 64-bit standalone.
|
||||||
|
- WebGL # WebGL.
|
||||||
|
- iOS # Build an iOS player.
|
||||||
|
- Android # Build an Android .apk.
|
||||||
|
steps:
|
||||||
|
- name: Checkout (default)
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
lfs: false
|
||||||
|
- run: yarn
|
||||||
|
- uses: ./
|
||||||
|
id: unity-build
|
||||||
|
timeout-minutes: 90
|
||||||
|
env:
|
||||||
|
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
|
||||||
|
with:
|
||||||
|
cloudRunnerTests: true
|
||||||
|
versioning: None
|
||||||
|
projectPath: test-project
|
||||||
|
gitPrivateToken: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
targetPlatform: ${{ matrix.targetPlatform }}
|
||||||
|
cloudRunnerCluster: ${{ matrix.cloudRunnerCluster }}
|
||||||
|
- run: |
|
||||||
|
cp ./cloud-runner-cache/cache/${{ steps.unity-build.outputs.CACHE_KEY }}/build/${{ steps.unity-build.outputs.BUILD_ARTIFACT }} ${{ steps.unity-build.outputs.BUILD_ARTIFACT }}
|
||||||
|
- uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: ${{ matrix.cloudRunnerCluster }} Build (${{ matrix.targetPlatform }})
|
||||||
|
path: ${{ steps.unity-build.outputs.BUILD_ARTIFACT }}
|
||||||
|
retention-days: 14
|
||||||
24
.github/workflows/integrity-check.yml
vendored
24
.github/workflows/integrity-check.yml
vendored
@@ -4,35 +4,21 @@ on:
|
|||||||
push: { branches: [main] }
|
push: { branches: [main] }
|
||||||
pull_request: {}
|
pull_request: {}
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
checks: write
|
|
||||||
statuses: write
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CODECOV_TOKEN: '2f2eb890-30e2-4724-83eb-7633832cf0de'
|
CODECOV_TOKEN: '2f2eb890-30e2-4724-83eb-7633832cf0de'
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
tests:
|
tests:
|
||||||
name: Tests
|
name: Tests
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: '18'
|
node-version: '16'
|
||||||
- run: yarn
|
- run: yarn
|
||||||
- run: yarn lint
|
- run: yarn lint
|
||||||
- run: yarn test:ci --coverage
|
- run: yarn test --coverage
|
||||||
- run: bash <(curl -s https://codecov.io/bash)
|
- run: bash <(curl -s https://codecov.io/bash)
|
||||||
- run: yarn build || { echo "build command should always succeed" ; exit 61; }
|
- run: yarn build || { echo "build command should always succeed" ; exit 61; }
|
||||||
# - run: yarn build --quiet && git diff --quiet dist || { echo "dist should be auto generated" ; git diff dist ; exit 62; }
|
# - run: yarn build --quiet && git diff --quiet dist || { echo "dist should be auto generated" ; git diff dist ; exit 62; }
|
||||||
|
|
||||||
cloud-runner:
|
|
||||||
name: Cloud Runner Integrity
|
|
||||||
uses: ./.github/workflows/cloud-runner-integrity.yml
|
|
||||||
secrets: inherit
|
|
||||||
|
|||||||
74
.github/workflows/mac-build-tests.yml
vendored
Normal file
74
.github/workflows/mac-build-tests.yml
vendored
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
name: Mac Builds
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
env:
|
||||||
|
UNITY_LICENSE: "<?xml version=\"1.0\" encoding=\"UTF-8\"?><root>\n <License id=\"Terms\">\n <MachineBindings>\n <Binding Key=\"1\" Value=\"576562626572264761624c65526f7578\"/>\n <Binding Key=\"2\" Value=\"576562626572264761624c65526f7578\"/>\n </MachineBindings>\n <MachineID Value=\"D7nTUnjNAmtsUMcnoyrqkgIbYdM=\"/>\n <SerialHash Value=\"2033b8ac3e6faa3742ca9f0bfae44d18f2a96b80\"/>\n <Features>\n <Feature Value=\"33\"/>\n <Feature Value=\"1\"/>\n <Feature Value=\"12\"/>\n <Feature Value=\"2\"/>\n <Feature Value=\"24\"/>\n <Feature Value=\"3\"/>\n <Feature Value=\"36\"/>\n <Feature Value=\"17\"/>\n <Feature Value=\"19\"/>\n <Feature Value=\"62\"/>\n </Features>\n <DeveloperData Value=\"AQAAAEY0LUJHUlgtWEQ0RS1aQ1dWLUM1SlctR0RIQg==\"/>\n <SerialMasked Value=\"F4-BGRX-XD4E-ZCWV-C5JW-XXXX\"/>\n <StartDate Value=\"2021-02-08T00:00:00\"/>\n <UpdateDate Value=\"2021-02-09T00:34:57\"/>\n <InitialActivationDate Value=\"2021-02-08T00:34:56\"/>\n <LicenseVersion Value=\"6.x\"/>\n <ClientProvidedVersion Value=\"2018.4.30f1\"/>\n <AlwaysOnline Value=\"false\"/>\n <Entitlements>\n <Entitlement Ns=\"unity_editor\" Tag=\"UnityPersonal\" Type=\"EDITOR\" ValidTo=\"9999-12-31T00:00:00\"/>\n <Entitlement Ns=\"unity_editor\" Tag=\"DarkSkin\" Type=\"EDITOR_FEATURE\" ValidTo=\"9999-12-31T00:00:00\"/>\n </Entitlements>\n </License>\n<Signature xmlns=\"http://www.w3.org/2000/09/xmldsig#\"><SignedInfo><CanonicalizationMethod Algorithm=\"http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments\"/><SignatureMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#rsa-sha1\"/><Reference URI=\"#Terms\"><Transforms><Transform Algorithm=\"http://www.w3.org/2000/09/xmldsig#enveloped-signature\"/></Transforms><DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\"/><DigestValue>m0Db8UK+ktnOLJBtHybkfetpcKo=</DigestValue></Reference></SignedInfo><SignatureValue>o/pUbSQAukz7+ZYAWhnA0AJbIlyyCPL7bKVEM2lVqbrXt7cyey+umkCXamuOgsWPVUKBMkXtMH8L\n5etLmD0getWIhTGhzOnDCk+gtIPfL4jMo9tkEuOCROQAXCci23VFscKcrkB+3X6h4wEOtA2APhOY\nB+wvC794o8/82ffjP79aVAi57rp3Wmzx+9pe9yMwoJuljAy2sc2tIMgdQGWVmOGBpQm3JqsidyzI\nJWG2kjnc7pDXK9pwYzXoKiqUqqrut90d+kQqRyv7MSZXR50HFqD/LI69h68b7P8Bjo3bPXOhNXGR\n9YCoemH6EkfCJxp2gIjzjWW+l2Hj2EsFQi8YXw==</SignatureValue></Signature></root>"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
buildForAllPlatformsWindows:
|
||||||
|
name: Build for ${{ matrix.targetPlatform }} on version ${{ matrix.unityVersion }}
|
||||||
|
runs-on: macos-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
projectPath:
|
||||||
|
- test-project
|
||||||
|
unityVersion:
|
||||||
|
- 2020.3.24f1
|
||||||
|
targetPlatform:
|
||||||
|
- StandaloneOSX # Build a MacOS executable
|
||||||
|
|
||||||
|
steps:
|
||||||
|
###########################
|
||||||
|
# Checkout #
|
||||||
|
###########################
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
lfs: true
|
||||||
|
|
||||||
|
###########################
|
||||||
|
# Cache #
|
||||||
|
###########################
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: ${{ matrix.projectPath }}/Library
|
||||||
|
key: Library-${{ matrix.projectPath }}-macos-${{ matrix.targetPlatform }}
|
||||||
|
restore-keys: |
|
||||||
|
Library-${{ matrix.projectPath }}-macos-
|
||||||
|
Library-
|
||||||
|
|
||||||
|
###########################
|
||||||
|
# Set Scripting Backend #
|
||||||
|
###########################
|
||||||
|
- name: Set Scripting Backend To il2cpp
|
||||||
|
run: |
|
||||||
|
mv -f "./test-project/ProjectSettings/ProjectSettingsIl2cpp.asset" "./test-project/ProjectSettings/ProjectSettings.asset"
|
||||||
|
|
||||||
|
###########################
|
||||||
|
# Build #
|
||||||
|
###########################
|
||||||
|
- uses: ./
|
||||||
|
env:
|
||||||
|
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
|
||||||
|
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
|
||||||
|
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
|
||||||
|
with:
|
||||||
|
projectPath: ${{ matrix.projectPath }}
|
||||||
|
unityVersion: ${{ matrix.unityVersion }}
|
||||||
|
targetPlatform: ${{ matrix.targetPlatform }}
|
||||||
|
customParameters: -profile SomeProfile -someBoolean -someValue exampleValue
|
||||||
|
# We use dirty build because we are replacing the default project settings file above
|
||||||
|
allowDirtyBuild: true
|
||||||
|
|
||||||
|
###########################
|
||||||
|
# Upload #
|
||||||
|
###########################
|
||||||
|
- uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: Build MacOS (${{ matrix.unityVersion }})
|
||||||
|
path: build
|
||||||
|
retention-days: 14
|
||||||
77
.github/workflows/windows-build-tests.yml
vendored
Normal file
77
.github/workflows/windows-build-tests.yml
vendored
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
name: Windows Builds
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
env:
|
||||||
|
UNITY_LICENSE: "<?xml version=\"1.0\" encoding=\"UTF-8\"?><root>\n <License id=\"Terms\">\n <MachineBindings>\n <Binding Key=\"1\" Value=\"576562626572264761624c65526f7578\"/>\n <Binding Key=\"2\" Value=\"576562626572264761624c65526f7578\"/>\n </MachineBindings>\n <MachineID Value=\"D7nTUnjNAmtsUMcnoyrqkgIbYdM=\"/>\n <SerialHash Value=\"2033b8ac3e6faa3742ca9f0bfae44d18f2a96b80\"/>\n <Features>\n <Feature Value=\"33\"/>\n <Feature Value=\"1\"/>\n <Feature Value=\"12\"/>\n <Feature Value=\"2\"/>\n <Feature Value=\"24\"/>\n <Feature Value=\"3\"/>\n <Feature Value=\"36\"/>\n <Feature Value=\"17\"/>\n <Feature Value=\"19\"/>\n <Feature Value=\"62\"/>\n </Features>\n <DeveloperData Value=\"AQAAAEY0LUJHUlgtWEQ0RS1aQ1dWLUM1SlctR0RIQg==\"/>\n <SerialMasked Value=\"F4-BGRX-XD4E-ZCWV-C5JW-XXXX\"/>\n <StartDate Value=\"2021-02-08T00:00:00\"/>\n <UpdateDate Value=\"2021-02-09T00:34:57\"/>\n <InitialActivationDate Value=\"2021-02-08T00:34:56\"/>\n <LicenseVersion Value=\"6.x\"/>\n <ClientProvidedVersion Value=\"2018.4.30f1\"/>\n <AlwaysOnline Value=\"false\"/>\n <Entitlements>\n <Entitlement Ns=\"unity_editor\" Tag=\"UnityPersonal\" Type=\"EDITOR\" ValidTo=\"9999-12-31T00:00:00\"/>\n <Entitlement Ns=\"unity_editor\" Tag=\"DarkSkin\" Type=\"EDITOR_FEATURE\" ValidTo=\"9999-12-31T00:00:00\"/>\n </Entitlements>\n </License>\n<Signature xmlns=\"http://www.w3.org/2000/09/xmldsig#\"><SignedInfo><CanonicalizationMethod Algorithm=\"http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments\"/><SignatureMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#rsa-sha1\"/><Reference URI=\"#Terms\"><Transforms><Transform Algorithm=\"http://www.w3.org/2000/09/xmldsig#enveloped-signature\"/></Transforms><DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\"/><DigestValue>m0Db8UK+ktnOLJBtHybkfetpcKo=</DigestValue></Reference></SignedInfo><SignatureValue>o/pUbSQAukz7+ZYAWhnA0AJbIlyyCPL7bKVEM2lVqbrXt7cyey+umkCXamuOgsWPVUKBMkXtMH8L\n5etLmD0getWIhTGhzOnDCk+gtIPfL4jMo9tkEuOCROQAXCci23VFscKcrkB+3X6h4wEOtA2APhOY\nB+wvC794o8/82ffjP79aVAi57rp3Wmzx+9pe9yMwoJuljAy2sc2tIMgdQGWVmOGBpQm3JqsidyzI\nJWG2kjnc7pDXK9pwYzXoKiqUqqrut90d+kQqRyv7MSZXR50HFqD/LI69h68b7P8Bjo3bPXOhNXGR\n9YCoemH6EkfCJxp2gIjzjWW+l2Hj2EsFQi8YXw==</SignatureValue></Signature></root>"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
buildForAllPlatformsWindows:
|
||||||
|
name: Build for ${{ matrix.targetPlatform }} on version ${{ matrix.unityVersion }}
|
||||||
|
runs-on: windows-2019
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
projectPath:
|
||||||
|
- test-project
|
||||||
|
unityVersion:
|
||||||
|
- 2020.3.24f1
|
||||||
|
targetPlatform:
|
||||||
|
- StandaloneWindows64 # Build a Windows 64-bit standalone.
|
||||||
|
- StandaloneWindows # Build a Windows 32-bit standalone.
|
||||||
|
- WSAPlayer # Build a UWP App
|
||||||
|
- tvOS # Build an Apple TV XCode project
|
||||||
|
|
||||||
|
steps:
|
||||||
|
###########################
|
||||||
|
# Checkout #
|
||||||
|
###########################
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
lfs: true
|
||||||
|
|
||||||
|
###########################
|
||||||
|
# Cache #
|
||||||
|
###########################
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: ${{ matrix.projectPath }}/Library
|
||||||
|
key: Library-${{ matrix.projectPath }}-windows-${{ matrix.targetPlatform }}
|
||||||
|
restore-keys: |
|
||||||
|
Library-${{ matrix.projectPath }}-windows-
|
||||||
|
Library-
|
||||||
|
|
||||||
|
###########################
|
||||||
|
# Set Scripting Backend #
|
||||||
|
###########################
|
||||||
|
- name: Set Scripting Backend To il2cpp
|
||||||
|
run: |
|
||||||
|
Move-Item -Path "./test-project/ProjectSettings/ProjectSettingsIl2cpp.asset" -Destination "./test-project/ProjectSettings/ProjectSettings.asset" -Force
|
||||||
|
|
||||||
|
###########################
|
||||||
|
# Build #
|
||||||
|
###########################
|
||||||
|
- uses: ./
|
||||||
|
env:
|
||||||
|
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
|
||||||
|
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
|
||||||
|
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
|
||||||
|
with:
|
||||||
|
projectPath: ${{ matrix.projectPath }}
|
||||||
|
unityVersion: ${{ matrix.unityVersion }}
|
||||||
|
targetPlatform: ${{ matrix.targetPlatform }}
|
||||||
|
customParameters: -profile SomeProfile -someBoolean -someValue exampleValue
|
||||||
|
allowDirtyBuild: true
|
||||||
|
# We use dirty build because we are replacing the default project settings file above
|
||||||
|
|
||||||
|
###########################
|
||||||
|
# Upload #
|
||||||
|
###########################
|
||||||
|
- uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: Build Windows (${{ matrix.unityVersion }})
|
||||||
|
path: build
|
||||||
|
retention-days: 14
|
||||||
32
.vscode/launch.json
vendored
32
.vscode/launch.json
vendored
@@ -1,32 +0,0 @@
|
|||||||
{
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"name": "PowerShell Launch Current File",
|
|
||||||
"type": "PowerShell",
|
|
||||||
"request": "launch",
|
|
||||||
"script": "${file}",
|
|
||||||
"cwd": "${cwd}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "node",
|
|
||||||
"request": "launch",
|
|
||||||
"name": "Debug Jest Test",
|
|
||||||
"program": "${workspaceRoot}/node_modules/jest/bin/jest.js",
|
|
||||||
"args": [
|
|
||||||
"--collectCoverage=false",
|
|
||||||
"--colors",
|
|
||||||
"--config",
|
|
||||||
"${workspaceRoot}/jest.config.js",
|
|
||||||
"--runInBand",
|
|
||||||
"--runTestsByPath",
|
|
||||||
"${relativeFile}",
|
|
||||||
"--testPathPattern=${fileDirname}",
|
|
||||||
"--testTimeout=10000000"
|
|
||||||
],
|
|
||||||
"outputCapture": "std",
|
|
||||||
"internalConsoleOptions": "openOnSessionStart",
|
|
||||||
"envFile": "${workspaceRoot}/.env",
|
|
||||||
"skipFiles": ["${workspaceRoot}/../../node_modules/**/*", "<node_internals>/**/*"]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -25,7 +25,7 @@ Steps to be performed to submit a pull request:
|
|||||||
|
|
||||||
#### Pull Request Prerequisites
|
#### Pull Request Prerequisites
|
||||||
|
|
||||||
You have [Node](https://nodejs.org/) installed at v18+ and [Yarn](https://yarnpkg.com/) at v1.22.0+.
|
You have [Node](https://nodejs.org/) installed at v12.2.0+ and [Yarn](https://yarnpkg.com/) at v1.18.0+.
|
||||||
|
|
||||||
Please note that commit hooks will run automatically to perform some tasks;
|
Please note that commit hooks will run automatically to perform some tasks;
|
||||||
|
|
||||||
@@ -36,8 +36,7 @@ Please note that commit hooks will run automatically to perform some tasks;
|
|||||||
#### Windows users
|
#### Windows users
|
||||||
|
|
||||||
Make sure your editor and terminal that run the tests are set to `Powershell 7` or above with
|
Make sure your editor and terminal that run the tests are set to `Powershell 7` or above with
|
||||||
`Git's Unix tools for Windows` installed. This is because some tests require you to be able to run `sh` and other
|
`Git's Unix tools for Windows` installed. Some tests require you to be able to run `sh` and other unix commands.
|
||||||
unix commands.
|
|
||||||
|
|
||||||
#### License
|
#### License
|
||||||
|
|
||||||
|
|||||||
@@ -10,9 +10,8 @@ Part of the <a href="https://game.ci">GameCI</a> open source project.
|
|||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
[](https://github.com/game-ci/unity-builder/actions/workflows/build-tests-ubuntu.yml)
|
[](https://github.com/game-ci/unity-builder/actions?query=branch%3Amain+event%3Apush+workflow%3A%22Builds)
|
||||||
[](https://github.com/game-ci/unity-builder/actions/workflows/build-tests-windows.yml)
|
[](https://lgtm.com/projects/g/webbertakken/unity-builder/context:javascript)
|
||||||
[](https://github.com/game-ci/unity-builder/actions/workflows/build-tests-mac.yml)
|
|
||||||
[](https://codecov.io/gh/game-ci/unity-builder)
|
[](https://codecov.io/gh/game-ci/unity-builder)
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
|
|||||||
122
action.yml
122
action.yml
@@ -18,15 +18,11 @@ inputs:
|
|||||||
projectPath:
|
projectPath:
|
||||||
required: false
|
required: false
|
||||||
default: ''
|
default: ''
|
||||||
description: 'Path to the project to be built, relative to the repository root.'
|
description: 'Relative path to the project to be built.'
|
||||||
buildProfile:
|
|
||||||
required: false
|
|
||||||
default: ''
|
|
||||||
description: 'Path to the build profile to activate, relative to the project root.'
|
|
||||||
buildName:
|
buildName:
|
||||||
required: false
|
required: false
|
||||||
default: ''
|
default: ''
|
||||||
description: 'Name of the build. Should not include a file extension.'
|
description: 'Name of the build.'
|
||||||
buildsPath:
|
buildsPath:
|
||||||
required: false
|
required: false
|
||||||
default: ''
|
default: ''
|
||||||
@@ -35,14 +31,6 @@ inputs:
|
|||||||
required: false
|
required: false
|
||||||
default: ''
|
default: ''
|
||||||
description: 'Path to a Namespace.Class.StaticMethod to run to perform the build.'
|
description: 'Path to a Namespace.Class.StaticMethod to run to perform the build.'
|
||||||
manualExit:
|
|
||||||
required: false
|
|
||||||
default: ''
|
|
||||||
description: 'Suppresses `-quit`. Exit your build method using `EditorApplication.Exit(0)` instead.'
|
|
||||||
enableGpu:
|
|
||||||
required: false
|
|
||||||
default: ''
|
|
||||||
description: 'Launches unity without specifying `-nographics`.'
|
|
||||||
customParameters:
|
customParameters:
|
||||||
required: false
|
required: false
|
||||||
default: ''
|
default: ''
|
||||||
@@ -59,12 +47,10 @@ inputs:
|
|||||||
required: false
|
required: false
|
||||||
default: ''
|
default: ''
|
||||||
description: 'The android versionCode'
|
description: 'The android versionCode'
|
||||||
androidExportType:
|
androidAppBundle:
|
||||||
required: false
|
required: false
|
||||||
default: 'androidPackage'
|
default: 'false'
|
||||||
description:
|
description: 'Whether to build .aab instead of .apk'
|
||||||
'The android export type. Should be androidPackage for apk, androidAppBundle for aab, or androidStudioProject for
|
|
||||||
an android studio project.'
|
|
||||||
androidKeystoreName:
|
androidKeystoreName:
|
||||||
required: false
|
required: false
|
||||||
default: ''
|
default: ''
|
||||||
@@ -89,18 +75,10 @@ inputs:
|
|||||||
required: false
|
required: false
|
||||||
default: ''
|
default: ''
|
||||||
description: 'The android target API level.'
|
description: 'The android target API level.'
|
||||||
androidSymbolType:
|
|
||||||
required: false
|
|
||||||
default: 'none'
|
|
||||||
description: 'The android symbol type to export. Should be "none", "public" or "debugging".'
|
|
||||||
sshAgent:
|
sshAgent:
|
||||||
required: false
|
required: false
|
||||||
default: ''
|
default: ''
|
||||||
description: 'SSH Agent path to forward to the container'
|
description: 'SSH Agent path to forward to the container'
|
||||||
sshPublicKeysDirectoryPath:
|
|
||||||
required: false
|
|
||||||
default: ''
|
|
||||||
description: 'Path to a directory containing SSH public keys to forward to the container.'
|
|
||||||
gitPrivateToken:
|
gitPrivateToken:
|
||||||
required: false
|
required: false
|
||||||
default: ''
|
default: ''
|
||||||
@@ -109,43 +87,11 @@ inputs:
|
|||||||
required: false
|
required: false
|
||||||
default: ''
|
default: ''
|
||||||
description: '[CloudRunner] GitHub owner name or organization/team name'
|
description: '[CloudRunner] GitHub owner name or organization/team name'
|
||||||
runAsHostUser:
|
|
||||||
required: false
|
|
||||||
default: 'false'
|
|
||||||
description:
|
|
||||||
'Whether to run as a user that matches the host system or the default root container user. Only applicable to
|
|
||||||
Linux hosts and containers. This is useful for fixing permission errors on Self-Hosted runners.'
|
|
||||||
chownFilesTo:
|
chownFilesTo:
|
||||||
required: false
|
required: false
|
||||||
default: ''
|
default: ''
|
||||||
description:
|
description:
|
||||||
'User and optionally group (user or user:group or uid:gid) to give ownership of the resulting build artifacts'
|
'User and optionally group (user or user:group or uid:gid) to give ownership of the resulting build artifacts'
|
||||||
dockerCpuLimit:
|
|
||||||
required: false
|
|
||||||
default: ''
|
|
||||||
description: 'Number of CPU cores to assign the docker container. Defaults to all available cores on all platforms.'
|
|
||||||
dockerMemoryLimit:
|
|
||||||
required: false
|
|
||||||
default: ''
|
|
||||||
description:
|
|
||||||
'Amount of memory to assign the docker container. Defaults to 95% of total system memory rounded down to the
|
|
||||||
nearest megabyte on Linux and 80% on Windows. On unrecognized platforms, defaults to 75% of total system memory.
|
|
||||||
To manually specify a value, use the format <number><unit>, where unit is either m or g. ie: 512m = 512 megabytes'
|
|
||||||
dockerIsolationMode:
|
|
||||||
required: false
|
|
||||||
default: 'default'
|
|
||||||
description:
|
|
||||||
'Isolation mode to use for the docker container. Can be one of process, hyperv, or default. Default will pick the
|
|
||||||
default mode as described by Microsoft where server versions use process and desktop versions use hyperv. Only
|
|
||||||
applicable on Windows'
|
|
||||||
containerRegistryRepository:
|
|
||||||
required: false
|
|
||||||
default: 'unityci/editor'
|
|
||||||
description: 'Container registry and repository to pull image from. Only applicable if customImage is not set.'
|
|
||||||
containerRegistryImageVersion:
|
|
||||||
required: false
|
|
||||||
default: '3'
|
|
||||||
description: 'Container registry image version. Only applicable if customImage is not set.'
|
|
||||||
allowDirtyBuild:
|
allowDirtyBuild:
|
||||||
required: false
|
required: false
|
||||||
default: ''
|
default: ''
|
||||||
@@ -162,7 +108,7 @@ inputs:
|
|||||||
description:
|
description:
|
||||||
'[CloudRunner] Run a pre build job after the repository setup but before the build job (in yaml format with the
|
'[CloudRunner] Run a pre build job after the repository setup but before the build job (in yaml format with the
|
||||||
keys image, secrets (name, value object array), command line string)'
|
keys image, secrets (name, value object array), command line string)'
|
||||||
containerHookFiles:
|
customStepFiles:
|
||||||
required: false
|
required: false
|
||||||
default: ''
|
default: ''
|
||||||
description:
|
description:
|
||||||
@@ -174,7 +120,7 @@ inputs:
|
|||||||
description:
|
description:
|
||||||
'[CloudRunner] Specify the names (by file name) of custom hooks to run before or after cloud runner jobs, must
|
'[CloudRunner] Specify the names (by file name) of custom hooks to run before or after cloud runner jobs, must
|
||||||
match a yaml step file inside your repo in the folder .game-ci/hooks/'
|
match a yaml step file inside your repo in the folder .game-ci/hooks/'
|
||||||
customCommandHooks:
|
customJobHooks:
|
||||||
required: false
|
required: false
|
||||||
default: ''
|
default: ''
|
||||||
description: '[CloudRunner] Specify custom commands and trigger hooks (injects commands into jobs)'
|
description: '[CloudRunner] Specify custom commands and trigger hooks (injects commands into jobs)'
|
||||||
@@ -184,25 +130,21 @@ inputs:
|
|||||||
description:
|
description:
|
||||||
'[CloudRunner] Run a custom job instead of the standard build automation for cloud runner (in yaml format with the
|
'[CloudRunner] Run a custom job instead of the standard build automation for cloud runner (in yaml format with the
|
||||||
keys image, secrets (name, value object array), command line string)'
|
keys image, secrets (name, value object array), command line string)'
|
||||||
awsStackName:
|
awsBaseStackName:
|
||||||
default: 'game-ci'
|
default: 'game-ci'
|
||||||
required: false
|
required: false
|
||||||
description: '[CloudRunner] The Cloud Formation stack name that must be setup before using this option.'
|
description: '[CloudRunner] The Cloud Formation stack name that must be setup before using this option.'
|
||||||
providerStrategy:
|
cloudRunnerCluster:
|
||||||
default: 'local'
|
default: 'local'
|
||||||
required: false
|
required: false
|
||||||
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.'
|
||||||
resourceTracking:
|
cloudRunnerCpu:
|
||||||
default: 'false'
|
|
||||||
required: false
|
|
||||||
description: '[CloudRunner] Enable resource tracking logs for disk usage and allocation summaries.'
|
|
||||||
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'
|
||||||
containerMemory:
|
cloudRunnerMemory:
|
||||||
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'
|
||||||
@@ -245,41 +187,6 @@ inputs:
|
|||||||
description:
|
description:
|
||||||
'[CloudRunner] Whether or not to watch the build to the end. Can be used for especially long running jobs e.g
|
'[CloudRunner] Whether or not to watch the build to the end. Can be used for especially long running jobs e.g
|
||||||
imports or self-hosted ephemeral runners.'
|
imports or self-hosted ephemeral runners.'
|
||||||
cacheUnityInstallationOnMac:
|
|
||||||
default: 'false'
|
|
||||||
required: false
|
|
||||||
description: 'Whether to cache the Unity hub and editor installation on MacOS'
|
|
||||||
unityHubVersionOnMac:
|
|
||||||
default: ''
|
|
||||||
required: false
|
|
||||||
description:
|
|
||||||
'The version of Unity Hub to install on MacOS (e.g. 3.4.0). Defaults to latest available on brew if empty string
|
|
||||||
or nothing is specified.'
|
|
||||||
unityLicensingServer:
|
|
||||||
default: ''
|
|
||||||
required: false
|
|
||||||
description: 'The Unity licensing server address to use for activating Unity.'
|
|
||||||
dockerWorkspacePath:
|
|
||||||
default: '/github/workspace'
|
|
||||||
required: false
|
|
||||||
description:
|
|
||||||
'The path to mount the workspace inside the docker container. For windows, leave out the drive letter. For example
|
|
||||||
c:/github/workspace should be defined as /github/workspace'
|
|
||||||
skipActivation:
|
|
||||||
default: 'false'
|
|
||||||
required: false
|
|
||||||
description: 'Skip the activation/deactivation of Unity. This assumes Unity is already activated.'
|
|
||||||
cloneDepth:
|
|
||||||
default: '50'
|
|
||||||
required: false
|
|
||||||
description: '[CloudRunner] Specifies the depth of the git clone for the repository. Use 0 for full clone.'
|
|
||||||
cloudRunnerRepoName:
|
|
||||||
default: 'game-ci/unity-builder'
|
|
||||||
required: false
|
|
||||||
description:
|
|
||||||
'[CloudRunner] Specifies the repo for the unity builder. Useful if you forked the repo for testing, features, or
|
|
||||||
fixes.'
|
|
||||||
|
|
||||||
outputs:
|
outputs:
|
||||||
volume:
|
volume:
|
||||||
description: 'The Persistent Volume (PV) where the build artifacts have been stored by Kubernetes'
|
description: 'The Persistent Volume (PV) where the build artifacts have been stored by Kubernetes'
|
||||||
@@ -287,14 +194,9 @@ outputs:
|
|||||||
description: 'The generated version used for the Unity build'
|
description: 'The generated version used for the Unity build'
|
||||||
androidVersionCode:
|
androidVersionCode:
|
||||||
description: 'The generated versionCode used for the Android Unity build'
|
description: 'The generated versionCode used for the Android Unity build'
|
||||||
engineExitCode:
|
|
||||||
description:
|
|
||||||
'Returns the exit code from the build scripts. This code is 0 if the build was successful. If there was an error
|
|
||||||
during activation, the code is from the activation step. If activation is successful, the code is from the project
|
|
||||||
build step.'
|
|
||||||
branding:
|
branding:
|
||||||
icon: 'box'
|
icon: 'box'
|
||||||
color: 'gray-dark'
|
color: 'gray-dark'
|
||||||
runs:
|
runs:
|
||||||
using: 'node20'
|
using: 'node16'
|
||||||
main: 'dist/index.js'
|
main: 'dist/index.js'
|
||||||
|
|||||||
Binary file not shown.
10
dist/BlankProject/ProjectSettings/XRSettings.asset
vendored
Normal file
10
dist/BlankProject/ProjectSettings/XRSettings.asset
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"m_SettingKeys": [
|
||||||
|
"VR Device Disabled",
|
||||||
|
"VR Device User Alert"
|
||||||
|
],
|
||||||
|
"m_SettingValues": [
|
||||||
|
"False",
|
||||||
|
"False"
|
||||||
|
]
|
||||||
|
}
|
||||||
709
dist/default-build-script/Assembly-CSharp-Editor.csproj
vendored
Normal file
709
dist/default-build-script/Assembly-CSharp-Editor.csproj
vendored
Normal file
@@ -0,0 +1,709 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
<_TargetFrameworkDirectories>non_empty_path_generated_by_unity.rider.package</_TargetFrameworkDirectories>
|
||||||
|
<_FullFrameworkReferenceAssemblyPaths>non_empty_path_generated_by_unity.rider.package</_FullFrameworkReferenceAssemblyPaths>
|
||||||
|
<DisableHandlePackageFileConflicts>true</DisableHandlePackageFileConflicts>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
<ProductVersion>10.0.20506</ProductVersion>
|
||||||
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
|
<RootNamespace></RootNamespace>
|
||||||
|
<ProjectGuid>{B7F8614B-1EC2-9D3A-DA1C-4D279A867D74}</ProjectGuid>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
|
<AssemblyName>Assembly-CSharp-Editor</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v4.7.1</TargetFrameworkVersion>
|
||||||
|
<FileAlignment>512</FileAlignment>
|
||||||
|
<BaseDirectory>.</BaseDirectory>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>Temp\bin\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE;UNITY_2019_2_11;UNITY_2019_2;UNITY_2019;UNITY_5_3_OR_NEWER;UNITY_5_4_OR_NEWER;UNITY_5_5_OR_NEWER;UNITY_5_6_OR_NEWER;UNITY_2017_1_OR_NEWER;UNITY_2017_2_OR_NEWER;UNITY_2017_3_OR_NEWER;UNITY_2017_4_OR_NEWER;UNITY_2018_1_OR_NEWER;UNITY_2018_2_OR_NEWER;UNITY_2018_3_OR_NEWER;UNITY_2018_4_OR_NEWER;UNITY_2019_1_OR_NEWER;UNITY_2019_2_OR_NEWER;PLATFORM_ARCH_64;UNITY_64;UNITY_INCLUDE_TESTS;ENABLE_AUDIO;ENABLE_CACHING;ENABLE_CLOTH;ENABLE_MICROPHONE;ENABLE_MULTIPLE_DISPLAYS;ENABLE_PHYSICS;ENABLE_TEXTURE_STREAMING;ENABLE_UNET;ENABLE_LZMA;ENABLE_UNITYEVENTS;ENABLE_WEBCAM;ENABLE_WWW;ENABLE_CLOUD_SERVICES_COLLAB;ENABLE_CLOUD_SERVICES_COLLAB_SOFTLOCKS;ENABLE_CLOUD_SERVICES_ADS;ENABLE_CLOUD_SERVICES_USE_WEBREQUEST;ENABLE_CLOUD_SERVICES_UNET;ENABLE_CLOUD_SERVICES_BUILD;ENABLE_CLOUD_LICENSE;ENABLE_EDITOR_HUB_LICENSE;ENABLE_WEBSOCKET_CLIENT;ENABLE_DIRECTOR_AUDIO;ENABLE_DIRECTOR_TEXTURE;ENABLE_MANAGED_JOBS;ENABLE_MANAGED_TRANSFORM_JOBS;ENABLE_MANAGED_ANIMATION_JOBS;ENABLE_MANAGED_AUDIO_JOBS;INCLUDE_DYNAMIC_GI;ENABLE_MONO_BDWGC;ENABLE_SCRIPTING_GC_WBARRIERS;PLATFORM_SUPPORTS_MONO;RENDER_SOFTWARE_CURSOR;ENABLE_VIDEO;PLATFORM_STANDALONE_WIN;PLATFORM_STANDALONE;UNITY_STANDALONE_WIN;UNITY_STANDALONE;ENABLE_RUNTIME_GI;ENABLE_MOVIES;ENABLE_NETWORK;ENABLE_CRUNCH_TEXTURE_COMPRESSION;ENABLE_UNITYWEBREQUEST;ENABLE_CLOUD_SERVICES;ENABLE_CLOUD_SERVICES_ANALYTICS;ENABLE_CLOUD_SERVICES_PURCHASING;ENABLE_CLOUD_SERVICES_CRASH_REPORTING;ENABLE_OUT_OF_PROCESS_CRASH_HANDLER;ENABLE_EVENT_QUEUE;ENABLE_CLUSTER_SYNC;ENABLE_CLUSTERINPUT;ENABLE_VR;ENABLE_AR;ENABLE_WEBSOCKET_HOST;ENABLE_MONO;NET_STANDARD_2_0;ENABLE_PROFILER;UNITY_ASSERTIONS;UNITY_EDITOR;UNITY_EDITOR_64;UNITY_EDITOR_WIN;ENABLE_UNITY_COLLECTIONS_CHECKS;ENABLE_BURST_AOT;UNITY_TEAM_LICENSE;ENABLE_CUSTOM_RENDER_TEXTURE;ENABLE_DIRECTOR;ENABLE_LOCALIZATION;ENABLE_SPRITES;ENABLE_TERRAIN;ENABLE_TILEMAP;ENABLE_TIMELINE;ENABLE_LEGACY_INPUT_MANAGER;NET_4_6;CSHARP_7_OR_LATER;CSHARP_7_3_OR_NEWER</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<NoWarn>0169</NoWarn>
|
||||||
|
<AllowUnsafeBlocks>False</AllowUnsafeBlocks>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<OutputPath>Temp\bin\Release\</OutputPath>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<NoWarn>0169</NoWarn>
|
||||||
|
<AllowUnsafeBlocks>False</AllowUnsafeBlocks>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<NoConfig>true</NoConfig>
|
||||||
|
<NoStdLib>true</NoStdLib>
|
||||||
|
<AddAdditionalExplicitAssemblyReferences>false</AddAdditionalExplicitAssemblyReferences>
|
||||||
|
<ImplicitlyExpandNETStandardFacades>false</ImplicitlyExpandNETStandardFacades>
|
||||||
|
<ImplicitlyExpandDesignTimeFacades>false</ImplicitlyExpandDesignTimeFacades>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="UnityEngine">
|
||||||
|
<HintPath>C:\Program Files\Unity\2019.2.11f1\Editor\Data\Managed/UnityEngine/UnityEngine.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEditor">
|
||||||
|
<HintPath>C:\Program Files\Unity\2019.2.11f1\Editor\Data\Managed/UnityEditor.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="Assets\Editor\Builder.cs" />
|
||||||
|
<Compile Include="Assets\Editor\Input\ArgumentsParser.cs" />
|
||||||
|
<Compile Include="Assets\Editor\Reporting\StdOutReporter.cs" />
|
||||||
|
<Compile Include="Assets\Editor\System\ProcessExtensions.cs" />
|
||||||
|
<Compile Include="Assets\Editor\Versioning\VersionApplicator.cs" />
|
||||||
|
<Compile Include="Assets\Editor\Versioning\Git.cs" />
|
||||||
|
<Compile Include="Assets\Editor\Versioning\VersionGenerator.cs" />
|
||||||
|
<Compile Include="Assets\Editor\Versioning\GitException.cs" />
|
||||||
|
<Reference Include="UnityEditor.TestRunner">
|
||||||
|
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/ScriptAssemblies/UnityEditor.TestRunner.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.TestRunner">
|
||||||
|
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/ScriptAssemblies/UnityEngine.TestRunner.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.Timeline.Editor">
|
||||||
|
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/ScriptAssemblies/Unity.Timeline.Editor.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="com.unity.multiplayer-hlapi.Editor">
|
||||||
|
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/ScriptAssemblies/com.unity.multiplayer-hlapi.Editor.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.VSCode.Editor">
|
||||||
|
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/ScriptAssemblies/Unity.VSCode.Editor.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.TextMeshPro.Editor">
|
||||||
|
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/ScriptAssemblies/Unity.TextMeshPro.Editor.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.UI">
|
||||||
|
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/ScriptAssemblies/UnityEngine.UI.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.Timeline">
|
||||||
|
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/ScriptAssemblies/Unity.Timeline.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.CollabProxy.Editor">
|
||||||
|
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/ScriptAssemblies/Unity.CollabProxy.Editor.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="com.unity.multiplayer-weaver.Editor">
|
||||||
|
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/ScriptAssemblies/com.unity.multiplayer-weaver.Editor.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.XR.LegacyInputHelpers">
|
||||||
|
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/ScriptAssemblies/UnityEngine.XR.LegacyInputHelpers.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.Rider.Editor">
|
||||||
|
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/ScriptAssemblies/Unity.Rider.Editor.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.2D.Sprite.Editor">
|
||||||
|
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/ScriptAssemblies/Unity.2D.Sprite.Editor.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.2D.Tilemap.Editor">
|
||||||
|
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/ScriptAssemblies/Unity.2D.Tilemap.Editor.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEditor.SpatialTracking">
|
||||||
|
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/ScriptAssemblies/UnityEditor.SpatialTracking.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.SpatialTracking">
|
||||||
|
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/ScriptAssemblies/UnityEngine.SpatialTracking.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.TextMeshPro">
|
||||||
|
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/ScriptAssemblies/Unity.TextMeshPro.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.Analytics.DataPrivacy">
|
||||||
|
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/ScriptAssemblies/Unity.Analytics.DataPrivacy.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEditor.XR.LegacyInputHelpers">
|
||||||
|
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/ScriptAssemblies/UnityEditor.XR.LegacyInputHelpers.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEditor.UI">
|
||||||
|
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/ScriptAssemblies/UnityEditor.UI.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="com.unity.multiplayer-hlapi.Runtime">
|
||||||
|
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/ScriptAssemblies/com.unity.multiplayer-hlapi.Runtime.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.AIModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.AIModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.ARModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.ARModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.AccessibilityModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.AccessibilityModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.AndroidJNIModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.AndroidJNIModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.AnimationModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.AnimationModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.AssetBundleModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.AssetBundleModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.AudioModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.AudioModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.ClothModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.ClothModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.ClusterInputModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.ClusterInputModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.ClusterRendererModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.ClusterRendererModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.CoreModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.CoreModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.CrashReportingModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.CrashReportingModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.DSPGraphModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.DSPGraphModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.DirectorModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.DirectorModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.FileSystemHttpModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.FileSystemHttpModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.GameCenterModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.GameCenterModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.GridModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.GridModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.HotReloadModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.HotReloadModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.IMGUIModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.IMGUIModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.ImageConversionModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.ImageConversionModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.InputModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.InputModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.InputLegacyModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.InputLegacyModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.JSONSerializeModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.JSONSerializeModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.LocalizationModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.LocalizationModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.ParticleSystemModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.ParticleSystemModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.PerformanceReportingModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.PerformanceReportingModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.PhysicsModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.PhysicsModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.Physics2DModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.Physics2DModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.ProfilerModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.ProfilerModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.ScreenCaptureModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.ScreenCaptureModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.SharedInternalsModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.SharedInternalsModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.SpriteMaskModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.SpriteMaskModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.SpriteShapeModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.SpriteShapeModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.StreamingModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.StreamingModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.SubstanceModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.SubstanceModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.TLSModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.TLSModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.TerrainModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.TerrainModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.TerrainPhysicsModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.TerrainPhysicsModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.TextCoreModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.TextCoreModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.TextRenderingModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.TextRenderingModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.TilemapModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.TilemapModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.UIModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.UIModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.UIElementsModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.UIElementsModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.UNETModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.UNETModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.UmbraModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.UmbraModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.UnityAnalyticsModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.UnityAnalyticsModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.UnityConnectModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.UnityConnectModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.UnityTestProtocolModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.UnityTestProtocolModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.UnityWebRequestModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.UnityWebRequestModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.UnityWebRequestAssetBundleModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.UnityWebRequestAssetBundleModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.UnityWebRequestAudioModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.UnityWebRequestAudioModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.UnityWebRequestTextureModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.UnityWebRequestTextureModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.UnityWebRequestWWWModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.UnityWebRequestWWWModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.VFXModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.VFXModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.VRModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.VRModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.VehiclesModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.VehiclesModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.VideoModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.VideoModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.WindModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.WindModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.XRModule">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEngine/UnityEngine.XRModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEditor.VR">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/UnityExtensions/Unity/UnityVR/Editor/UnityEditor.VR.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEditor.Graphs">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/Managed/UnityEditor.Graphs.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEditor.WindowsStandalone.Extensions">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/PlaybackEngines/WindowsStandaloneSupport/UnityEditor.WindowsStandalone.Extensions.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEditor.WebGL.Extensions">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/PlaybackEngines/WebGLSupport/UnityEditor.WebGL.Extensions.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEditor.Android.Extensions">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/PlaybackEngines/AndroidPlayer/UnityEditor.Android.Extensions.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEditor.UWP.Extensions">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/PlaybackEngines/MetroSupport/UnityEditor.UWP.Extensions.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEditor.Advertisements">
|
||||||
|
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/PackageCache/com.unity.ads@2.0.8/Editor/UnityEditor.Advertisements.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.Analytics.Editor">
|
||||||
|
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/PackageCache/com.unity.analytics@3.3.2/Unity.Analytics.Editor.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.Analytics.StandardEvents">
|
||||||
|
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/PackageCache/com.unity.analytics@3.3.2/Unity.Analytics.StandardEvents.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.Analytics.Tracker">
|
||||||
|
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/PackageCache/com.unity.analytics@3.3.2/Unity.Analytics.Tracker.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEditor.Purchasing">
|
||||||
|
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/PackageCache/com.unity.purchasing@2.0.6/Editor/UnityEditor.Purchasing.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="nunit.framework">
|
||||||
|
<HintPath>C:/Repositories/unity-builder/builder/default-build-script/Library/PackageCache/com.unity.ext.nunit@1.0.0/net35/unity-custom/nunit.framework.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="mscorlib">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/mscorlib.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/System.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Core">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/System.Core.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Runtime.Serialization">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/System.Runtime.Serialization.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Xml">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/System.Xml.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Xml.Linq">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/System.Xml.Linq.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Numerics">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/System.Numerics.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Numerics.Vectors">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/System.Numerics.Vectors.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Net.Http">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/System.Net.Http.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.CSharp">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Microsoft.CSharp.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Data">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/System.Data.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.Win32.Primitives">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/Microsoft.Win32.Primitives.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="netstandard">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/netstandard.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.AppContext">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.AppContext.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Collections.Concurrent">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Collections.Concurrent.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Collections">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Collections.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Collections.NonGeneric">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Collections.NonGeneric.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Collections.Specialized">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Collections.Specialized.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.ComponentModel.Annotations">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.ComponentModel.Annotations.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.ComponentModel">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.ComponentModel.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.ComponentModel.EventBasedAsync">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.ComponentModel.EventBasedAsync.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.ComponentModel.Primitives">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.ComponentModel.Primitives.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.ComponentModel.TypeConverter">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.ComponentModel.TypeConverter.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Console">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Console.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Data.Common">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Data.Common.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Diagnostics.Contracts">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Diagnostics.Contracts.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Diagnostics.Debug">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Diagnostics.Debug.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Diagnostics.FileVersionInfo">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Diagnostics.FileVersionInfo.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Diagnostics.Process">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Diagnostics.Process.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Diagnostics.StackTrace">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Diagnostics.StackTrace.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Diagnostics.TextWriterTraceListener">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Diagnostics.TextWriterTraceListener.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Diagnostics.Tools">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Diagnostics.Tools.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Diagnostics.TraceSource">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Diagnostics.TraceSource.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Drawing.Primitives">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Drawing.Primitives.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Dynamic.Runtime">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Dynamic.Runtime.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Globalization.Calendars">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Globalization.Calendars.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Globalization">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Globalization.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Globalization.Extensions">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Globalization.Extensions.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.IO.Compression.ZipFile">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.IO.Compression.ZipFile.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.IO">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.IO.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.IO.FileSystem">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.IO.FileSystem.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.IO.FileSystem.DriveInfo">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.IO.FileSystem.DriveInfo.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.IO.FileSystem.Primitives">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.IO.FileSystem.Primitives.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.IO.FileSystem.Watcher">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.IO.FileSystem.Watcher.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.IO.IsolatedStorage">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.IO.IsolatedStorage.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.IO.MemoryMappedFiles">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.IO.MemoryMappedFiles.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.IO.Pipes">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.IO.Pipes.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.IO.UnmanagedMemoryStream">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.IO.UnmanagedMemoryStream.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Linq">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Linq.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Linq.Expressions">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Linq.Expressions.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Linq.Parallel">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Linq.Parallel.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Linq.Queryable">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Linq.Queryable.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Net.Http.Rtc">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Net.Http.Rtc.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Net.NameResolution">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Net.NameResolution.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Net.NetworkInformation">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Net.NetworkInformation.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Net.Ping">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Net.Ping.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Net.Primitives">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Net.Primitives.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Net.Requests">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Net.Requests.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Net.Security">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Net.Security.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Net.Sockets">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Net.Sockets.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Net.WebHeaderCollection">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Net.WebHeaderCollection.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Net.WebSockets.Client">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Net.WebSockets.Client.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Net.WebSockets">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Net.WebSockets.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.ObjectModel">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.ObjectModel.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Reflection">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Reflection.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Reflection.Emit">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Reflection.Emit.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Reflection.Emit.ILGeneration">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Reflection.Emit.ILGeneration.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Reflection.Emit.Lightweight">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Reflection.Emit.Lightweight.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Reflection.Extensions">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Reflection.Extensions.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Reflection.Primitives">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Reflection.Primitives.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Resources.Reader">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Resources.Reader.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Resources.ResourceManager">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Resources.ResourceManager.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Resources.Writer">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Resources.Writer.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Runtime.CompilerServices.VisualC">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Runtime.CompilerServices.VisualC.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Runtime">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Runtime.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Runtime.Extensions">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Runtime.Extensions.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Runtime.Handles">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Runtime.Handles.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Runtime.InteropServices">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Runtime.InteropServices.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Runtime.InteropServices.RuntimeInformation">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Runtime.InteropServices.RuntimeInformation.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Runtime.InteropServices.WindowsRuntime">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Runtime.InteropServices.WindowsRuntime.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Runtime.Numerics">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Runtime.Numerics.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Runtime.Serialization.Formatters">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Runtime.Serialization.Formatters.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Runtime.Serialization.Json">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Runtime.Serialization.Json.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Runtime.Serialization.Primitives">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Runtime.Serialization.Primitives.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Runtime.Serialization.Xml">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Runtime.Serialization.Xml.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Security.Claims">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Security.Claims.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Security.Cryptography.Algorithms">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Security.Cryptography.Algorithms.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Security.Cryptography.Csp">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Security.Cryptography.Csp.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Security.Cryptography.Encoding">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Security.Cryptography.Encoding.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Security.Cryptography.Primitives">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Security.Cryptography.Primitives.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Security.Cryptography.X509Certificates">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Security.Cryptography.X509Certificates.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Security.Principal">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Security.Principal.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Security.SecureString">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Security.SecureString.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.ServiceModel.Duplex">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.ServiceModel.Duplex.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.ServiceModel.Http">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.ServiceModel.Http.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.ServiceModel.NetTcp">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.ServiceModel.NetTcp.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.ServiceModel.Primitives">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.ServiceModel.Primitives.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.ServiceModel.Security">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.ServiceModel.Security.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Text.Encoding">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Text.Encoding.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Text.Encoding.Extensions">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Text.Encoding.Extensions.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Text.RegularExpressions">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Text.RegularExpressions.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Threading">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Threading.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Threading.Overlapped">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Threading.Overlapped.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Threading.Tasks">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Threading.Tasks.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Threading.Tasks.Parallel">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Threading.Tasks.Parallel.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Threading.Thread">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Threading.Thread.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Threading.ThreadPool">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Threading.ThreadPool.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Threading.Timer">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Threading.Timer.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.ValueTuple">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.ValueTuple.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Xml.ReaderWriter">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Xml.ReaderWriter.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Xml.XDocument">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Xml.XDocument.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Xml.XmlDocument">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Xml.XmlDocument.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Xml.XmlSerializer">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Xml.XmlSerializer.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Xml.XPath">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Xml.XPath.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Xml.XPath.XDocument">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/Facades/System.Xml.XPath.XDocument.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityScript">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/unityscript/UnityScript.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityScript.Lang">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/unityscript/UnityScript.Lang.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Boo.Lang">
|
||||||
|
<HintPath>C:/Program Files/Unity/2019.2.11f1/Editor/Data/MonoBleedingEdge/lib/mono/unityscript/Boo.Lang.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include=".editorconfig" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||||
|
Other similar extension points exist, see Microsoft.Common.targets.
|
||||||
|
<Target Name="BeforeBuild">
|
||||||
|
</Target>
|
||||||
|
<Target Name="AfterBuild">
|
||||||
|
</Target>
|
||||||
|
-->
|
||||||
|
</Project>
|
||||||
@@ -6,9 +6,6 @@ using UnityBuilderAction.Reporting;
|
|||||||
using UnityBuilderAction.Versioning;
|
using UnityBuilderAction.Versioning;
|
||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
using UnityEditor.Build.Reporting;
|
using UnityEditor.Build.Reporting;
|
||||||
#if UNITY_6000_0_OR_NEWER
|
|
||||||
using UnityEditor.Build.Profile;
|
|
||||||
#endif
|
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace UnityBuilderAction
|
namespace UnityBuilderAction
|
||||||
@@ -20,9 +17,36 @@ namespace UnityBuilderAction
|
|||||||
// Gather values from args
|
// Gather values from args
|
||||||
var options = ArgumentsParser.GetValidatedOptions();
|
var options = ArgumentsParser.GetValidatedOptions();
|
||||||
|
|
||||||
|
// Gather values from project
|
||||||
|
var scenes = EditorBuildSettings.scenes.Where(scene => scene.enabled).Select(s => s.path).ToArray();
|
||||||
|
|
||||||
|
// Get all buildOptions from options
|
||||||
|
BuildOptions buildOptions = BuildOptions.None;
|
||||||
|
foreach (string buildOptionString in Enum.GetNames(typeof(BuildOptions))) {
|
||||||
|
if (options.ContainsKey(buildOptionString)) {
|
||||||
|
BuildOptions buildOptionEnum = (BuildOptions) Enum.Parse(typeof(BuildOptions), buildOptionString);
|
||||||
|
buildOptions |= buildOptionEnum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define BuildPlayer Options
|
||||||
|
var buildPlayerOptions = new BuildPlayerOptions {
|
||||||
|
scenes = scenes,
|
||||||
|
locationPathName = options["customBuildPath"],
|
||||||
|
target = (BuildTarget) Enum.Parse(typeof(BuildTarget), options["buildTarget"]),
|
||||||
|
options = buildOptions
|
||||||
|
};
|
||||||
|
|
||||||
// Set version for this build
|
// Set version for this build
|
||||||
VersionApplicator.SetVersion(options["buildVersion"]);
|
VersionApplicator.SetVersion(options["buildVersion"]);
|
||||||
|
|
||||||
|
// Apply Android settings
|
||||||
|
if (buildPlayerOptions.target == BuildTarget.Android)
|
||||||
|
{
|
||||||
|
VersionApplicator.SetAndroidVersionCode(options["androidVersionCode"]);
|
||||||
|
AndroidSettings.Apply(options);
|
||||||
|
}
|
||||||
|
|
||||||
// Execute default AddressableAsset content build, if the package is installed.
|
// Execute default AddressableAsset content build, if the package is installed.
|
||||||
// Version defines would be the best solution here, but Unity 2018 doesn't support that,
|
// Version defines would be the best solution here, but Unity 2018 doesn't support that,
|
||||||
// so we fall back to using reflection instead.
|
// so we fall back to using reflection instead.
|
||||||
@@ -39,85 +63,10 @@ namespace UnityBuilderAction
|
|||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Debug.LogError("Failed to run default addressables build:\n" + e);
|
Debug.LogError($"Failed to run default addressables build:\n{e}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get all buildOptions from options
|
|
||||||
BuildOptions buildOptions = BuildOptions.None;
|
|
||||||
foreach (string buildOptionString in Enum.GetNames(typeof(BuildOptions))) {
|
|
||||||
if (options.ContainsKey(buildOptionString)) {
|
|
||||||
BuildOptions buildOptionEnum = (BuildOptions) Enum.Parse(typeof(BuildOptions), buildOptionString);
|
|
||||||
buildOptions |= buildOptionEnum;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Depending on whether the build is using a build profile, `buildPlayerOptions` will an instance
|
|
||||||
// of either `UnityEditor.BuildPlayerOptions` or `UnityEditor.BuildPlayerWithProfileOptions`
|
|
||||||
dynamic buildPlayerOptions;
|
|
||||||
|
|
||||||
if (options.TryGetValue("activeBuildProfile", out var buildProfilePath)) {
|
|
||||||
if (string.IsNullOrEmpty(buildProfilePath)) {
|
|
||||||
throw new Exception("`-activeBuildProfile` is set but with an empty value; this shouldn't happen");
|
|
||||||
}
|
|
||||||
|
|
||||||
#if UNITY_6000_0_OR_NEWER
|
|
||||||
// Load build profile from Assets folder
|
|
||||||
var buildProfile = AssetDatabase.LoadAssetAtPath<BuildProfile>(buildProfilePath)
|
|
||||||
?? throw new Exception("Build profile file not found at path: " + buildProfilePath);
|
|
||||||
|
|
||||||
// no need to set active profile, as already set by `-activeBuildProfile` CLI argument
|
|
||||||
// BuildProfile.SetActiveBuildProfile(buildProfile);
|
|
||||||
Debug.Log($"build profile: {buildProfile.name}");
|
|
||||||
|
|
||||||
// Define BuildPlayerWithProfileOptions
|
|
||||||
buildPlayerOptions = new BuildPlayerWithProfileOptions {
|
|
||||||
buildProfile = buildProfile,
|
|
||||||
locationPathName = options["customBuildPath"],
|
|
||||||
options = buildOptions,
|
|
||||||
};
|
|
||||||
#else // UNITY_6000_0_OR_NEWER
|
|
||||||
throw new Exception("Build profiles are not supported by this version of Unity (" + Application.unityVersion +")");
|
|
||||||
#endif // UNITY_6000_0_OR_NEWER
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
#if BUILD_PROFILE_LOADED
|
|
||||||
throw new Exception("Build profile's define symbol present; shouldn't happen");
|
|
||||||
#endif // BUILD_PROFILE_LOADED
|
|
||||||
|
|
||||||
// Gather values from project
|
|
||||||
var scenes = EditorBuildSettings.scenes.Where(scene => scene.enabled).Select(s => s.path).ToArray();
|
|
||||||
|
|
||||||
#if UNITY_2021_2_OR_NEWER
|
|
||||||
// Determine subtarget
|
|
||||||
StandaloneBuildSubtarget buildSubtarget;
|
|
||||||
if (!options.TryGetValue("standaloneBuildSubtarget", out var subtargetValue) || !Enum.TryParse(subtargetValue, out buildSubtarget)) {
|
|
||||||
buildSubtarget = default;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
BuildTarget buildTarget = (BuildTarget) Enum.Parse(typeof(BuildTarget), options["buildTarget"]);
|
|
||||||
|
|
||||||
// Define BuildPlayerOptions
|
|
||||||
buildPlayerOptions = new BuildPlayerOptions {
|
|
||||||
scenes = scenes,
|
|
||||||
locationPathName = options["customBuildPath"],
|
|
||||||
target = buildTarget,
|
|
||||||
options = buildOptions,
|
|
||||||
#if UNITY_2021_2_OR_NEWER
|
|
||||||
subtarget = (int) buildSubtarget
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
// Apply Android settings
|
|
||||||
if (buildTarget == BuildTarget.Android) {
|
|
||||||
VersionApplicator.SetAndroidVersionCode(options["androidVersionCode"]);
|
|
||||||
AndroidSettings.Apply(options);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Perform build
|
// Perform build
|
||||||
BuildReport buildReport = BuildPipeline.BuildPlayer(buildPlayerOptions);
|
BuildReport buildReport = BuildPipeline.BuildPlayer(buildPlayerOptions);
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace UnityBuilderAction.Input
|
namespace UnityBuilderAction.Input
|
||||||
{
|
{
|
||||||
public static class AndroidSettings
|
public class AndroidSettings
|
||||||
{
|
{
|
||||||
public static void Apply(Dictionary<string, string> options)
|
public static void Apply(Dictionary<string, string> options)
|
||||||
{
|
{
|
||||||
|
EditorUserBuildSettings.buildAppBundle = options["customBuildPath"].EndsWith(".aab");
|
||||||
#if UNITY_2019_1_OR_NEWER
|
#if UNITY_2019_1_OR_NEWER
|
||||||
if (options.TryGetValue("androidKeystoreName", out string keystoreName) && !string.IsNullOrEmpty(keystoreName))
|
if (options.TryGetValue("androidKeystoreName", out string keystoreName) && !string.IsNullOrEmpty(keystoreName))
|
||||||
{
|
{
|
||||||
@@ -16,21 +16,13 @@ namespace UnityBuilderAction.Input
|
|||||||
PlayerSettings.Android.keystoreName = keystoreName;
|
PlayerSettings.Android.keystoreName = keystoreName;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
// Can't use out variable declaration as Unity 2018 doesn't support it
|
if (options.TryGetValue("androidKeystorePass", out string keystorePass) && !string.IsNullOrEmpty(keystorePass))
|
||||||
string keystorePass;
|
|
||||||
if (options.TryGetValue("androidKeystorePass", out keystorePass) && !string.IsNullOrEmpty(keystorePass))
|
|
||||||
PlayerSettings.Android.keystorePass = keystorePass;
|
PlayerSettings.Android.keystorePass = keystorePass;
|
||||||
|
if (options.TryGetValue("androidKeyaliasName", out string keyaliasName) && !string.IsNullOrEmpty(keyaliasName))
|
||||||
string keyaliasName;
|
|
||||||
if (options.TryGetValue("androidKeyaliasName", out keyaliasName) && !string.IsNullOrEmpty(keyaliasName))
|
|
||||||
PlayerSettings.Android.keyaliasName = keyaliasName;
|
PlayerSettings.Android.keyaliasName = keyaliasName;
|
||||||
|
if (options.TryGetValue("androidKeyaliasPass", out string keyaliasPass) && !string.IsNullOrEmpty(keyaliasPass))
|
||||||
string keyaliasPass;
|
|
||||||
if (options.TryGetValue("androidKeyaliasPass", out keyaliasPass) && !string.IsNullOrEmpty(keyaliasPass))
|
|
||||||
PlayerSettings.Android.keyaliasPass = keyaliasPass;
|
PlayerSettings.Android.keyaliasPass = keyaliasPass;
|
||||||
|
if (options.TryGetValue("androidTargetSdkVersion", out string androidTargetSdkVersion) && !string.IsNullOrEmpty(androidTargetSdkVersion))
|
||||||
string androidTargetSdkVersion;
|
|
||||||
if (options.TryGetValue("androidTargetSdkVersion", out androidTargetSdkVersion) && !string.IsNullOrEmpty(androidTargetSdkVersion))
|
|
||||||
{
|
{
|
||||||
var targetSdkVersion = AndroidSdkVersions.AndroidApiLevelAuto;
|
var targetSdkVersion = AndroidSdkVersions.AndroidApiLevelAuto;
|
||||||
try
|
try
|
||||||
@@ -44,107 +36,6 @@ namespace UnityBuilderAction.Input
|
|||||||
}
|
}
|
||||||
PlayerSettings.Android.targetSdkVersion = targetSdkVersion;
|
PlayerSettings.Android.targetSdkVersion = targetSdkVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
string androidExportType;
|
|
||||||
if (options.TryGetValue("androidExportType", out androidExportType) && !string.IsNullOrEmpty(androidExportType))
|
|
||||||
{
|
|
||||||
// Only exists in 2018.3 and above
|
|
||||||
PropertyInfo buildAppBundle = typeof(EditorUserBuildSettings)
|
|
||||||
.GetProperty("buildAppBundle", BindingFlags.Public | BindingFlags.Static);
|
|
||||||
switch (androidExportType)
|
|
||||||
{
|
|
||||||
case "androidStudioProject":
|
|
||||||
EditorUserBuildSettings.exportAsGoogleAndroidProject = true;
|
|
||||||
if (buildAppBundle != null)
|
|
||||||
buildAppBundle.SetValue(null, false, null);
|
|
||||||
break;
|
|
||||||
case "androidAppBundle":
|
|
||||||
EditorUserBuildSettings.exportAsGoogleAndroidProject = false;
|
|
||||||
if (buildAppBundle != null)
|
|
||||||
buildAppBundle.SetValue(null, true, null);
|
|
||||||
break;
|
|
||||||
case "androidPackage":
|
|
||||||
EditorUserBuildSettings.exportAsGoogleAndroidProject = false;
|
|
||||||
if (buildAppBundle != null)
|
|
||||||
buildAppBundle.SetValue(null, false, null);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
string symbolType;
|
|
||||||
if (options.TryGetValue("androidSymbolType", out symbolType) && !string.IsNullOrEmpty(symbolType))
|
|
||||||
{
|
|
||||||
#if UNITY_6000_0_OR_NEWER
|
|
||||||
switch (symbolType)
|
|
||||||
{
|
|
||||||
case "public":
|
|
||||||
SetDebugSymbols("SymbolTable");
|
|
||||||
break;
|
|
||||||
case "debugging":
|
|
||||||
SetDebugSymbols("Full");
|
|
||||||
break;
|
|
||||||
case "none":
|
|
||||||
SetDebugSymbols("None");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#elif UNITY_2021_1_OR_NEWER
|
|
||||||
switch (symbolType)
|
|
||||||
{
|
|
||||||
case "public":
|
|
||||||
EditorUserBuildSettings.androidCreateSymbols = AndroidCreateSymbols.Public;
|
|
||||||
break;
|
|
||||||
case "debugging":
|
|
||||||
EditorUserBuildSettings.androidCreateSymbols = AndroidCreateSymbols.Debugging;
|
|
||||||
break;
|
|
||||||
case "none":
|
|
||||||
EditorUserBuildSettings.androidCreateSymbols = AndroidCreateSymbols.Disabled;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#elif UNITY_2019_2_OR_NEWER
|
|
||||||
switch (symbolType)
|
|
||||||
{
|
|
||||||
case "public":
|
|
||||||
case "debugging":
|
|
||||||
EditorUserBuildSettings.androidCreateSymbolsZip = true;
|
|
||||||
break;
|
|
||||||
case "none":
|
|
||||||
EditorUserBuildSettings.androidCreateSymbolsZip = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if UNITY_6000_0_OR_NEWER
|
|
||||||
private static void SetDebugSymbols(string enumValueName)
|
|
||||||
{
|
|
||||||
// UnityEditor.Android.UserBuildSettings and Unity.Android.Types.DebugSymbolLevel are part of the Unity Android module.
|
|
||||||
// Reflection is used here to ensure the code works even if the module is not installed.
|
|
||||||
|
|
||||||
var debugSymbolsType = Type.GetType("UnityEditor.Android.UserBuildSettings+DebugSymbols, UnityEditor.Android.Extensions");
|
|
||||||
if (debugSymbolsType == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var levelProp = debugSymbolsType.GetProperty("level", BindingFlags.Static | BindingFlags.Public);
|
|
||||||
if (levelProp == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var enumType = Type.GetType("Unity.Android.Types.DebugSymbolLevel, Unity.Android.Types");
|
|
||||||
if (enumType == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Enum.TryParse(enumType, enumValueName, false , out var enumValue))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
levelProp.SetValue(null, enumValue);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,53 +12,33 @@ namespace UnityBuilderAction.Input
|
|||||||
|
|
||||||
public static Dictionary<string, string> GetValidatedOptions()
|
public static Dictionary<string, string> GetValidatedOptions()
|
||||||
{
|
{
|
||||||
Dictionary<string, string> validatedOptions;
|
ParseCommandLineArguments(out var validatedOptions);
|
||||||
ParseCommandLineArguments(out validatedOptions);
|
|
||||||
|
|
||||||
string projectPath;
|
if (!validatedOptions.TryGetValue("projectPath", out var projectPath)) {
|
||||||
if (!validatedOptions.TryGetValue("projectPath", out projectPath)) {
|
|
||||||
Console.WriteLine("Missing argument -projectPath");
|
Console.WriteLine("Missing argument -projectPath");
|
||||||
EditorApplication.Exit(110);
|
EditorApplication.Exit(110);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if UNITY_6000_0_OR_NEWER
|
if (!validatedOptions.TryGetValue("buildTarget", out var buildTarget)) {
|
||||||
var buildProfileSupport = true;
|
|
||||||
#else
|
|
||||||
var buildProfileSupport = false;
|
|
||||||
#endif // UNITY_6000_0_OR_NEWER
|
|
||||||
|
|
||||||
string buildProfile;
|
|
||||||
if (buildProfileSupport && validatedOptions.TryGetValue("activeBuildProfile", out buildProfile)) {
|
|
||||||
if (validatedOptions.ContainsKey("buildTarget")) {
|
|
||||||
Console.WriteLine("Extra argument -buildTarget");
|
|
||||||
EditorApplication.Exit(122);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
string buildTarget;
|
|
||||||
if (!validatedOptions.TryGetValue("buildTarget", out buildTarget)) {
|
|
||||||
Console.WriteLine("Missing argument -buildTarget");
|
Console.WriteLine("Missing argument -buildTarget");
|
||||||
EditorApplication.Exit(120);
|
EditorApplication.Exit(120);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Enum.IsDefined(typeof(BuildTarget), buildTarget)) {
|
if (!Enum.IsDefined(typeof(BuildTarget), buildTarget)) {
|
||||||
Console.WriteLine(buildTarget + " is not a defined " + typeof(BuildTarget).Name);
|
|
||||||
EditorApplication.Exit(121);
|
EditorApplication.Exit(121);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
string customBuildPath;
|
if (!validatedOptions.TryGetValue("customBuildPath", out var customBuildPath)) {
|
||||||
if (!validatedOptions.TryGetValue("customBuildPath", out customBuildPath)) {
|
|
||||||
Console.WriteLine("Missing argument -customBuildPath");
|
Console.WriteLine("Missing argument -customBuildPath");
|
||||||
EditorApplication.Exit(130);
|
EditorApplication.Exit(130);
|
||||||
}
|
}
|
||||||
|
|
||||||
const string defaultCustomBuildName = "TestBuild";
|
const string defaultCustomBuildName = "TestBuild";
|
||||||
string customBuildName;
|
if (!validatedOptions.TryGetValue("customBuildName", out var customBuildName)) {
|
||||||
if (!validatedOptions.TryGetValue("customBuildName", out customBuildName)) {
|
Console.WriteLine($"Missing argument -customBuildName, defaulting to {defaultCustomBuildName}.");
|
||||||
Console.WriteLine("Missing argument -customBuildName, defaulting to" + defaultCustomBuildName);
|
|
||||||
validatedOptions.Add("customBuildName", defaultCustomBuildName);
|
validatedOptions.Add("customBuildName", defaultCustomBuildName);
|
||||||
} else if (customBuildName == "") {
|
} else if (customBuildName == "") {
|
||||||
Console.WriteLine("Invalid argument -customBuildName, defaulting to" + defaultCustomBuildName);
|
Console.WriteLine($"Invalid argument -customBuildName, defaulting to {defaultCustomBuildName}.");
|
||||||
validatedOptions.Add("customBuildName", defaultCustomBuildName);
|
validatedOptions.Add("customBuildName", defaultCustomBuildName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,11 +51,11 @@ namespace UnityBuilderAction.Input
|
|||||||
string[] args = Environment.GetCommandLineArgs();
|
string[] args = Environment.GetCommandLineArgs();
|
||||||
|
|
||||||
Console.WriteLine(
|
Console.WriteLine(
|
||||||
EOL +
|
$"{EOL}" +
|
||||||
"###########################" + EOL +
|
$"###########################{EOL}" +
|
||||||
"# Parsing settings #" + EOL +
|
$"# Parsing settings #{EOL}" +
|
||||||
"###########################" + EOL +
|
$"###########################{EOL}" +
|
||||||
EOL
|
$"{EOL}"
|
||||||
);
|
);
|
||||||
|
|
||||||
// Extract flags with optional values
|
// Extract flags with optional values
|
||||||
@@ -92,7 +72,7 @@ namespace UnityBuilderAction.Input
|
|||||||
string displayValue = secret ? "*HIDDEN*" : "\"" + value + "\"";
|
string displayValue = secret ? "*HIDDEN*" : "\"" + value + "\"";
|
||||||
|
|
||||||
// Assign
|
// Assign
|
||||||
Console.WriteLine("Found flag \"" + flag + "\" with value " + displayValue);
|
Console.WriteLine($"Found flag \"{flag}\" with value {displayValue}.");
|
||||||
providedArguments.Add(flag, value);
|
providedArguments.Add(flag, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
using System;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEditor;
|
|
||||||
|
|
||||||
namespace UnityBuilderAction.Reporting
|
|
||||||
{
|
|
||||||
[InitializeOnLoad]
|
|
||||||
static class CompileListener
|
|
||||||
{
|
|
||||||
static CompileListener()
|
|
||||||
{
|
|
||||||
if (Application.isBatchMode)
|
|
||||||
{
|
|
||||||
Application.logMessageReceived += Application_logMessageReceived;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void Application_logMessageReceived(string condition, string stackTrace, LogType type)
|
|
||||||
{
|
|
||||||
string prefix = "";
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case LogType.Error:
|
|
||||||
prefix = "error";
|
|
||||||
break;
|
|
||||||
case LogType.Warning:
|
|
||||||
prefix = "warning";
|
|
||||||
break;
|
|
||||||
case LogType.Exception:
|
|
||||||
prefix = "error";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Console.WriteLine(Environment.NewLine + "::" + prefix + "::" + condition + Environment.NewLine + stackTrace);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: fad44373fb7b61a4bb584e2675795aca
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -11,16 +11,16 @@ namespace UnityBuilderAction.Reporting
|
|||||||
public static void ReportSummary(BuildSummary summary)
|
public static void ReportSummary(BuildSummary summary)
|
||||||
{
|
{
|
||||||
Console.WriteLine(
|
Console.WriteLine(
|
||||||
EOL +
|
$"{EOL}" +
|
||||||
"###########################" + EOL +
|
$"###########################{EOL}" +
|
||||||
"# Build results #" + EOL +
|
$"# Build results #{EOL}" +
|
||||||
"###########################" + EOL +
|
$"###########################{EOL}" +
|
||||||
EOL +
|
$"{EOL}" +
|
||||||
"Duration: " + summary.totalTime.ToString() + EOL +
|
$"Duration: {summary.totalTime.ToString()}{EOL}" +
|
||||||
"Warnings: " + summary.totalWarnings.ToString() + EOL +
|
$"Warnings: {summary.totalWarnings.ToString()}{EOL}" +
|
||||||
"Errors: " + summary.totalErrors.ToString() + EOL +
|
$"Errors: {summary.totalErrors.ToString()}{EOL}" +
|
||||||
"Size: " + summary.totalSize.ToString() + " bytes" + EOL +
|
$"Size: {summary.totalSize.ToString()} bytes{EOL}" +
|
||||||
EOL
|
$"{EOL}"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,11 +21,11 @@ namespace UnityBuilderAction.Versioning
|
|||||||
version = GetSemanticCommitVersion();
|
version = GetSemanticCommitVersion();
|
||||||
Console.WriteLine("Repository has a valid version tag.");
|
Console.WriteLine("Repository has a valid version tag.");
|
||||||
} else {
|
} else {
|
||||||
version = "0.0." + GetTotalNumberOfCommits();
|
version = $"0.0.{GetTotalNumberOfCommits()}";
|
||||||
Console.WriteLine("Repository does not have tags to base the version on.");
|
Console.WriteLine("Repository does not have tags to base the version on.");
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine("Version is " + version);
|
Console.WriteLine($"Version is {version}");
|
||||||
|
|
||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
@@ -106,8 +106,7 @@ namespace UnityBuilderAction.Versioning
|
|||||||
using (var process = new System.Diagnostics.Process()) {
|
using (var process = new System.Diagnostics.Process()) {
|
||||||
string workingDirectory = UnityEngine.Application.dataPath;
|
string workingDirectory = UnityEngine.Application.dataPath;
|
||||||
|
|
||||||
string output, errors;
|
int exitCode = process.Run(application, arguments, workingDirectory, out string output, out string errors);
|
||||||
int exitCode = process.Run(application, arguments, workingDirectory, out output, out errors);
|
|
||||||
if (exitCode != 0) { throw new GitException(exitCode, errors); }
|
if (exitCode != 0) { throw new GitException(exitCode, errors); }
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
|
|||||||
10
dist/default-build-script/ProjectSettings/XRSettings.asset
vendored
Normal file
10
dist/default-build-script/ProjectSettings/XRSettings.asset
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"m_SettingKeys": [
|
||||||
|
"VR Device Disabled",
|
||||||
|
"VR Device User Alert"
|
||||||
|
],
|
||||||
|
"m_SettingValues": [
|
||||||
|
"False",
|
||||||
|
"False"
|
||||||
|
]
|
||||||
|
}
|
||||||
20
dist/default-build-script/default-build-script.sln
vendored
Normal file
20
dist/default-build-script/default-build-script.sln
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 11.00
|
||||||
|
# Visual Studio 2010
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Assembly-CSharp-Editor", "Assembly-CSharp-Editor.csproj", "{B7F8614B-1EC2-9D3A-DA1C-4D279A867D74}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{B7F8614B-1EC2-9D3A-DA1C-4D279A867D74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{B7F8614B-1EC2-9D3A-DA1C-4D279A867D74}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{B7F8614B-1EC2-9D3A-DA1C-4D279A867D74}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{B7F8614B-1EC2-9D3A-DA1C-4D279A867D74}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
3
dist/default-build-script/default-build-script.sln.DotSettings
vendored
Normal file
3
dist/default-build-script/default-build-script.sln.DotSettings
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Untracked/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Versioning/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||||
351177
dist/index.js
generated
vendored
351177
dist/index.js
generated
vendored
File diff suppressed because one or more lines are too long
2
dist/index.js.map
generated
vendored
2
dist/index.js.map
generated
vendored
File diff suppressed because one or more lines are too long
16838
dist/licenses.txt
generated
vendored
16838
dist/licenses.txt
generated
vendored
File diff suppressed because it is too large
Load Diff
36
dist/platforms/mac/entrypoint.sh
vendored
36
dist/platforms/mac/entrypoint.sh
vendored
@@ -1,40 +1,28 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
#
|
#
|
||||||
# Perform Activation
|
# Create directories for license activation
|
||||||
#
|
#
|
||||||
|
|
||||||
if [ "$SKIP_ACTIVATION" != "true" ]; then
|
sudo mkdir /Library/Application\ Support/Unity
|
||||||
UNITY_LICENSE_PATH="/Library/Application Support/Unity"
|
sudo chmod -R 777 /Library/Application\ Support/Unity
|
||||||
|
|
||||||
if [ ! -d "$UNITY_LICENSE_PATH" ]; then
|
ACTIVATE_LICENSE_PATH="$ACTION_FOLDER/BlankProject"
|
||||||
echo "Creating Unity License Directory"
|
mkdir -p "$ACTIVATE_LICENSE_PATH"
|
||||||
sudo mkdir -p "$UNITY_LICENSE_PATH"
|
|
||||||
sudo chmod -R 777 "$UNITY_LICENSE_PATH"
|
|
||||||
fi;
|
|
||||||
|
|
||||||
ACTIVATE_LICENSE_PATH="$ACTION_FOLDER/BlankProject"
|
|
||||||
mkdir -p "$ACTIVATE_LICENSE_PATH"
|
|
||||||
|
|
||||||
source $ACTION_FOLDER/platforms/mac/steps/activate.sh
|
|
||||||
else
|
|
||||||
echo "Skipping activation"
|
|
||||||
fi
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Run Build
|
# Run steps
|
||||||
#
|
#
|
||||||
|
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
|
||||||
|
|
||||||
#
|
#
|
||||||
# License Cleanup
|
# Remove license activation directory
|
||||||
#
|
#
|
||||||
|
|
||||||
if [ "$SKIP_ACTIVATION" != "true" ]; then
|
sudo rm -r /Library/Application\ Support/Unity
|
||||||
source $ACTION_FOLDER/platforms/mac/steps/return_license.sh
|
rm -r "$ACTIVATE_LICENSE_PATH"
|
||||||
rm -r "$ACTIVATE_LICENSE_PATH"
|
|
||||||
fi
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Instructions for debugging
|
# Instructions for debugging
|
||||||
@@ -49,7 +37,7 @@ echo ""
|
|||||||
echo "Please note that the exit code is not very descriptive."
|
echo "Please note that the exit code is not very descriptive."
|
||||||
echo "Most likely it will not help you solve the issue."
|
echo "Most likely it will not help you solve the issue."
|
||||||
echo ""
|
echo ""
|
||||||
echo "To find the reason for failure: please search for errors in the log above and check for annotations in the summary view."
|
echo "To find the reason for failure: please search for errors in the log above."
|
||||||
echo ""
|
echo ""
|
||||||
fi;
|
fi;
|
||||||
|
|
||||||
|
|||||||
75
dist/platforms/mac/steps/activate.sh
vendored
75
dist/platforms/mac/steps/activate.sh
vendored
@@ -4,69 +4,21 @@
|
|||||||
echo "Changing to \"$ACTIVATE_LICENSE_PATH\" directory."
|
echo "Changing to \"$ACTIVATE_LICENSE_PATH\" directory."
|
||||||
pushd "$ACTIVATE_LICENSE_PATH"
|
pushd "$ACTIVATE_LICENSE_PATH"
|
||||||
|
|
||||||
if [[ -n "$UNITY_SERIAL" && -n "$UNITY_EMAIL" && -n "$UNITY_PASSWORD" ]]; then
|
echo "Requesting activation"
|
||||||
#
|
|
||||||
# SERIAL LICENSE MODE
|
|
||||||
#
|
|
||||||
# This will activate unity, using the serial activation process.
|
|
||||||
#
|
|
||||||
|
|
||||||
echo "Requesting activation"
|
# Activate license
|
||||||
|
/Applications/Unity/Hub/Editor/$UNITY_VERSION/Unity.app/Contents/MacOS/Unity \
|
||||||
|
-logFile /dev/stdout \
|
||||||
|
-batchmode \
|
||||||
|
-nographics \
|
||||||
|
-quit \
|
||||||
|
-serial "$UNITY_SERIAL" \
|
||||||
|
-username "$UNITY_EMAIL" \
|
||||||
|
-password "$UNITY_PASSWORD" \
|
||||||
|
-projectPath "$ACTIVATE_LICENSE_PATH"
|
||||||
|
|
||||||
# Activate license
|
# Store the exit code from the verify command
|
||||||
/Applications/Unity/Hub/Editor/$UNITY_VERSION/Unity.app/Contents/MacOS/Unity \
|
UNITY_EXIT_CODE=$?
|
||||||
-logFile - \
|
|
||||||
-batchmode \
|
|
||||||
-nographics \
|
|
||||||
-quit \
|
|
||||||
-serial "$UNITY_SERIAL" \
|
|
||||||
-username "$UNITY_EMAIL" \
|
|
||||||
-password "$UNITY_PASSWORD" \
|
|
||||||
-projectPath "$ACTIVATE_LICENSE_PATH"
|
|
||||||
|
|
||||||
# Store the exit code from the verify command
|
|
||||||
UNITY_EXIT_CODE=$?
|
|
||||||
|
|
||||||
elif [[ -n "$UNITY_LICENSING_SERVER" ]]; then
|
|
||||||
#
|
|
||||||
# Custom Unity License Server
|
|
||||||
#
|
|
||||||
echo "Adding licensing server config"
|
|
||||||
mkdir -p "$UNITY_LICENSE_PATH/config/"
|
|
||||||
cp "$ACTION_FOLDER/unity-config/services-config.json" "$UNITY_LICENSE_PATH/config/services-config.json"
|
|
||||||
|
|
||||||
/Applications/Unity/Hub/Editor/$UNITY_VERSION/Unity.app/Contents/Frameworks/UnityLicensingClient.app/Contents/MacOS/Unity.Licensing.Client \
|
|
||||||
--acquire-floating > license.txt
|
|
||||||
|
|
||||||
# Store the exit code from the verify command
|
|
||||||
UNITY_EXIT_CODE=$?
|
|
||||||
|
|
||||||
if [ $UNITY_EXIT_CODE -eq 0 ]; then
|
|
||||||
PARSEDFILE=$(grep -oE '\"[^"]*\"' < license.txt | tr -d '"')
|
|
||||||
export FLOATING_LICENSE
|
|
||||||
FLOATING_LICENSE=$(sed -n 2p <<< "$PARSEDFILE")
|
|
||||||
FLOATING_LICENSE_TIMEOUT=$(sed -n 4p <<< "$PARSEDFILE")
|
|
||||||
|
|
||||||
echo "Acquired floating license: \"$FLOATING_LICENSE\" with timeout $FLOATING_LICENSE_TIMEOUT"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
#
|
|
||||||
# NO LICENSE ACTIVATION STRATEGY MATCHED
|
|
||||||
#
|
|
||||||
# This will exit since no activation strategies could be matched.
|
|
||||||
#
|
|
||||||
echo "License activation strategy could not be determined."
|
|
||||||
echo ""
|
|
||||||
echo "Visit https://game.ci/docs/github/activation for more"
|
|
||||||
echo "details on how to set up one of the possible activation strategies."
|
|
||||||
|
|
||||||
echo "::error ::No valid license activation strategy could be determined. Make sure to provide UNITY_EMAIL, UNITY_PASSWORD, and either a UNITY_SERIAL \
|
|
||||||
or UNITY_LICENSE. Otherwise please use UNITY_LICENSING_SERVER. See more info at https://game.ci/docs/github/activation"
|
|
||||||
|
|
||||||
# Immediately exit as no UNITY_EXIT_CODE can be derived.
|
|
||||||
exit 1;
|
|
||||||
|
|
||||||
fi
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Display information about the result
|
# Display information about the result
|
||||||
@@ -78,7 +30,6 @@ else
|
|||||||
# Activation failed so exit with the code from the license verification step
|
# Activation failed so exit with the code from the license verification step
|
||||||
echo "Unclassified error occured while trying to activate license."
|
echo "Unclassified error occured while trying to activate license."
|
||||||
echo "Exit code was: $UNITY_EXIT_CODE"
|
echo "Exit code was: $UNITY_EXIT_CODE"
|
||||||
echo "::error ::There was an error while trying to activate the Unity license."
|
|
||||||
exit $UNITY_EXIT_CODE
|
exit $UNITY_EXIT_CODE
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
33
dist/platforms/mac/steps/build.sh
vendored
33
dist/platforms/mac/steps/build.sh
vendored
@@ -19,23 +19,6 @@ echo "Using build name \"$BUILD_NAME\"."
|
|||||||
|
|
||||||
echo "Using build target \"$BUILD_TARGET\"."
|
echo "Using build target \"$BUILD_TARGET\"."
|
||||||
|
|
||||||
#
|
|
||||||
# Display the build profile
|
|
||||||
#
|
|
||||||
|
|
||||||
if [ -z "$BUILD_PROFILE" ]; then
|
|
||||||
# User has not provided a build profile
|
|
||||||
#
|
|
||||||
echo "Doing a default \"$BUILD_TARGET\" platform build."
|
|
||||||
#
|
|
||||||
else
|
|
||||||
# User has provided a path to a build profile `.asset` file
|
|
||||||
#
|
|
||||||
echo "Using build profile \"$BUILD_PROFILE\" relative to \"$UNITY_PROJECT_PATH\"."
|
|
||||||
#
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Display build path and file
|
# Display build path and file
|
||||||
#
|
#
|
||||||
@@ -145,17 +128,16 @@ echo ""
|
|||||||
# Reference: https://docs.unity3d.com/2019.3/Documentation/Manual/CommandLineArguments.html
|
# Reference: https://docs.unity3d.com/2019.3/Documentation/Manual/CommandLineArguments.html
|
||||||
|
|
||||||
/Applications/Unity/Hub/Editor/$UNITY_VERSION/Unity.app/Contents/MacOS/Unity \
|
/Applications/Unity/Hub/Editor/$UNITY_VERSION/Unity.app/Contents/MacOS/Unity \
|
||||||
-logFile - \
|
-quit \
|
||||||
$( [ "${MANUAL_EXIT}" == "true" ] || echo "-quit" ) \
|
|
||||||
-batchmode \
|
-batchmode \
|
||||||
$( [ "${ENABLE_GPU}" == "true" ] || echo "-nographics" ) \
|
-nographics \
|
||||||
|
-username "$UNITY_EMAIL" \
|
||||||
|
-password "$UNITY_PASSWORD" \
|
||||||
-customBuildName "$BUILD_NAME" \
|
-customBuildName "$BUILD_NAME" \
|
||||||
-projectPath "$UNITY_PROJECT_PATH" \
|
-projectPath "$UNITY_PROJECT_PATH" \
|
||||||
$( [ -z "$BUILD_PROFILE" ] && echo "-buildTarget $BUILD_TARGET") \
|
-buildTarget "$BUILD_TARGET" \
|
||||||
-customBuildTarget "$BUILD_TARGET" \
|
-customBuildTarget "$BUILD_TARGET" \
|
||||||
-customBuildPath "$CUSTOM_BUILD_PATH" \
|
-customBuildPath "$CUSTOM_BUILD_PATH" \
|
||||||
-customBuildProfile "$BUILD_PROFILE" \
|
|
||||||
${BUILD_PROFILE:+-activeBuildProfile} ${BUILD_PROFILE:+"$BUILD_PROFILE"} \
|
|
||||||
-executeMethod "$BUILD_METHOD" \
|
-executeMethod "$BUILD_METHOD" \
|
||||||
-buildVersion "$VERSION" \
|
-buildVersion "$VERSION" \
|
||||||
-androidVersionCode "$ANDROID_VERSION_CODE" \
|
-androidVersionCode "$ANDROID_VERSION_CODE" \
|
||||||
@@ -164,9 +146,8 @@ echo ""
|
|||||||
-androidKeyaliasName "$ANDROID_KEYALIAS_NAME" \
|
-androidKeyaliasName "$ANDROID_KEYALIAS_NAME" \
|
||||||
-androidKeyaliasPass "$ANDROID_KEYALIAS_PASS" \
|
-androidKeyaliasPass "$ANDROID_KEYALIAS_PASS" \
|
||||||
-androidTargetSdkVersion "$ANDROID_TARGET_SDK_VERSION" \
|
-androidTargetSdkVersion "$ANDROID_TARGET_SDK_VERSION" \
|
||||||
-androidExportType "$ANDROID_EXPORT_TYPE" \
|
$CUSTOM_PARAMETERS \
|
||||||
-androidSymbolType "$ANDROID_SYMBOL_TYPE" \
|
> "$UNITY_PROJECT_PATH/out.log" 2>&1
|
||||||
$CUSTOM_PARAMETERS
|
|
||||||
|
|
||||||
# Catch exit code
|
# Catch exit code
|
||||||
BUILD_EXIT_CODE=$?
|
BUILD_EXIT_CODE=$?
|
||||||
|
|||||||
32
dist/platforms/mac/steps/return_license.sh
vendored
32
dist/platforms/mac/steps/return_license.sh
vendored
@@ -4,29 +4,15 @@
|
|||||||
echo "Changing to \"$ACTIVATE_LICENSE_PATH\" directory."
|
echo "Changing to \"$ACTIVATE_LICENSE_PATH\" directory."
|
||||||
pushd "$ACTIVATE_LICENSE_PATH"
|
pushd "$ACTIVATE_LICENSE_PATH"
|
||||||
|
|
||||||
if [[ -n "$UNITY_LICENSING_SERVER" ]]; then
|
/Applications/Unity/Hub/Editor/$UNITY_VERSION/Unity.app/Contents/MacOS/Unity \
|
||||||
#
|
-logFile /dev/stdout \
|
||||||
# Return any floating license used.
|
-batchmode \
|
||||||
#
|
-nographics \
|
||||||
echo "Returning floating license: \"$FLOATING_LICENSE\""
|
-quit \
|
||||||
/Applications/Unity/Hub/Editor/$UNITY_VERSION/Unity.app/Contents/Frameworks/UnityLicensingClient.app/Contents/MacOS/Unity.Licensing.Client \
|
-username "$UNITY_EMAIL" \
|
||||||
--return-floating "$FLOATING_LICENSE"
|
-password "$UNITY_PASSWORD" \
|
||||||
elif [[ -n "$UNITY_SERIAL" ]]; then
|
-returnlicense \
|
||||||
#
|
-projectPath "$ACTIVATE_LICENSE_PATH"
|
||||||
# SERIAL LICENSE MODE
|
|
||||||
#
|
|
||||||
# This will return the license that is currently in use.
|
|
||||||
#
|
|
||||||
/Applications/Unity/Hub/Editor/$UNITY_VERSION/Unity.app/Contents/MacOS/Unity \
|
|
||||||
-logFile - \
|
|
||||||
-batchmode \
|
|
||||||
-nographics \
|
|
||||||
-quit \
|
|
||||||
-username "$UNITY_EMAIL" \
|
|
||||||
-password "$UNITY_PASSWORD" \
|
|
||||||
-returnlicense \
|
|
||||||
-projectPath "$ACTIVATE_LICENSE_PATH"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Return to previous working directory
|
# Return to previous working directory
|
||||||
popd
|
popd
|
||||||
|
|||||||
104
dist/platforms/ubuntu/entrypoint.sh
vendored
104
dist/platforms/ubuntu/entrypoint.sh
vendored
@@ -1,83 +1,45 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
# Ensure machine ID is randomized for personal license activation
|
|
||||||
if [[ "$UNITY_SERIAL" = F* ]]; then
|
|
||||||
echo "Randomizing machine ID for personal license activation"
|
|
||||||
dbus-uuidgen > /etc/machine-id && mkdir -p /var/lib/dbus/ && ln -sf /etc/machine-id /var/lib/dbus/machine-id
|
|
||||||
fi
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Prepare Android SDK, if needed
|
# Create directory for license activation
|
||||||
# We do this here to ensure it has root permissions
|
|
||||||
#
|
#
|
||||||
|
|
||||||
fullProjectPath="$GITHUB_WORKSPACE/$PROJECT_PATH"
|
ACTIVATE_LICENSE_PATH="$GITHUB_WORKSPACE/_activate-license~"
|
||||||
|
mkdir -p "$ACTIVATE_LICENSE_PATH"
|
||||||
|
|
||||||
if [[ "$BUILD_TARGET" == "Android" ]]; then
|
#
|
||||||
export JAVA_HOME="$(awk -F'=' '/JAVA_HOME=/{print $2}' /usr/bin/unity-editor.d/*)"
|
# Run steps
|
||||||
ANDROID_HOME_DIRECTORY="$(awk -F'=' '/ANDROID_HOME=/{print $2}' /usr/bin/unity-editor.d/*)"
|
#
|
||||||
SDKMANAGER=$(find $ANDROID_HOME_DIRECTORY/cmdline-tools -name sdkmanager)
|
source /steps/set_gitcredential.sh
|
||||||
if [ -z "${SDKMANAGER}" ]
|
source /steps/activate.sh
|
||||||
then
|
source /steps/build.sh
|
||||||
SDKMANAGER=$(find $ANDROID_HOME_DIRECTORY/tools/bin -name sdkmanager)
|
source /steps/return_license.sh
|
||||||
if [ -z "${SDKMANAGER}" ]
|
|
||||||
then
|
|
||||||
echo "No sdkmanager found"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -n "$ANDROID_SDK_MANAGER_PARAMETERS" ]]; then
|
#
|
||||||
echo "Updating Android SDK with parameters: $ANDROID_SDK_MANAGER_PARAMETERS"
|
# Remove license activation directory
|
||||||
$SDKMANAGER "$ANDROID_SDK_MANAGER_PARAMETERS"
|
#
|
||||||
else
|
|
||||||
echo "Updating Android SDK with auto detected target API version"
|
|
||||||
# Read the line containing AndroidTargetSdkVersion from the file
|
|
||||||
targetAPILine=$(grep 'AndroidTargetSdkVersion' "$fullProjectPath/ProjectSettings/ProjectSettings.asset")
|
|
||||||
|
|
||||||
# Extract the number after the semicolon
|
rm -r "$ACTIVATE_LICENSE_PATH"
|
||||||
targetAPI=$(echo "$targetAPILine" | cut -d':' -f2 | tr -d '[:space:]')
|
|
||||||
|
|
||||||
$SDKMANAGER "platforms;android-$targetAPI"
|
#
|
||||||
fi
|
# Instructions for debugging
|
||||||
|
#
|
||||||
|
|
||||||
echo "Updated Android SDK."
|
if [[ $BUILD_EXIT_CODE -gt 0 ]]; then
|
||||||
else
|
echo ""
|
||||||
echo "Not updating Android SDK."
|
echo "###########################"
|
||||||
fi
|
echo "# Failure #"
|
||||||
|
echo "###########################"
|
||||||
|
echo ""
|
||||||
|
echo "Please note that the exit code is not very descriptive."
|
||||||
|
echo "Most likely it will not help you solve the issue."
|
||||||
|
echo ""
|
||||||
|
echo "To find the reason for failure: please search for errors in the log above."
|
||||||
|
echo ""
|
||||||
|
fi;
|
||||||
|
|
||||||
if [[ "$RUN_AS_HOST_USER" == "true" ]]; then
|
#
|
||||||
echo "Running as host user"
|
# Exit with code from the build step.
|
||||||
|
#
|
||||||
|
|
||||||
# Stop on error if we can't set up the user
|
exit $BUILD_EXIT_CODE
|
||||||
set -e
|
|
||||||
|
|
||||||
# Get host user/group info so we create files with the correct ownership
|
|
||||||
USERNAME=$(stat -c '%U' "$fullProjectPath")
|
|
||||||
USERID=$(stat -c '%u' "$fullProjectPath")
|
|
||||||
GROUPNAME=$(stat -c '%G' "$fullProjectPath")
|
|
||||||
GROUPID=$(stat -c '%g' "$fullProjectPath")
|
|
||||||
|
|
||||||
groupadd -g $GROUPID $GROUPNAME
|
|
||||||
useradd -u $USERID -g $GROUPID $USERNAME
|
|
||||||
usermod -aG $GROUPNAME $USERNAME
|
|
||||||
mkdir -p "/home/$USERNAME"
|
|
||||||
chown $USERNAME:$GROUPNAME "/home/$USERNAME"
|
|
||||||
|
|
||||||
# Normally need root permissions to access when using su
|
|
||||||
chmod 777 /dev/stdout
|
|
||||||
chmod 777 /dev/stderr
|
|
||||||
|
|
||||||
# Don't stop on error when running our scripts as error handling is baked in
|
|
||||||
set +e
|
|
||||||
|
|
||||||
# Switch to the host user so we can create files with the correct ownership
|
|
||||||
su $USERNAME -c "$SHELL -c 'source /steps/runsteps.sh'"
|
|
||||||
else
|
|
||||||
echo "Running as root"
|
|
||||||
|
|
||||||
# Run as root
|
|
||||||
source /steps/runsteps.sh
|
|
||||||
fi
|
|
||||||
|
|
||||||
exit $?
|
|
||||||
|
|||||||
130
dist/platforms/ubuntu/steps/activate.sh
vendored
130
dist/platforms/ubuntu/steps/activate.sh
vendored
@@ -1,65 +1,78 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
# if blankproject folder doesn't exist create it
|
# Run in ACTIVATE_LICENSE_PATH directory
|
||||||
if [ ! -d "/BlankProject" ]; then
|
echo "Changing to \"$ACTIVATE_LICENSE_PATH\" directory."
|
||||||
mkdir /BlankProject
|
pushd "$ACTIVATE_LICENSE_PATH"
|
||||||
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_LICENSE" ]] || [[ -n "$UNITY_LICENSE_FILE" ]]; then
|
||||||
#
|
#
|
||||||
# SERIAL LICENSE MODE
|
# PERSONAL LICENSE MODE
|
||||||
#
|
#
|
||||||
# This will activate unity, using the serial activation process.
|
# This will activate Unity, using a license file
|
||||||
#
|
#
|
||||||
echo "Requesting activation"
|
# Note that this is the ONLY WAY for PERSONAL LICENSES in 2020.
|
||||||
|
# * See for more details: https://gitlab.com/gableroux/unity3d-gitlab-ci-example/issues/5#note_72815478
|
||||||
|
#
|
||||||
|
# The license file can be acquired using `webbertakken/request-manual-activation-file` action.
|
||||||
|
echo "Requesting activation (personal license)"
|
||||||
|
|
||||||
# Loop the unity-editor call until the license is activated with exponential backoff and a maximum of 5 retries
|
# Set the license file path
|
||||||
retry_count=0
|
FILE_PATH=UnityLicenseFile.ulf
|
||||||
|
|
||||||
# Initialize delay to 15 seconds
|
if [[ -n "$UNITY_LICENSE" ]]; then
|
||||||
delay=15
|
# Copy license file from Github variables
|
||||||
|
echo "$UNITY_LICENSE" | tr -d '\r' > $FILE_PATH
|
||||||
|
elif [[ -n "$UNITY_LICENSE_FILE" ]]; then
|
||||||
|
# Copy license file from file system
|
||||||
|
cat "$UNITY_LICENSE_FILE" | tr -d '\r' > $FILE_PATH
|
||||||
|
fi
|
||||||
|
|
||||||
# Loop until UNITY_EXIT_CODE is 0 or retry count reaches 5
|
# Activate license
|
||||||
while [[ $retry_count -lt 5 ]]
|
ACTIVATION_OUTPUT=$(unity-editor \
|
||||||
do
|
|
||||||
# Activate license
|
|
||||||
unity-editor \
|
|
||||||
-logFile /dev/stdout \
|
-logFile /dev/stdout \
|
||||||
-quit \
|
-quit \
|
||||||
-serial "$UNITY_SERIAL" \
|
-manualLicenseFile $FILE_PATH)
|
||||||
-username "$UNITY_EMAIL" \
|
|
||||||
-password "$UNITY_PASSWORD" \
|
|
||||||
-projectPath "/BlankProject"
|
|
||||||
|
|
||||||
# Store the exit code from the verify command
|
# Store the exit code from the verify command
|
||||||
UNITY_EXIT_CODE=$?
|
UNITY_EXIT_CODE=$?
|
||||||
|
|
||||||
# Check if UNITY_EXIT_CODE is 0
|
# The exit code for personal activation is always 1;
|
||||||
if [[ $UNITY_EXIT_CODE -eq 0 ]]
|
# Determine whether activation was successful.
|
||||||
then
|
#
|
||||||
echo "Activation successful"
|
# Successful output should include the following:
|
||||||
break
|
#
|
||||||
else
|
# "LICENSE SYSTEM [2020120 18:51:20] Next license update check is after 2019-11-25T18:23:38"
|
||||||
# Increment retry count
|
#
|
||||||
((retry_count++))
|
ACTIVATION_SUCCESSFUL=$(echo $ACTIVATION_OUTPUT | grep 'Next license update check is after' | wc -l)
|
||||||
|
|
||||||
echo "::warning ::Activation failed, attempting retry #$retry_count"
|
# Set exit code to 0 if activation was successful
|
||||||
echo "Activation failed, retrying in $delay seconds..."
|
if [[ $ACTIVATION_SUCCESSFUL -eq 1 ]]; then
|
||||||
sleep $delay
|
UNITY_EXIT_CODE=0
|
||||||
|
fi;
|
||||||
|
|
||||||
# Double the delay for the next iteration
|
# Remove license file
|
||||||
delay=$((delay * 2))
|
rm -f $FILE_PATH
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
if [[ $retry_count -eq 5 ]]
|
elif [[ -n "$UNITY_SERIAL" && -n "$UNITY_EMAIL" && -n "$UNITY_PASSWORD" ]]; then
|
||||||
then
|
#
|
||||||
echo "Activation failed after 5 retries"
|
# PROFESSIONAL (SERIAL) LICENSE MODE
|
||||||
fi
|
#
|
||||||
|
# This will activate unity, using the activating process.
|
||||||
|
#
|
||||||
|
# Note: This is the preferred way for PROFESSIONAL LICENSES.
|
||||||
|
#
|
||||||
|
echo "Requesting activation (professional license)"
|
||||||
|
|
||||||
|
# Activate license
|
||||||
|
unity-editor \
|
||||||
|
-logFile /dev/stdout \
|
||||||
|
-quit \
|
||||||
|
-serial "$UNITY_SERIAL" \
|
||||||
|
-username "$UNITY_EMAIL" \
|
||||||
|
-password "$UNITY_PASSWORD"
|
||||||
|
|
||||||
|
# Store the exit code from the verify command
|
||||||
|
UNITY_EXIT_CODE=$?
|
||||||
|
|
||||||
elif [[ -n "$UNITY_LICENSING_SERVER" ]]; then
|
elif [[ -n "$UNITY_LICENSING_SERVER" ]]; then
|
||||||
#
|
#
|
||||||
@@ -68,18 +81,14 @@ elif [[ -n "$UNITY_LICENSING_SERVER" ]]; then
|
|||||||
echo "Adding licensing server config"
|
echo "Adding licensing server config"
|
||||||
|
|
||||||
/opt/unity/Editor/Data/Resources/Licensing/Client/Unity.Licensing.Client --acquire-floating > license.txt #is this accessible in a env variable?
|
/opt/unity/Editor/Data/Resources/Licensing/Client/Unity.Licensing.Client --acquire-floating > license.txt #is this accessible in a env variable?
|
||||||
|
PARSEDFILE=$(grep -oP '\".*?\"' < license.txt | tr -d '"')
|
||||||
|
export FLOATING_LICENSE
|
||||||
|
FLOATING_LICENSE=$(sed -n 2p <<< "$PARSEDFILE")
|
||||||
|
FLOATING_LICENSE_TIMEOUT=$(sed -n 4p <<< "$PARSEDFILE")
|
||||||
|
|
||||||
|
echo "Acquired floating license: \"$FLOATING_LICENSE\" with timeout $FLOATING_LICENSE_TIMEOUT"
|
||||||
# Store the exit code from the verify command
|
# Store the exit code from the verify command
|
||||||
UNITY_EXIT_CODE=$?
|
UNITY_EXIT_CODE=$?
|
||||||
|
|
||||||
if [ $UNITY_EXIT_CODE -eq 0 ]; then
|
|
||||||
PARSEDFILE=$(grep -oP '\".*?\"' < license.txt | tr -d '"')
|
|
||||||
export FLOATING_LICENSE
|
|
||||||
FLOATING_LICENSE=$(sed -n 2p <<< "$PARSEDFILE")
|
|
||||||
FLOATING_LICENSE_TIMEOUT=$(sed -n 4p <<< "$PARSEDFILE")
|
|
||||||
|
|
||||||
echo "Acquired floating license: \"$FLOATING_LICENSE\" with timeout $FLOATING_LICENSE_TIMEOUT"
|
|
||||||
fi
|
|
||||||
else
|
else
|
||||||
#
|
#
|
||||||
# NO LICENSE ACTIVATION STRATEGY MATCHED
|
# NO LICENSE ACTIVATION STRATEGY MATCHED
|
||||||
@@ -88,13 +97,10 @@ 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/activation for more"
|
echo "Visit https://game.ci/docs/github/getting-started 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 \
|
# Immediately exit as no UNITY_EXIT_CODE can be derrived.
|
||||||
or UNITY_LICENSE. Otherwise please use UNITY_LICENSING_SERVER. See more info at https://game.ci/docs/github/activation"
|
|
||||||
|
|
||||||
# Immediately exit as no UNITY_EXIT_CODE can be derived.
|
|
||||||
exit 1;
|
exit 1;
|
||||||
|
|
||||||
fi
|
fi
|
||||||
@@ -109,6 +115,8 @@ else
|
|||||||
# Activation failed so exit with the code from the license verification step
|
# Activation failed so exit with the code from the license verification step
|
||||||
echo "Unclassified error occured while trying to activate license."
|
echo "Unclassified error occured while trying to activate license."
|
||||||
echo "Exit code was: $UNITY_EXIT_CODE"
|
echo "Exit code was: $UNITY_EXIT_CODE"
|
||||||
echo "::error ::There was an error while trying to activate the Unity license."
|
|
||||||
exit $UNITY_EXIT_CODE
|
exit $UNITY_EXIT_CODE
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Return to previous working directory
|
||||||
|
popd
|
||||||
|
|||||||
38
dist/platforms/ubuntu/steps/build.sh
vendored
38
dist/platforms/ubuntu/steps/build.sh
vendored
@@ -19,22 +19,6 @@ echo "Using build name \"$BUILD_NAME\"."
|
|||||||
|
|
||||||
echo "Using build target \"$BUILD_TARGET\"."
|
echo "Using build target \"$BUILD_TARGET\"."
|
||||||
|
|
||||||
#
|
|
||||||
# Display the build profile
|
|
||||||
#
|
|
||||||
|
|
||||||
if [ -z "$BUILD_PROFILE" ]; then
|
|
||||||
# User has not provided a build profile
|
|
||||||
#
|
|
||||||
echo "Doing a default \"$BUILD_TARGET\" platform build."
|
|
||||||
#
|
|
||||||
else
|
|
||||||
# User has provided a path to a build profile `.asset` file
|
|
||||||
#
|
|
||||||
echo "Using build profile \"$BUILD_PROFILE\" relative to \"$UNITY_PROJECT_PATH\"."
|
|
||||||
#
|
|
||||||
fi
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Display build path and file
|
# Display build path and file
|
||||||
#
|
#
|
||||||
@@ -78,6 +62,19 @@ else
|
|||||||
#
|
#
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# Prepare Android SDK, if needed
|
||||||
|
#
|
||||||
|
|
||||||
|
if [[ "$BUILD_TARGET" == "Android" && -n "$ANDROID_SDK_MANAGER_PARAMETERS" ]]; then
|
||||||
|
echo "Updating Android SDK with parameters: $ANDROID_SDK_MANAGER_PARAMETERS"
|
||||||
|
export JAVA_HOME="$(awk -F'=' '/JAVA_HOME=/{print $2}' /usr/bin/unity-editor.d/*)"
|
||||||
|
"$(awk -F'=' '/ANDROID_HOME=/{print $2}' /usr/bin/unity-editor.d/*)/tools/bin/sdkmanager" "$ANDROID_SDK_MANAGER_PARAMETERS"
|
||||||
|
echo "Updated Android SDK."
|
||||||
|
else
|
||||||
|
echo "Not updating Android SDK."
|
||||||
|
fi
|
||||||
|
|
||||||
#
|
#
|
||||||
# Pre-build debug information
|
# Pre-build debug information
|
||||||
#
|
#
|
||||||
@@ -122,14 +119,12 @@ echo ""
|
|||||||
|
|
||||||
unity-editor \
|
unity-editor \
|
||||||
-logfile /dev/stdout \
|
-logfile /dev/stdout \
|
||||||
$( [ "${MANUAL_EXIT}" == "true" ] || echo "-quit" ) \
|
-quit \
|
||||||
-customBuildName "$BUILD_NAME" \
|
-customBuildName "$BUILD_NAME" \
|
||||||
-projectPath "$UNITY_PROJECT_PATH" \
|
-projectPath "$UNITY_PROJECT_PATH" \
|
||||||
$( [ -z "$BUILD_PROFILE" ] && echo "-buildTarget $BUILD_TARGET" ) \
|
-buildTarget "$BUILD_TARGET" \
|
||||||
-customBuildTarget "$BUILD_TARGET" \
|
-customBuildTarget "$BUILD_TARGET" \
|
||||||
-customBuildPath "$CUSTOM_BUILD_PATH" \
|
-customBuildPath "$CUSTOM_BUILD_PATH" \
|
||||||
-customBuildProfile "$BUILD_PROFILE" \
|
|
||||||
${BUILD_PROFILE:+-activeBuildProfile} ${BUILD_PROFILE:+"$BUILD_PROFILE"} \
|
|
||||||
-executeMethod "$BUILD_METHOD" \
|
-executeMethod "$BUILD_METHOD" \
|
||||||
-buildVersion "$VERSION" \
|
-buildVersion "$VERSION" \
|
||||||
-androidVersionCode "$ANDROID_VERSION_CODE" \
|
-androidVersionCode "$ANDROID_VERSION_CODE" \
|
||||||
@@ -138,8 +133,6 @@ unity-editor \
|
|||||||
-androidKeyaliasName "$ANDROID_KEYALIAS_NAME" \
|
-androidKeyaliasName "$ANDROID_KEYALIAS_NAME" \
|
||||||
-androidKeyaliasPass "$ANDROID_KEYALIAS_PASS" \
|
-androidKeyaliasPass "$ANDROID_KEYALIAS_PASS" \
|
||||||
-androidTargetSdkVersion "$ANDROID_TARGET_SDK_VERSION" \
|
-androidTargetSdkVersion "$ANDROID_TARGET_SDK_VERSION" \
|
||||||
-androidExportType "$ANDROID_EXPORT_TYPE" \
|
|
||||||
-androidSymbolType "$ANDROID_SYMBOL_TYPE" \
|
|
||||||
$CUSTOM_PARAMETERS
|
$CUSTOM_PARAMETERS
|
||||||
|
|
||||||
# Catch exit code
|
# Catch exit code
|
||||||
@@ -158,7 +151,6 @@ fi
|
|||||||
|
|
||||||
# Make a given user owner of all artifacts
|
# Make a given user owner of all artifacts
|
||||||
if [[ -n "$CHOWN_FILES_TO" ]]; then
|
if [[ -n "$CHOWN_FILES_TO" ]]; then
|
||||||
echo "Changing ownership of files to $CHOWN_FILES_TO for $BUILD_PATH_FULL and $UNITY_PROJECT_PATH"
|
|
||||||
chown -R "$CHOWN_FILES_TO" "$BUILD_PATH_FULL"
|
chown -R "$CHOWN_FILES_TO" "$BUILD_PATH_FULL"
|
||||||
chown -R "$CHOWN_FILES_TO" "$UNITY_PROJECT_PATH"
|
chown -R "$CHOWN_FILES_TO" "$UNITY_PROJECT_PATH"
|
||||||
fi
|
fi
|
||||||
|
|||||||
17
dist/platforms/ubuntu/steps/return_license.sh
vendored
17
dist/platforms/ubuntu/steps/return_license.sh
vendored
@@ -1,6 +1,11 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
if [[ -n "$UNITY_LICENSING_SERVER" ]]; then
|
# Run in ACTIVATE_LICENSE_PATH directory
|
||||||
|
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.
|
||||||
#
|
#
|
||||||
@@ -8,15 +13,15 @@ if [[ -n "$UNITY_LICENSING_SERVER" ]]; then
|
|||||||
/opt/unity/Editor/Data/Resources/Licensing/Client/Unity.Licensing.Client --return-floating "$FLOATING_LICENSE"
|
/opt/unity/Editor/Data/Resources/Licensing/Client/Unity.Licensing.Client --return-floating "$FLOATING_LICENSE"
|
||||||
elif [[ -n "$UNITY_SERIAL" ]]; then
|
elif [[ -n "$UNITY_SERIAL" ]]; then
|
||||||
#
|
#
|
||||||
# SERIAL LICENSE MODE
|
# PROFESSIONAL (SERIAL) LICENSE MODE
|
||||||
#
|
#
|
||||||
# This will return the license that is currently in use.
|
# This will return the license that is currently in use.
|
||||||
#
|
#
|
||||||
unity-editor \
|
unity-editor \
|
||||||
-logFile /dev/stdout \
|
-logFile /dev/stdout \
|
||||||
-quit \
|
-quit \
|
||||||
-returnlicense \
|
-returnlicense
|
||||||
-username "$UNITY_EMAIL" \
|
|
||||||
-password "$UNITY_PASSWORD" \
|
|
||||||
-projectPath "/BlankProject"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Return to previous working directory
|
||||||
|
popd
|
||||||
|
|||||||
48
dist/platforms/ubuntu/steps/runsteps.sh
vendored
48
dist/platforms/ubuntu/steps/runsteps.sh
vendored
@@ -1,48 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
#
|
|
||||||
# Run steps
|
|
||||||
#
|
|
||||||
source /steps/set_extra_git_configs.sh
|
|
||||||
source /steps/set_gitcredential.sh
|
|
||||||
|
|
||||||
if [ "$SKIP_ACTIVATION" != "true" ]; then
|
|
||||||
source /steps/activate.sh
|
|
||||||
|
|
||||||
# If we didn't activate successfully, exit with the exit code from the activation step.
|
|
||||||
if [[ $UNITY_EXIT_CODE -ne 0 ]]; then
|
|
||||||
exit $UNITY_EXIT_CODE
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "Skipping activation"
|
|
||||||
fi
|
|
||||||
|
|
||||||
source /steps/build.sh
|
|
||||||
|
|
||||||
if [ "$SKIP_ACTIVATION" != "true" ]; then
|
|
||||||
source /steps/return_license.sh
|
|
||||||
fi
|
|
||||||
|
|
||||||
#
|
|
||||||
# Instructions for debugging
|
|
||||||
#
|
|
||||||
|
|
||||||
if [[ $BUILD_EXIT_CODE -gt 0 ]]; then
|
|
||||||
echo ""
|
|
||||||
echo "###########################"
|
|
||||||
echo "# Failure #"
|
|
||||||
echo "###########################"
|
|
||||||
echo ""
|
|
||||||
echo "Please note that the exit code is not very descriptive."
|
|
||||||
echo "Most likely it will not help you solve the issue."
|
|
||||||
echo ""
|
|
||||||
echo "To find the reason for failure: please search for errors in the log above and check for annotations in the summary view."
|
|
||||||
echo ""
|
|
||||||
fi;
|
|
||||||
|
|
||||||
#
|
|
||||||
# Exit with code from the build step.
|
|
||||||
#
|
|
||||||
|
|
||||||
# Exiting su
|
|
||||||
exit $BUILD_EXIT_CODE
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
if [ -z "${GIT_CONFIG_EXTENSIONS}" ]
|
|
||||||
then
|
|
||||||
echo "GIT_CONFIG_EXTENSIONS unset skipping"
|
|
||||||
else
|
|
||||||
echo "GIT_CONFIG_EXTENSIONS is set configuring extra git configs"
|
|
||||||
|
|
||||||
IFS=$'\n'
|
|
||||||
for config in $(echo "${GIT_CONFIG_EXTENSIONS}" | sed 's/\(.*\)=\(.*\)/"\1" "\2"/g'); do
|
|
||||||
if [[ $config =~ \"([^\"]+)\"\ \"([^\"]+)\" ]]; then
|
|
||||||
key="${BASH_REMATCH[1]}"
|
|
||||||
value="${BASH_REMATCH[2]}"
|
|
||||||
else
|
|
||||||
echo "Error parsing config: $config"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "Adding extra git config: \"$key\" = \"$value\""
|
|
||||||
git config --global --add "$key" "$value"
|
|
||||||
done
|
|
||||||
unset IFS
|
|
||||||
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "---------- git config --list -------------"
|
|
||||||
git config --list
|
|
||||||
|
|
||||||
echo "---------- git config --list --show-origin -------------"
|
|
||||||
git config --list --show-origin
|
|
||||||
@@ -9,8 +9,8 @@ else
|
|||||||
git config --global credential.helper store
|
git config --global credential.helper store
|
||||||
git config --global --replace-all url."https://token:$GIT_PRIVATE_TOKEN@github.com/".insteadOf ssh://git@github.com/
|
git config --global --replace-all url."https://token:$GIT_PRIVATE_TOKEN@github.com/".insteadOf ssh://git@github.com/
|
||||||
git config --global --add url."https://token:$GIT_PRIVATE_TOKEN@github.com/".insteadOf git@github.com
|
git config --global --add url."https://token:$GIT_PRIVATE_TOKEN@github.com/".insteadOf git@github.com
|
||||||
|
|
||||||
git config --global --add url."https://token:$GIT_PRIVATE_TOKEN@github.com/".insteadOf "https://github.com/"
|
git config --global --add url."https://token:$GIT_PRIVATE_TOKEN@github.com/".insteadOf "https://github.com/"
|
||||||
|
|
||||||
git config --global url."https://ssh:$GIT_PRIVATE_TOKEN@github.com/".insteadOf "ssh://git@github.com/"
|
git config --global url."https://ssh:$GIT_PRIVATE_TOKEN@github.com/".insteadOf "ssh://git@github.com/"
|
||||||
git config --global url."https://git:$GIT_PRIVATE_TOKEN@github.com/".insteadOf "git@github.com:"
|
git config --global url."https://git:$GIT_PRIVATE_TOKEN@github.com/".insteadOf "git@github.com:"
|
||||||
|
|
||||||
|
|||||||
98
dist/platforms/windows/activate.ps1
vendored
98
dist/platforms/windows/activate.ps1
vendored
@@ -1,93 +1,7 @@
|
|||||||
# Activates Unity
|
# Activates Unity
|
||||||
|
& "C:\Program Files\Unity\Hub\Editor\$Env:UNITY_VERSION\Editor\Unity.exe" -batchmode -quit -nographics `
|
||||||
Write-Output ""
|
-username $Env:UNITY_EMAIL `
|
||||||
Write-Output "###########################"
|
-password $Env:UNITY_PASSWORD `
|
||||||
Write-Output "# Activating #"
|
-serial $Env:UNITY_SERIAL `
|
||||||
Write-Output "###########################"
|
-projectPath "c:/BlankProject" `
|
||||||
Write-Output ""
|
-logfile | Out-Host
|
||||||
|
|
||||||
if ( ($null -ne ${env:UNITY_SERIAL}) -and ($null -ne ${env:UNITY_EMAIL}) -and ($null -ne ${env:UNITY_PASSWORD}) )
|
|
||||||
{
|
|
||||||
#
|
|
||||||
# SERIAL LICENSE MODE
|
|
||||||
#
|
|
||||||
# This will activate unity, using the serial activation process.
|
|
||||||
#
|
|
||||||
Write-Output "Requesting activation"
|
|
||||||
|
|
||||||
$ACTIVATION_OUTPUT = Start-Process -FilePath "$Env:UNITY_PATH/Editor/Unity.exe" `
|
|
||||||
-NoNewWindow `
|
|
||||||
-PassThru `
|
|
||||||
-ArgumentList "-batchmode `
|
|
||||||
-quit `
|
|
||||||
-nographics `
|
|
||||||
-username $Env:UNITY_EMAIL `
|
|
||||||
-password $Env:UNITY_PASSWORD `
|
|
||||||
-serial $Env:UNITY_SERIAL `
|
|
||||||
-projectPath c:/BlankProject `
|
|
||||||
-logfile -"
|
|
||||||
|
|
||||||
# Cache the handle so exit code works properly
|
|
||||||
# https://stackoverflow.com/questions/10262231/obtaining-exitcode-using-start-process-and-waitforexit-instead-of-wait
|
|
||||||
$unityHandle = $ACTIVATION_OUTPUT.Handle
|
|
||||||
|
|
||||||
while ($true) {
|
|
||||||
if ($ACTIVATION_OUTPUT.HasExited) {
|
|
||||||
$ACTIVATION_EXIT_CODE = $ACTIVATION_OUTPUT.ExitCode
|
|
||||||
|
|
||||||
# Display results
|
|
||||||
if ($ACTIVATION_EXIT_CODE -eq 0)
|
|
||||||
{
|
|
||||||
Write-Output "Activation Succeeded"
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
Write-Output "Activation failed, with exit code $ACTIVATION_EXIT_CODE"
|
|
||||||
}
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
Start-Sleep -Seconds 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
elseif( ($null -ne ${env:UNITY_LICENSING_SERVER}))
|
|
||||||
{
|
|
||||||
#
|
|
||||||
# Custom Unity License Server
|
|
||||||
#
|
|
||||||
|
|
||||||
Write-Output "Adding licensing server config"
|
|
||||||
|
|
||||||
$ACTIVATION_OUTPUT = Start-Process -FilePath "$Env:UNITY_PATH\Editor\Data\Resources\Licensing\Client\Unity.Licensing.Client.exe" `
|
|
||||||
-ArgumentList "--acquire-floating" `
|
|
||||||
-NoNewWindow `
|
|
||||||
-PassThru `
|
|
||||||
-Wait `
|
|
||||||
-RedirectStandardOutput "license.txt"
|
|
||||||
|
|
||||||
$PARSEDFILE = (Get-Content "license.txt" | Select-String -AllMatches -Pattern '\".*?\"' | ForEach-Object { $_.Matches.Value }) -replace '"'
|
|
||||||
|
|
||||||
$env:FLOATING_LICENSE = $PARSEDFILE[1]
|
|
||||||
$FLOATING_LICENSE_TIMEOUT = $PARSEDFILE[3]
|
|
||||||
|
|
||||||
Write-Output "Acquired floating license: ""$env:FLOATING_LICENSE"" with timeout $FLOATING_LICENSE_TIMEOUT"
|
|
||||||
# Store the exit code from the verify command
|
|
||||||
$ACTIVATION_EXIT_CODE = $ACTIVATION_OUTPUT.ExitCode
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
#
|
|
||||||
# NO LICENSE ACTIVATION STRATEGY MATCHED
|
|
||||||
#
|
|
||||||
# This will exit since no activation strategies could be matched.
|
|
||||||
#
|
|
||||||
Write-Output "License activation strategy could not be determined."
|
|
||||||
Write-Output ""
|
|
||||||
Write-Output "Visit https://game.ci/docs/github/activation for more"
|
|
||||||
Write-Output "details on how to set up one of the possible activation strategies."
|
|
||||||
|
|
||||||
Write-Output "::error ::No valid license activation strategy could be determined. Make sure to provide UNITY_EMAIL, UNITY_PASSWORD, and either a UNITY_SERIAL \
|
|
||||||
or UNITY_LICENSE. See more info at https://game.ci/docs/github/activation"
|
|
||||||
|
|
||||||
$ACTIVATION_EXIT_CODE = 1;
|
|
||||||
}
|
|
||||||
|
|||||||
148
dist/platforms/windows/build.ps1
vendored
148
dist/platforms/windows/build.ps1
vendored
@@ -16,25 +16,6 @@ Write-Output "$('Using build name "')$($Env:BUILD_NAME)$('".')"
|
|||||||
|
|
||||||
Write-Output "$('Using build target "')$($Env:BUILD_TARGET)$('".')"
|
Write-Output "$('Using build target "')$($Env:BUILD_TARGET)$('".')"
|
||||||
|
|
||||||
#
|
|
||||||
# Display the build profile
|
|
||||||
#
|
|
||||||
|
|
||||||
if ($Env:BUILD_PROFILE)
|
|
||||||
{
|
|
||||||
# User has provided a path to a build profile `.asset` file
|
|
||||||
#
|
|
||||||
Write-Output "$('Using build profile "')$($Env:BUILD_PROFILE)$('" relative to "')$($Env:UNITY_PROJECT_PATH)$('".')"
|
|
||||||
#
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
# User has not provided a build profile
|
|
||||||
#
|
|
||||||
Write-Output "$('Doing a default "')$($Env:BUILD_TARGET)$('" platform build.')"
|
|
||||||
#
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Display build path and file
|
# Display build path and file
|
||||||
#
|
#
|
||||||
@@ -85,26 +66,6 @@ else
|
|||||||
Get-ChildItem -Path $Env:UNITY_PROJECT_PATH\Assets\Editor -Recurse
|
Get-ChildItem -Path $Env:UNITY_PROJECT_PATH\Assets\Editor -Recurse
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( "$Env:BUILD_TARGET" -eq "Android" -and -not ([string]::IsNullOrEmpty("$Env:ANDROID_KEYSTORE_BASE64")) )
|
|
||||||
{
|
|
||||||
Write-Output "Creating Android keystore."
|
|
||||||
|
|
||||||
# Write to consistent location as Windows Unity seems to have issues with pwd and can't find the keystore
|
|
||||||
$keystorePath = "C:/android.keystore"
|
|
||||||
[System.IO.File]::WriteAllBytes($keystorePath, [System.Convert]::FromBase64String($Env:ANDROID_KEYSTORE_BASE64))
|
|
||||||
|
|
||||||
# Ensure the project settings are pointed at the correct path
|
|
||||||
$unitySettingsPath = "$Env:UNITY_PROJECT_PATH\ProjectSettings\ProjectSettings.asset"
|
|
||||||
$fileContent = Get-Content -Path "$unitySettingsPath"
|
|
||||||
$fileContent = $fileContent -replace "AndroidKeystoreName:\s+.*", "AndroidKeystoreName: $keystorePath"
|
|
||||||
$fileContent | Set-Content -Path "$unitySettingsPath"
|
|
||||||
|
|
||||||
Write-Output "Created Android keystore."
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Write-Output "Not creating Android keystore."
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Pre-build debug information
|
# Pre-build debug information
|
||||||
#
|
#
|
||||||
@@ -148,84 +109,49 @@ Write-Output "# Building project #"
|
|||||||
Write-Output "###########################"
|
Write-Output "###########################"
|
||||||
Write-Output ""
|
Write-Output ""
|
||||||
|
|
||||||
$unityGraphics = "-nographics"
|
|
||||||
|
|
||||||
if ($LLVMPIPE_INSTALLED -eq "true")
|
|
||||||
{
|
|
||||||
$unityGraphics = "-force-opengl"
|
|
||||||
}
|
|
||||||
|
|
||||||
# If $Env:CUSTOM_PARAMETERS contains spaces and is passed directly on the command line to Unity, powershell will wrap it
|
# If $Env:CUSTOM_PARAMETERS contains spaces and is passed directly on the command line to Unity, powershell will wrap it
|
||||||
# in double quotes. To avoid this, parse $Env:CUSTOM_PARAMETERS into an array, while respecting any quotations within the string.
|
# in double quotes. To avoid this, parse $Env:CUSTOM_PARAMETERS into an array, while respecting any quotations within the string.
|
||||||
$_, $customParametersArray = Invoke-Expression('Write-Output -- "" ' + $Env:CUSTOM_PARAMETERS)
|
$_, $customParametersArray = Invoke-Expression('Write-Output -- "" ' + $Env:CUSTOM_PARAMETERS)
|
||||||
$unityArgs = @(
|
|
||||||
"-quit",
|
|
||||||
"-batchmode",
|
|
||||||
$unityGraphics,
|
|
||||||
"-silent-crashes",
|
|
||||||
"-customBuildName", "`"$Env:BUILD_NAME`"",
|
|
||||||
"-projectPath", "`"$Env:UNITY_PROJECT_PATH`"",
|
|
||||||
"-executeMethod", "`"$Env:BUILD_METHOD`"",
|
|
||||||
"-customBuildTarget", "`"$Env:BUILD_TARGET`"",
|
|
||||||
"-customBuildPath", "`"$Env:CUSTOM_BUILD_PATH`"",
|
|
||||||
"-customBuildProfile", "`"$Env:BUILD_PROFILE`"",
|
|
||||||
"-buildVersion", "`"$Env:VERSION`"",
|
|
||||||
"-androidVersionCode", "`"$Env:ANDROID_VERSION_CODE`"",
|
|
||||||
"-androidKeystorePass", "`"$Env:ANDROID_KEYSTORE_PASS`"",
|
|
||||||
"-androidKeyaliasName", "`"$Env:ANDROID_KEYALIAS_NAME`"",
|
|
||||||
"-androidKeyaliasPass", "`"$Env:ANDROID_KEYALIAS_PASS`"",
|
|
||||||
"-androidTargetSdkVersion", "`"$Env:ANDROID_TARGET_SDK_VERSION`"",
|
|
||||||
"-androidExportType", "`"$Env:ANDROID_EXPORT_TYPE`"",
|
|
||||||
"-androidSymbolType", "`"$Env:ANDROID_SYMBOL_TYPE`"",
|
|
||||||
"-logfile", "-"
|
|
||||||
) + $customParametersArray
|
|
||||||
|
|
||||||
if (-not $Env:BUILD_PROFILE) {
|
& "C:\Program Files\Unity\Hub\Editor\$Env:UNITY_VERSION\Editor\Unity.exe" -quit -batchmode -nographics `
|
||||||
$unityArgs += @("-buildTarget", "`"$Env:BUILD_TARGET`"")
|
-projectPath $Env:UNITY_PROJECT_PATH `
|
||||||
}
|
-executeMethod $Env:BUILD_METHOD `
|
||||||
if ($Env:BUILD_PROFILE) {
|
-buildTarget $Env:BUILD_TARGET `
|
||||||
$unityArgs += @("-activeBuildProfile", "`"$Env:BUILD_PROFILE`"")
|
-customBuildTarget $Env:BUILD_TARGET `
|
||||||
|
-customBuildPath $Env:CUSTOM_BUILD_PATH `
|
||||||
|
-buildVersion $Env:VERSION `
|
||||||
|
-androidVersionCode $Env:ANDROID_VERSION_CODE `
|
||||||
|
-androidKeystoreName $Env:ANDROID_KEYSTORE_NAME `
|
||||||
|
-androidKeystorePass $Env:ANDROID_KEYSTORE_PASS `
|
||||||
|
-androidKeyaliasName $Env:ANDROID_KEYALIAS_NAME `
|
||||||
|
-androidKeyaliasPass $Env:ANDROID_KEYALIAS_PASS `
|
||||||
|
-androidTargetSdkVersion $Env:ANDROID_TARGET_SDK_VERSION `
|
||||||
|
$customParametersArray `
|
||||||
|
-logfile | Out-Host
|
||||||
|
|
||||||
|
# Catch exit code
|
||||||
|
$Env:BUILD_EXIT_CODE=$?
|
||||||
|
|
||||||
|
# Display results
|
||||||
|
if ($Env:BUILD_EXIT_CODE -eq 0)
|
||||||
|
{
|
||||||
|
Write-Output "Build Succeeded!"
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
Write-Output "$('Build failed, with exit code ')$($Env:BUILD_EXIT_CODE)$('"')"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Remove null items as that will fail the Start-Process call
|
# TODO: Determine if we need to set permissions on any files
|
||||||
$unityArgs = $unityArgs | Where-Object { $_ -ne $null }
|
|
||||||
|
|
||||||
$unityProcess = Start-Process -FilePath "$Env:UNITY_PATH/Editor/Unity.exe" `
|
#
|
||||||
-ArgumentList $unityArgs `
|
# Results
|
||||||
-PassThru `
|
#
|
||||||
-NoNewWindow
|
|
||||||
|
|
||||||
# Cache the handle so exit code works properly
|
Write-Output ""
|
||||||
# https://stackoverflow.com/questions/10262231/obtaining-exitcode-using-start-process-and-waitforexit-instead-of-wait
|
Write-Output "###########################"
|
||||||
$unityHandle = $unityProcess.Handle
|
Write-Output "# Build output #"
|
||||||
|
Write-Output "###########################"
|
||||||
|
Write-Output ""
|
||||||
|
|
||||||
while ($true) {
|
Get-ChildItem $Env:BUILD_PATH_FULL
|
||||||
if ($unityProcess.HasExited) {
|
Write-Output ""
|
||||||
Start-Sleep -Seconds 3
|
|
||||||
Get-Process
|
|
||||||
|
|
||||||
$BUILD_EXIT_CODE = $unityProcess.ExitCode
|
|
||||||
|
|
||||||
# Display results
|
|
||||||
if ($BUILD_EXIT_CODE -eq 0)
|
|
||||||
{
|
|
||||||
Write-Output "Build Succeeded!!"
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
Write-Output "Build failed, with exit code $BUILD_EXIT_CODE"
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Output ""
|
|
||||||
Write-Output "###########################"
|
|
||||||
Write-Output "# Build output #"
|
|
||||||
Write-Output "###########################"
|
|
||||||
Write-Output ""
|
|
||||||
|
|
||||||
Get-ChildItem $Env:BUILD_PATH_FULL
|
|
||||||
Write-Output ""
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
Start-Sleep -Seconds 3
|
|
||||||
}
|
|
||||||
|
|||||||
50
dist/platforms/windows/entrypoint.ps1
vendored
50
dist/platforms/windows/entrypoint.ps1
vendored
@@ -1,55 +1,15 @@
|
|||||||
Get-Process
|
# Activate Unity
|
||||||
|
& "c:\steps\activate.ps1"
|
||||||
# Copy .upmconfig.toml if it exists
|
|
||||||
if (Test-Path "C:\githubhome\.upmconfig.toml") {
|
|
||||||
Write-Host "Copying .upmconfig.toml to $Env:USERPROFILE\.upmconfig.toml"
|
|
||||||
Copy-Item -Path "C:\githubhome\.upmconfig.toml" -Destination "$Env:USERPROFILE\.upmconfig.toml" -Force
|
|
||||||
} else {
|
|
||||||
Write-Host "No .upmconfig.toml found at C:\githubhome"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Import any necessary registry keys, ie: location of windows 10 sdk
|
# 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 {reg import $_.fullname}
|
||||||
|
|
||||||
# Register the Visual Studio installation so Unity can find it
|
# Register the Visual Studio installation so Unity can find it
|
||||||
regsvr32 C:\ProgramData\Microsoft\VisualStudio\Setup\x64\Microsoft.VisualStudio.Setup.Configuration.Native.dll
|
regsvr32 C:\ProgramData\Microsoft\VisualStudio\Setup\x64\Microsoft.VisualStudio.Setup.Configuration.Native.dll
|
||||||
|
|
||||||
# Kill the regsvr process
|
|
||||||
Get-Process -Name regsvr32 | ForEach-Object { Stop-Process -Id $_.Id -Force }
|
|
||||||
|
|
||||||
# Install Visual C++ 2013 Redistributables
|
|
||||||
. "c:\steps\install_vcredist13.ps1"
|
|
||||||
|
|
||||||
# Setup Git Credentials
|
|
||||||
. "c:\steps\set_gitcredential.ps1"
|
|
||||||
|
|
||||||
if ($env:ENABLE_GPU -eq "true") {
|
|
||||||
# Install LLVMpipe software graphics driver
|
|
||||||
. "c:\steps\install_llvmpipe.ps1"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Activate Unity
|
|
||||||
if ($env:SKIP_ACTIVATION -ne "true") {
|
|
||||||
. "c:\steps\activate.ps1"
|
|
||||||
|
|
||||||
# If we didn't activate successfully, exit with the exit code from the activation step.
|
|
||||||
if ($ACTIVATION_EXIT_CODE -ne 0) {
|
|
||||||
exit $ACTIVATION_EXIT_CODE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Write-Host "Skipping activation"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Build the project
|
# 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
|
||||||
if ($env:SKIP_ACTIVATION -ne "true") {
|
& "c:\steps\return_license.ps1"
|
||||||
. "c:\steps\return_license.ps1"
|
|
||||||
}
|
|
||||||
|
|
||||||
Get-Process
|
|
||||||
|
|
||||||
exit $BUILD_EXIT_CODE
|
|
||||||
|
|||||||
56
dist/platforms/windows/install_llvmpipe.ps1
vendored
56
dist/platforms/windows/install_llvmpipe.ps1
vendored
@@ -1,56 +0,0 @@
|
|||||||
$Private:repo = "mmozeiko/build-mesa"
|
|
||||||
$Private:downloadPath = "$Env:TEMP\mesa.zip"
|
|
||||||
$Private:extractPath = "$Env:TEMP\mesa"
|
|
||||||
$Private:destinationPath = "$Env:UNITY_PATH\Editor\"
|
|
||||||
$Private:version = "25.1.0"
|
|
||||||
|
|
||||||
$LLVMPIPE_INSTALLED = "false"
|
|
||||||
|
|
||||||
try {
|
|
||||||
# Get the release info from GitHub API (version fixed to decrease probability of breakage)
|
|
||||||
$releaseUrl = "https://api.github.com/repos/$repo/releases/tags/$version"
|
|
||||||
$release = Invoke-RestMethod -Uri $releaseUrl -Headers @{ "User-Agent" = "PowerShell" }
|
|
||||||
|
|
||||||
# Get the download URL for the zip asset
|
|
||||||
$zipUrl = $release.assets | Where-Object { $_.name -like "mesa-llvmpipe-x64*.zip" } | Select-Object -First 1 -ExpandProperty browser_download_url
|
|
||||||
|
|
||||||
if (-not $zipUrl) {
|
|
||||||
throw "No zip file found in the latest release."
|
|
||||||
}
|
|
||||||
|
|
||||||
# Download the zip file
|
|
||||||
Write-Host "Downloading $zipUrl..."
|
|
||||||
Invoke-WebRequest -Uri $zipUrl -OutFile $downloadPath
|
|
||||||
|
|
||||||
# Create extraction directory if it doesn't exist
|
|
||||||
if (-not (Test-Path $extractPath)) {
|
|
||||||
New-Item -ItemType Directory -Path $extractPath | Out-Null
|
|
||||||
}
|
|
||||||
|
|
||||||
# Extract the zip file
|
|
||||||
Write-Host "Extracting $downloadPath to $extractPath..."
|
|
||||||
Expand-Archive -Path $downloadPath -DestinationPath $extractPath -Force
|
|
||||||
|
|
||||||
# Create destination directory if it doesn't exist
|
|
||||||
if (-not (Test-Path $destinationPath)) {
|
|
||||||
New-Item -ItemType Directory -Path $destinationPath | Out-Null
|
|
||||||
}
|
|
||||||
|
|
||||||
# Copy extracted files to destination
|
|
||||||
Write-Host "Copying files to $destinationPath..."
|
|
||||||
Copy-Item -Path "$extractPath\*" -Destination $destinationPath -Recurse -Force
|
|
||||||
|
|
||||||
Write-Host "Successfully downloaded, extracted, and copied Mesa files to $destinationPath"
|
|
||||||
|
|
||||||
$LLVMPIPE_INSTALLED = "true"
|
|
||||||
} catch {
|
|
||||||
Write-Error "An error occurred: $_"
|
|
||||||
} finally {
|
|
||||||
# Clean up temporary files
|
|
||||||
if (Test-Path $downloadPath) {
|
|
||||||
Remove-Item $downloadPath -Force
|
|
||||||
}
|
|
||||||
if (Test-Path $extractPath) {
|
|
||||||
Remove-Item $extractPath -Recurse -Force
|
|
||||||
}
|
|
||||||
}
|
|
||||||
11
dist/platforms/windows/install_vcredist13.ps1
vendored
11
dist/platforms/windows/install_vcredist13.ps1
vendored
@@ -1,11 +0,0 @@
|
|||||||
# For some reason, Unity is failing in github actions windows runners
|
|
||||||
# due to missing Visual C++ 2013 redistributables.
|
|
||||||
# This script downloads and installs the required redistributables.
|
|
||||||
Write-Output ""
|
|
||||||
Write-Output "#########################################################"
|
|
||||||
Write-Output "# Installing Visual C++ Redistributables (2013) #"
|
|
||||||
Write-Output "#########################################################"
|
|
||||||
Write-Output ""
|
|
||||||
|
|
||||||
|
|
||||||
choco install vcredist2013 -y --no-progress
|
|
||||||
66
dist/platforms/windows/return_license.ps1
vendored
66
dist/platforms/windows/return_license.ps1
vendored
@@ -1,61 +1,7 @@
|
|||||||
# Return the active Unity license
|
# Return the active Unity license
|
||||||
|
& "C:\Program Files\Unity\Hub\Editor\$Env:UNITY_VERSION\Editor\Unity.exe" -batchmode -quit -nographics `
|
||||||
Write-Output ""
|
-username $Env:UNITY_EMAIL `
|
||||||
Write-Output "###########################"
|
-password $Env:UNITY_PASSWORD `
|
||||||
Write-Output "# Return License #"
|
-returnlicense `
|
||||||
Write-Output "###########################"
|
-projectPath "c:/BlankProject" `
|
||||||
Write-Output ""
|
-logfile | Out-Host
|
||||||
|
|
||||||
if (($null -ne ${env:UNITY_LICENSING_SERVER}))
|
|
||||||
{
|
|
||||||
Write-Output "Returning floating license: ""$env:FLOATING_LICENSE"""
|
|
||||||
Start-Process -FilePath "$Env:UNITY_PATH\Editor\Data\Resources\Licensing\Client\Unity.Licensing.Client.exe" `
|
|
||||||
-ArgumentList "--return-floating ""$env:FLOATING_LICENSE"" " `
|
|
||||||
-NoNewWindow `
|
|
||||||
-Wait
|
|
||||||
}
|
|
||||||
|
|
||||||
elseif (($null -ne ${env:UNITY_SERIAL}) -and ($null -ne ${env:UNITY_EMAIL}) -and ($null -ne ${env:UNITY_PASSWORD}))
|
|
||||||
{
|
|
||||||
#
|
|
||||||
# SERIAL LICENSE MODE
|
|
||||||
#
|
|
||||||
# This will return the license that is currently in use.
|
|
||||||
#
|
|
||||||
$RETURN_LICENSE_OUTPUT = Start-Process -FilePath "$Env:UNITY_PATH/Editor/Unity.exe" `
|
|
||||||
-NoNewWindow `
|
|
||||||
-PassThru `
|
|
||||||
-ArgumentList "-batchmode `
|
|
||||||
-quit `
|
|
||||||
-nographics `
|
|
||||||
-username $Env:UNITY_EMAIL `
|
|
||||||
-password $Env:UNITY_PASSWORD `
|
|
||||||
-returnlicense `
|
|
||||||
-projectPath c:/BlankProject `
|
|
||||||
-logfile -"
|
|
||||||
|
|
||||||
# Cache the handle so exit code works properly
|
|
||||||
# https://stackoverflow.com/questions/10262231/obtaining-exitcode-using-start-process-and-waitforexit-instead-of-wait
|
|
||||||
$unityHandle = $RETURN_LICENSE_OUTPUT.Handle
|
|
||||||
|
|
||||||
while ($true) {
|
|
||||||
if ($RETURN_LICENSE_OUTPUT.HasExited) {
|
|
||||||
$RETURN_LICENSE_EXIT_CODE = $RETURN_LICENSE_OUTPUT.ExitCode
|
|
||||||
|
|
||||||
# Display results
|
|
||||||
if ($RETURN_LICENSE_EXIT_CODE -eq 0)
|
|
||||||
{
|
|
||||||
Write-Output "License Return Succeeded"
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
Write-Output "License Return failed, with exit code $RETURN_LICENSE_EXIT_CODE"
|
|
||||||
Write-Output "::warning ::License Return failed! If this is a Pro License you might need to manually `
|
|
||||||
free the seat in your Unity admin panel or you might run out of seats to activate with."
|
|
||||||
}
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
Start-Sleep -Seconds 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
20
dist/platforms/windows/set_gitcredential.ps1
vendored
20
dist/platforms/windows/set_gitcredential.ps1
vendored
@@ -1,20 +0,0 @@
|
|||||||
if ($null -eq ${env:GIT_PRIVATE_TOKEN}) {
|
|
||||||
Write-Host "GIT_PRIVATE_TOKEN unset skipping"
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Write-Host "GIT_PRIVATE_TOKEN is set configuring git credentials"
|
|
||||||
|
|
||||||
git config --global credential.helper store
|
|
||||||
git config --global --replace-all url."https://token:$env:GIT_PRIVATE_TOKEN@github.com/".insteadOf "ssh://git@github.com/"
|
|
||||||
git config --global --add url."https://token:$env:GIT_PRIVATE_TOKEN@github.com/".insteadOf "git@github.com"
|
|
||||||
git config --global --add url."https://token:$env:GIT_PRIVATE_TOKEN@github.com/".insteadOf "https://github.com/"
|
|
||||||
|
|
||||||
git config --global url."https://ssh:$env:GIT_PRIVATE_TOKEN@github.com/".insteadOf "ssh://git@github.com/"
|
|
||||||
git config --global url."https://git:$env:GIT_PRIVATE_TOKEN@github.com/".insteadOf "git@github.com:"
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "---------- git config --list -------------"
|
|
||||||
git config --list
|
|
||||||
|
|
||||||
Write-Host "---------- git config --list --show-origin -------------"
|
|
||||||
git config --list --show-origin
|
|
||||||
2
dist/sourcemap-register.js
generated
vendored
2
dist/sourcemap-register.js
generated
vendored
File diff suppressed because one or more lines are too long
60
dist/xhr-sync-worker.js
vendored
Normal file
60
dist/xhr-sync-worker.js
vendored
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
"use strict";
|
||||||
|
/* eslint-disable no-process-exit */
|
||||||
|
const util = require("util");
|
||||||
|
const { JSDOM } = require("../../../..");
|
||||||
|
const { READY_STATES } = require("./xhr-utils");
|
||||||
|
const idlUtils = require("../generated/utils");
|
||||||
|
const tough = require("tough-cookie");
|
||||||
|
|
||||||
|
const dom = new JSDOM();
|
||||||
|
const xhr = new dom.window.XMLHttpRequest();
|
||||||
|
const xhrImpl = idlUtils.implForWrapper(xhr);
|
||||||
|
|
||||||
|
const chunks = [];
|
||||||
|
|
||||||
|
process.stdin.on("data", chunk => {
|
||||||
|
chunks.push(chunk);
|
||||||
|
});
|
||||||
|
|
||||||
|
process.stdin.on("end", () => {
|
||||||
|
const buffer = Buffer.concat(chunks);
|
||||||
|
|
||||||
|
const flag = JSON.parse(buffer.toString());
|
||||||
|
if (flag.body && flag.body.type === "Buffer" && flag.body.data) {
|
||||||
|
flag.body = Buffer.from(flag.body.data);
|
||||||
|
}
|
||||||
|
if (flag.cookieJar) {
|
||||||
|
flag.cookieJar = tough.CookieJar.fromJSON(flag.cookieJar);
|
||||||
|
}
|
||||||
|
|
||||||
|
flag.synchronous = false;
|
||||||
|
Object.assign(xhrImpl.flag, flag);
|
||||||
|
const { properties } = xhrImpl;
|
||||||
|
xhrImpl.readyState = READY_STATES.OPENED;
|
||||||
|
try {
|
||||||
|
xhr.addEventListener("loadend", () => {
|
||||||
|
if (properties.error) {
|
||||||
|
properties.error = properties.error.stack || util.inspect(properties.error);
|
||||||
|
}
|
||||||
|
process.stdout.write(JSON.stringify({
|
||||||
|
responseURL: xhrImpl.responseURL,
|
||||||
|
status: xhrImpl.status,
|
||||||
|
statusText: xhrImpl.statusText,
|
||||||
|
properties
|
||||||
|
}), () => {
|
||||||
|
process.exit(0);
|
||||||
|
});
|
||||||
|
}, false);
|
||||||
|
xhr.send(flag.body);
|
||||||
|
} catch (error) {
|
||||||
|
properties.error += error.stack || util.inspect(error);
|
||||||
|
process.stdout.write(JSON.stringify({
|
||||||
|
responseURL: xhrImpl.responseURL,
|
||||||
|
status: xhrImpl.status,
|
||||||
|
statusText: xhrImpl.statusText,
|
||||||
|
properties
|
||||||
|
}), () => {
|
||||||
|
process.exit(0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
hook: after
|
hook: after-build
|
||||||
commands: |
|
commands: |
|
||||||
echo "after-build hook test!"
|
echo "after-build hook test!"
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
hook: before
|
hook: before-build
|
||||||
commands: |
|
commands: |
|
||||||
echo "before-build hook test!!"
|
echo "before-build hook test!!"
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
const base = require('./jest.config.js');
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
...base,
|
|
||||||
forceExit: true,
|
|
||||||
detectOpenHandles: true,
|
|
||||||
testTimeout: 120000,
|
|
||||||
maxWorkers: 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
@@ -18,6 +18,7 @@ module.exports = {
|
|||||||
transform: {
|
transform: {
|
||||||
'^.+\\.ts$': 'ts-jest',
|
'^.+\\.ts$': 'ts-jest',
|
||||||
},
|
},
|
||||||
|
autoRun: false,
|
||||||
|
|
||||||
// Indicates whether each individual test should be reported during the run
|
// Indicates whether each individual test should be reported during the run
|
||||||
verbose: true,
|
verbose: true,
|
||||||
@@ -25,6 +26,6 @@ module.exports = {
|
|||||||
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
|
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
|
||||||
modulePathIgnorePatterns: ['<rootDir>/lib/', '<rootDir>/dist/'],
|
modulePathIgnorePatterns: ['<rootDir>/lib/', '<rootDir>/dist/'],
|
||||||
|
|
||||||
// Use jest.setup.js to polyfill fetch for all tests
|
// A list of paths to modules that run some code to configure or set up the testing framework before each test
|
||||||
setupFiles: ['<rootDir>/jest.setup.js'],
|
setupFilesAfterEnv: ['<rootDir>/src/jest.setup.ts'],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
const fetch = require('node-fetch');
|
|
||||||
global.fetch = fetch;
|
|
||||||
67
package.json
67
package.json
@@ -1,71 +1,57 @@
|
|||||||
{
|
{
|
||||||
"name": "unity-builder",
|
"name": "unity-builder",
|
||||||
"version": "3.0.0",
|
"version": "2.0.0",
|
||||||
"description": "Build Unity projects for different platforms.",
|
"description": "Build Unity projects for different platforms.",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"repository": "git@github.com:game-ci/unity-builder.git",
|
"repository": "git@github.com:game-ci/unity-builder.git",
|
||||||
"author": "Webber <webber@takken.io>",
|
"author": "Webber <webber@takken.io>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepare": "lefthook install",
|
"prepare": "lefthook install && npx husky uninstall -y",
|
||||||
"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 inputPullCommand=\"gcp-secret-manager\" populateOverride=true pullInputList=UNITY_EMAIL,UNITY_SERIAL,UNITY_PASSWORD yarn test -i -t \"cloud runner\"",
|
"gcp-secrets-tests": "cross-env cloudRunnerCluster=aws cloudRunnerTests=true readInputOverrideCommand=\"gcp-secret-manager\" populateOverride=true readInputFromOverrideList=UNITY_EMAIL,UNITY_SERIAL,UNITY_PASSWORD yarn test -i -t \"cloud runner\"",
|
||||||
"gcp-secrets-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",
|
"gcp-secrets-cli": "cross-env cloudRunnerTests=true readInputOverrideCommand=\"gcp-secret-manager\" yarn ts-node src/index.ts -m cli --populateOverride true --readInputFromOverrideList UNITY_EMAIL,UNITY_SERIAL,UNITY_PASSWORD",
|
||||||
"aws-secrets-cli": "cross-env cloudRunnerTests=true inputPullCommand=\"aws-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",
|
||||||
"cli-aws": "cross-env providerStrategy=aws yarn run test-cli",
|
"cli-aws": "cross-env cloudRunnerCluster=aws yarn run test-cli",
|
||||||
"cli-k8s": "cross-env providerStrategy=k8s yarn run test-cli",
|
"cli-k8s": "cross-env cloudRunnerCluster=k8s yarn run test-cli",
|
||||||
"test-cli": "cross-env cloudRunnerTests=true yarn ts-node src/index.ts -m cli --projectPath test-project",
|
"test-cli": "cross-env cloudRunnerTests=true yarn ts-node src/index.ts -m cli --projectPath test-project",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"test:ci": "jest --config=jest.ci.config.js --runInBand",
|
|
||||||
"test-i": "cross-env cloudRunnerTests=true yarn test -i -t \"cloud runner\"",
|
"test-i": "cross-env cloudRunnerTests=true yarn test -i -t \"cloud runner\"",
|
||||||
"test-i-*": "yarn run test-i-aws && yarn run test-i-k8s",
|
"test-i-*": "yarn run test-i-aws && yarn run test-i-k8s",
|
||||||
"test-i-aws": "cross-env cloudRunnerTests=true providerStrategy=aws yarn test -i -t \"cloud runner\"",
|
"test-i-aws": "cross-env cloudRunnerTests=true cloudRunnerCluster=aws yarn test -i -t \"cloud runner\"",
|
||||||
"test-i-k8s": "cross-env cloudRunnerTests=true providerStrategy=k8s yarn test -i -t \"cloud runner\""
|
"test-i-k8s": "cross-env cloudRunnerTests=true cloudRunnerCluster=k8s yarn test -i -t \"cloud runner\""
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18.x"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/cache": "^4.0.0",
|
"@actions/core": "^1.10.0",
|
||||||
"@actions/core": "^1.11.1",
|
"@actions/exec": "^1.1.0",
|
||||||
"@actions/exec": "^1.1.1",
|
"@actions/github": "^5.0.0",
|
||||||
"@actions/github": "^6.0.0",
|
|
||||||
"@aws-sdk/client-cloudformation": "^3.777.0",
|
|
||||||
"@aws-sdk/client-cloudwatch-logs": "^3.777.0",
|
|
||||||
"@aws-sdk/client-ecs": "^3.778.0",
|
|
||||||
"@aws-sdk/client-kinesis": "^3.777.0",
|
|
||||||
"@aws-sdk/client-s3": "^3.779.0",
|
|
||||||
"@kubernetes/client-node": "^0.16.3",
|
"@kubernetes/client-node": "^0.16.3",
|
||||||
"@octokit/core": "^5.1.0",
|
"@octokit/core": "^3.5.1",
|
||||||
"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.3.5",
|
||||||
"shell-quote": "^1.8.3",
|
"unity-changeset": "^1.6.0",
|
||||||
"ts-md5": "^1.3.1",
|
"uuid": "^8.3.2",
|
||||||
"unity-changeset": "^3.1.0",
|
"yaml": "^1.10.2"
|
||||||
"uuid": "^9.0.0",
|
|
||||||
"yaml": "^2.2.2"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/base-64": "^1.0.0",
|
"@arkweid/lefthook": "^0.7.7",
|
||||||
"@types/jest": "^27.4.1",
|
"@types/jest": "^27.4.1",
|
||||||
"@types/node": "^17.0.23",
|
"@types/node": "^17.0.23",
|
||||||
"@types/semver": "^7.3.9",
|
"@types/semver": "^7.3.9",
|
||||||
"@types/uuid": "^9.0.0",
|
|
||||||
"@typescript-eslint/parser": "4.8.1",
|
"@typescript-eslint/parser": "4.8.1",
|
||||||
"@vercel/ncc": "^0.36.1",
|
"@vercel/ncc": "^0.33.3",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"eslint": "^7.23.0",
|
"eslint": "7.17.0",
|
||||||
"eslint-config-prettier": "8.1.0",
|
"eslint-config-prettier": "8.1.0",
|
||||||
"eslint-plugin-github": "^4.1.1",
|
"eslint-plugin-github": "^4.1.1",
|
||||||
"eslint-plugin-jest": "24.1.3",
|
"eslint-plugin-jest": "24.1.3",
|
||||||
@@ -73,18 +59,11 @@
|
|||||||
"eslint-plugin-unicorn": "28.0.2",
|
"eslint-plugin-unicorn": "28.0.2",
|
||||||
"jest": "^27.5.1",
|
"jest": "^27.5.1",
|
||||||
"jest-circus": "^27.5.1",
|
"jest-circus": "^27.5.1",
|
||||||
"jest-fail-on-console": "^3.0.2",
|
"jest-fail-on-console": "^2.3.0",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"lefthook": "^1.6.1",
|
|
||||||
"node-fetch": "2",
|
|
||||||
"prettier": "^2.5.1",
|
"prettier": "^2.5.1",
|
||||||
"ts-jest": "^27.1.3",
|
"ts-jest": "^27.1.3",
|
||||||
"ts-node": "10.8.1",
|
"ts-node": "10.4.0",
|
||||||
"typescript": "4.7.4",
|
"typescript": "4.1.3"
|
||||||
"yarn-audit-fix": "^9.3.8"
|
|
||||||
},
|
|
||||||
"volta": {
|
|
||||||
"node": "20.5.1",
|
|
||||||
"yarn": "1.22.19"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
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
|
|
||||||
29
src/index.ts
29
src/index.ts
@@ -3,7 +3,6 @@ import { Action, BuildParameters, Cache, CloudRunner, Docker, ImageTag, Output }
|
|||||||
import { Cli } from './model/cli/cli';
|
import { Cli } from './model/cli/cli';
|
||||||
import MacBuilder from './model/mac-builder';
|
import MacBuilder from './model/mac-builder';
|
||||||
import PlatformSetup from './model/platform-setup';
|
import PlatformSetup from './model/platform-setup';
|
||||||
|
|
||||||
async function runMain() {
|
async function runMain() {
|
||||||
try {
|
try {
|
||||||
if (Cli.InitCliMode()) {
|
if (Cli.InitCliMode()) {
|
||||||
@@ -19,35 +18,23 @@ async function runMain() {
|
|||||||
const buildParameters = await BuildParameters.create();
|
const buildParameters = await BuildParameters.create();
|
||||||
const baseImage = new ImageTag(buildParameters);
|
const baseImage = new ImageTag(buildParameters);
|
||||||
|
|
||||||
let exitCode = -1;
|
if (buildParameters.cloudRunnerCluster !== 'local') {
|
||||||
|
await CloudRunner.run(buildParameters, baseImage.toString());
|
||||||
if (buildParameters.providerStrategy === 'local') {
|
} else {
|
||||||
core.info('Building locally');
|
core.info('Building locally');
|
||||||
await PlatformSetup.setup(buildParameters, actionFolder);
|
await PlatformSetup.setup(buildParameters, actionFolder);
|
||||||
exitCode =
|
if (process.platform === 'darwin') {
|
||||||
process.platform === 'darwin'
|
MacBuilder.run(actionFolder, workspace, buildParameters);
|
||||||
? await MacBuilder.run(actionFolder)
|
} else {
|
||||||
: await Docker.run(baseImage.toString(), {
|
await Docker.run(baseImage, { workspace, actionFolder, ...buildParameters });
|
||||||
workspace,
|
}
|
||||||
actionFolder,
|
|
||||||
...buildParameters,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
await CloudRunner.run(buildParameters, baseImage.toString());
|
|
||||||
exitCode = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set output
|
// Set output
|
||||||
await Output.setBuildVersion(buildParameters.buildVersion);
|
await Output.setBuildVersion(buildParameters.buildVersion);
|
||||||
await Output.setAndroidVersionCode(buildParameters.androidVersionCode);
|
await Output.setAndroidVersionCode(buildParameters.androidVersionCode);
|
||||||
await Output.setEngineExitCode(exitCode);
|
|
||||||
|
|
||||||
if (exitCode !== 0) {
|
|
||||||
core.setFailed(`Build failed with exit code ${exitCode}`);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
core.setFailed((error as Error).message);
|
core.setFailed((error as Error).message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
runMain();
|
runMain();
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
// Integration test for exercising real GitHub check creation and updates.
|
|
||||||
import CloudRunner from '../model/cloud-runner/cloud-runner';
|
|
||||||
import UnityVersioning from '../model/unity-versioning';
|
|
||||||
import GitHub from '../model/github';
|
|
||||||
import { TIMEOUT_INFINITE, createParameters } from '../test-utils/cloud-runner-test-helpers';
|
|
||||||
|
|
||||||
const runIntegration = process.env.RUN_GITHUB_INTEGRATION_TESTS === 'true';
|
|
||||||
const describeOrSkip = runIntegration ? describe : describe.skip;
|
|
||||||
|
|
||||||
describeOrSkip('Cloud Runner Github Checks Integration', () => {
|
|
||||||
it(
|
|
||||||
'creates and updates a real GitHub check',
|
|
||||||
async () => {
|
|
||||||
const buildParameter = await createParameters({
|
|
||||||
versioning: 'None',
|
|
||||||
projectPath: 'test-project',
|
|
||||||
unityVersion: UnityVersioning.read('test-project'),
|
|
||||||
asyncCloudRunner: `true`,
|
|
||||||
githubChecks: `true`,
|
|
||||||
});
|
|
||||||
await CloudRunner.setup(buildParameter);
|
|
||||||
const checkId = await GitHub.createGitHubCheck(`integration create`);
|
|
||||||
expect(checkId).not.toEqual('');
|
|
||||||
await GitHub.updateGitHubCheck(`1 ${new Date().toISOString()}`, `integration`);
|
|
||||||
await GitHub.updateGitHubCheck(`2 ${new Date().toISOString()}`, `integration`, `success`, `completed`);
|
|
||||||
},
|
|
||||||
TIMEOUT_INFINITE,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { stat } from 'node:fs/promises';
|
import { stat } from 'fs/promises';
|
||||||
|
|
||||||
describe('Integrity tests', () => {
|
describe('Integrity tests', () => {
|
||||||
describe('package-lock.json', () => {
|
describe('package-lock.json', () => {
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
import { fetch as undiciFetch, Headers, Request, Response } from 'undici';
|
|
||||||
|
|
||||||
Object.assign(globalThis, { fetch: undiciFetch, Headers, Request, Response });
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import path from 'node:path';
|
import path from 'path';
|
||||||
import fs from 'node:fs';
|
import fs from 'fs';
|
||||||
import Action from './action';
|
import Action from './action';
|
||||||
|
|
||||||
describe('Action', () => {
|
describe('Action', () => {
|
||||||
|
|||||||
@@ -1,27 +1,23 @@
|
|||||||
import path from 'node:path';
|
import path from 'path';
|
||||||
|
|
||||||
class Action {
|
class Action {
|
||||||
static get supportedPlatforms(): string[] {
|
static get supportedPlatforms() {
|
||||||
return ['linux', 'win32', 'darwin'];
|
return ['linux', 'win32', 'darwin'];
|
||||||
}
|
}
|
||||||
|
|
||||||
static get isRunningLocally(): boolean {
|
static get isRunningLocally() {
|
||||||
return process.env.RUNNER_WORKSPACE === undefined;
|
return process.env.RUNNER_WORKSPACE === undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get isRunningFromSource(): boolean {
|
static get isRunningFromSource() {
|
||||||
return path.basename(__dirname) === 'model';
|
return path.basename(__dirname) === 'model';
|
||||||
}
|
}
|
||||||
|
|
||||||
static get canonicalName(): string {
|
static get canonicalName() {
|
||||||
if (Action.isRunningFromSource) {
|
|
||||||
return path.basename(path.dirname(path.join(path.dirname(__filename), '/..')));
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'unity-builder';
|
return 'unity-builder';
|
||||||
}
|
}
|
||||||
|
|
||||||
static get rootFolder(): string {
|
static get rootFolder() {
|
||||||
if (Action.isRunningFromSource) {
|
if (Action.isRunningFromSource) {
|
||||||
return path.dirname(path.dirname(path.dirname(__filename)));
|
return path.dirname(path.dirname(path.dirname(__filename)));
|
||||||
}
|
}
|
||||||
@@ -29,12 +25,12 @@ class Action {
|
|||||||
return path.dirname(path.dirname(__filename));
|
return path.dirname(path.dirname(__filename));
|
||||||
}
|
}
|
||||||
|
|
||||||
static get actionFolder(): string {
|
static get actionFolder() {
|
||||||
return `${Action.rootFolder}/dist`;
|
return `${Action.rootFolder}/dist`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get workspace(): string {
|
static get workspace() {
|
||||||
return process.env.GITHUB_WORKSPACE!;
|
return process.env.GITHUB_WORKSPACE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static checkCompatibility() {
|
static checkCompatibility() {
|
||||||
|
|||||||
@@ -3,15 +3,15 @@ import AndroidVersioning from './android-versioning';
|
|||||||
describe('Android Versioning', () => {
|
describe('Android Versioning', () => {
|
||||||
describe('versionToVersionCode', () => {
|
describe('versionToVersionCode', () => {
|
||||||
it('defaults to 0 when versioning strategy is none', () => {
|
it('defaults to 0 when versioning strategy is none', () => {
|
||||||
expect(AndroidVersioning.versionToVersionCode('none')).toBe('0');
|
expect(AndroidVersioning.versionToVersionCode('none')).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('defaults to 1 when version is not a valid semver', () => {
|
it('defaults to 1 when version is not a valid semver', () => {
|
||||||
expect(AndroidVersioning.versionToVersionCode('abcd')).toBe('1');
|
expect(AndroidVersioning.versionToVersionCode('abcd')).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns a number', () => {
|
it('returns a number', () => {
|
||||||
expect(AndroidVersioning.versionToVersionCode('123.456.789')).toBe('123456789');
|
expect(AndroidVersioning.versionToVersionCode('123.456.789')).toBe(123456789);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throw when generated version code is too large', () => {
|
it('throw when generated version code is too large', () => {
|
||||||
@@ -21,11 +21,11 @@ describe('Android Versioning', () => {
|
|||||||
|
|
||||||
describe('determineVersionCode', () => {
|
describe('determineVersionCode', () => {
|
||||||
it('defaults to parsed version', () => {
|
it('defaults to parsed version', () => {
|
||||||
expect(AndroidVersioning.determineVersionCode('1.2.3', '')).toBe('1002003');
|
expect(AndroidVersioning.determineVersionCode('1.2.3', '')).toBe(1002003);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('use specified code', () => {
|
it('use specified code', () => {
|
||||||
expect(AndroidVersioning.determineVersionCode('1.2.3', '2')).toBe('2');
|
expect(AndroidVersioning.determineVersionCode('1.2.3', 2)).toBe(2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -2,19 +2,19 @@ import * as core from '@actions/core';
|
|||||||
import * as semver from 'semver';
|
import * as semver from 'semver';
|
||||||
|
|
||||||
export default class AndroidVersioning {
|
export default class AndroidVersioning {
|
||||||
static determineVersionCode(version: string, inputVersionCode: string): string {
|
static determineVersionCode(version, inputVersionCode) {
|
||||||
if (inputVersionCode === '') {
|
if (!inputVersionCode) {
|
||||||
return AndroidVersioning.versionToVersionCode(version);
|
return AndroidVersioning.versionToVersionCode(version);
|
||||||
}
|
}
|
||||||
|
|
||||||
return inputVersionCode;
|
return inputVersionCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
static versionToVersionCode(version: string): string {
|
static versionToVersionCode(version) {
|
||||||
if (version === 'none') {
|
if (version === 'none') {
|
||||||
core.info(`Versioning strategy is set to ${version}, so android version code should not be applied.`);
|
core.info(`Versioning strategy is set to ${version}, so android version code should not be applied.`);
|
||||||
|
|
||||||
return '0';
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const parsedVersion = semver.parse(version);
|
const parsedVersion = semver.parse(version);
|
||||||
@@ -22,7 +22,7 @@ export default class AndroidVersioning {
|
|||||||
if (!parsedVersion) {
|
if (!parsedVersion) {
|
||||||
core.warning(`Could not parse "${version}" to semver, defaulting android version code to 1`);
|
core.warning(`Could not parse "${version}" to semver, defaulting android version code to 1`);
|
||||||
|
|
||||||
return '1';
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The greatest value Google Plays allows is 2100000000.
|
// The greatest value Google Plays allows is 2100000000.
|
||||||
@@ -36,10 +36,10 @@ export default class AndroidVersioning {
|
|||||||
}
|
}
|
||||||
core.info(`Using android versionCode ${versionCode}`);
|
core.info(`Using android versionCode ${versionCode}`);
|
||||||
|
|
||||||
return versionCode.toString();
|
return versionCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
static determineSdkManagerParameters(targetSdkVersion: string) {
|
static determineSdkManagerParameters(targetSdkVersion) {
|
||||||
const parsedVersion = Number.parseInt(targetSdkVersion.slice(-2), 10);
|
const parsedVersion = Number.parseInt(targetSdkVersion.slice(-2), 10);
|
||||||
|
|
||||||
return Number.isNaN(parsedVersion) ? '' : `platforms;android-${parsedVersion}`;
|
return Number.isNaN(parsedVersion) ? '' : `platforms;android-${parsedVersion}`;
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ describe('BuildParameters', () => {
|
|||||||
it('determines the unity version only once', async () => {
|
it('determines the unity version only once', async () => {
|
||||||
jest.spyOn(UnityVersioning, 'determineUnityVersion').mockImplementation(() => '2019.2.11f1');
|
jest.spyOn(UnityVersioning, 'determineUnityVersion').mockImplementation(() => '2019.2.11f1');
|
||||||
await BuildParameters.create();
|
await BuildParameters.create();
|
||||||
expect(UnityVersioning.determineUnityVersion).toHaveBeenCalledTimes(1);
|
await expect(UnityVersioning.determineUnityVersion).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns the android version code with provided input', async () => {
|
it('returns the android version code with provided input', async () => {
|
||||||
@@ -47,15 +47,13 @@ describe('BuildParameters', () => {
|
|||||||
it('returns the android version code from version by default', async () => {
|
it('returns the android version code from version by default', async () => {
|
||||||
const mockValue = '';
|
const mockValue = '';
|
||||||
jest.spyOn(Input, 'androidVersionCode', 'get').mockReturnValue(mockValue);
|
jest.spyOn(Input, 'androidVersionCode', 'get').mockReturnValue(mockValue);
|
||||||
await expect(BuildParameters.create()).resolves.toEqual(
|
await expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ androidVersionCode: 1003037 }));
|
||||||
expect.objectContaining({ androidVersionCode: '1003037' }),
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('determines the android sdk manager parameters only once', async () => {
|
it('determines the android sdk manager parameters only once', async () => {
|
||||||
jest.spyOn(AndroidVersioning, 'determineSdkManagerParameters').mockImplementation(() => 'platforms;android-30');
|
jest.spyOn(AndroidVersioning, 'determineSdkManagerParameters').mockImplementation(() => 'platforms;android-30');
|
||||||
await BuildParameters.create();
|
await BuildParameters.create();
|
||||||
expect(AndroidVersioning.determineSdkManagerParameters).toHaveBeenCalledTimes(1);
|
await expect(AndroidVersioning.determineSdkManagerParameters).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns the targetPlatform', async () => {
|
it('returns the targetPlatform', async () => {
|
||||||
@@ -71,12 +69,6 @@ describe('BuildParameters', () => {
|
|||||||
await expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ projectPath: mockValue }));
|
await expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ projectPath: mockValue }));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns the build profile', async () => {
|
|
||||||
const mockValue = 'path/to/build_profile.asset';
|
|
||||||
jest.spyOn(Input, 'buildProfile', 'get').mockReturnValue(mockValue);
|
|
||||||
await expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ buildProfile: mockValue }));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns the build name', async () => {
|
it('returns the build name', async () => {
|
||||||
const mockValue = 'someBuildName';
|
const mockValue = 'someBuildName';
|
||||||
jest.spyOn(Input, 'buildName', 'get').mockReturnValue(mockValue);
|
jest.spyOn(Input, 'buildName', 'get').mockReturnValue(mockValue);
|
||||||
@@ -103,41 +95,34 @@ describe('BuildParameters', () => {
|
|||||||
await expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ buildFile: mockValue }));
|
await expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ buildFile: mockValue }));
|
||||||
});
|
});
|
||||||
|
|
||||||
test.each`
|
test.each([Platform.types.StandaloneWindows, Platform.types.StandaloneWindows64])(
|
||||||
targetPlatform | expectedExtension | androidExportType
|
'appends exe for %s',
|
||||||
${Platform.types.Android} | ${'.apk'} | ${'androidPackage'}
|
async (targetPlatform) => {
|
||||||
${Platform.types.Android} | ${'.aab'} | ${'androidAppBundle'}
|
|
||||||
${Platform.types.Android} | ${''} | ${'androidStudioProject'}
|
|
||||||
${Platform.types.StandaloneWindows} | ${'.exe'} | ${'n/a'}
|
|
||||||
${Platform.types.StandaloneWindows64} | ${'.exe'} | ${'n/a'}
|
|
||||||
`(
|
|
||||||
'appends $expectedExtension for $targetPlatform with androidExportType $androidExportType',
|
|
||||||
async ({ targetPlatform, expectedExtension, androidExportType }) => {
|
|
||||||
jest.spyOn(Input, 'targetPlatform', 'get').mockReturnValue(targetPlatform);
|
jest.spyOn(Input, 'targetPlatform', 'get').mockReturnValue(targetPlatform);
|
||||||
jest.spyOn(Input, 'buildName', 'get').mockReturnValue(targetPlatform);
|
jest.spyOn(Input, 'buildName', 'get').mockReturnValue(targetPlatform);
|
||||||
jest.spyOn(Input, 'androidExportType', 'get').mockReturnValue(androidExportType);
|
|
||||||
await expect(BuildParameters.create()).resolves.toEqual(
|
await expect(BuildParameters.create()).resolves.toEqual(
|
||||||
expect.objectContaining({ buildFile: `${targetPlatform}${expectedExtension}` }),
|
expect.objectContaining({ buildFile: `${targetPlatform}.exe` }),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
test.each`
|
test.each([Platform.types.Android])('appends apk for %s', async (targetPlatform) => {
|
||||||
targetPlatform | androidSymbolType
|
jest.spyOn(Input, 'targetPlatform', 'get').mockReturnValue(targetPlatform);
|
||||||
${Platform.types.Android} | ${'none'}
|
jest.spyOn(Input, 'buildName', 'get').mockReturnValue(targetPlatform);
|
||||||
${Platform.types.Android} | ${'public'}
|
jest.spyOn(Input, 'androidAppBundle', 'get').mockReturnValue(false);
|
||||||
${Platform.types.Android} | ${'debugging'}
|
await expect(BuildParameters.create()).resolves.toEqual(
|
||||||
${Platform.types.StandaloneWindows} | ${'none'}
|
expect.objectContaining({ buildFile: `${targetPlatform}.apk` }),
|
||||||
${Platform.types.StandaloneWindows64} | ${'none'}
|
);
|
||||||
`(
|
});
|
||||||
'androidSymbolType is set to $androidSymbolType when targetPlatform is $targetPlatform and input targetSymbolType is $androidSymbolType',
|
|
||||||
async ({ targetPlatform, androidSymbolType }) => {
|
test.each([Platform.types.Android])('appends aab for %s', async (targetPlatform) => {
|
||||||
jest.spyOn(Input, 'targetPlatform', 'get').mockReturnValue(targetPlatform);
|
jest.spyOn(Input, 'targetPlatform', 'get').mockReturnValue(targetPlatform);
|
||||||
jest.spyOn(Input, 'androidSymbolType', 'get').mockReturnValue(androidSymbolType);
|
jest.spyOn(Input, 'buildName', 'get').mockReturnValue(targetPlatform);
|
||||||
jest.spyOn(Input, 'buildName', 'get').mockReturnValue(targetPlatform);
|
jest.spyOn(Input, 'androidAppBundle', 'get').mockReturnValue(true);
|
||||||
await expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ androidSymbolType }));
|
await expect(BuildParameters.create()).resolves.toEqual(
|
||||||
},
|
expect.objectContaining({ buildFile: `${targetPlatform}.aab` }),
|
||||||
);
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('returns the build method', async () => {
|
it('returns the build method', async () => {
|
||||||
const mockValue = 'Namespace.ClassName.BuildMethod';
|
const mockValue = 'Namespace.ClassName.BuildMethod';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { customAlphabet } from 'nanoid';
|
import { customAlphabet } from 'nanoid';
|
||||||
import AndroidVersioning from './android-versioning';
|
import AndroidVersioning from './android-versioning';
|
||||||
import CloudRunnerConstants from './cloud-runner/options/cloud-runner-constants';
|
import CloudRunnerConstants from './cloud-runner/services/cloud-runner-constants';
|
||||||
import CloudRunnerBuildGuid from './cloud-runner/options/cloud-runner-guid';
|
import CloudRunnerBuildGuid from './cloud-runner/services/cloud-runner-guid';
|
||||||
import Input from './input';
|
import Input from './input';
|
||||||
import Platform from './platform';
|
import Platform from './platform';
|
||||||
import UnityVersioning from './unity-versioning';
|
import UnityVersioning from './unity-versioning';
|
||||||
@@ -10,30 +10,21 @@ import { GitRepoReader } from './input-readers/git-repo';
|
|||||||
import { GithubCliReader } from './input-readers/github-cli';
|
import { GithubCliReader } from './input-readers/github-cli';
|
||||||
import { Cli } from './cli/cli';
|
import { Cli } from './cli/cli';
|
||||||
import GitHub from './github';
|
import GitHub from './github';
|
||||||
import CloudRunnerOptions from './cloud-runner/options/cloud-runner-options';
|
import CloudRunnerOptions from './cloud-runner/cloud-runner-options';
|
||||||
import CloudRunner from './cloud-runner/cloud-runner';
|
|
||||||
import * as core from '@actions/core';
|
|
||||||
|
|
||||||
class BuildParameters {
|
class BuildParameters {
|
||||||
// eslint-disable-next-line no-undef
|
|
||||||
[key: string]: any;
|
|
||||||
|
|
||||||
public editorVersion!: string;
|
public editorVersion!: string;
|
||||||
public customImage!: string;
|
public customImage!: string;
|
||||||
public unitySerial!: string;
|
public unitySerial!: string;
|
||||||
public unityLicensingServer!: string;
|
public unityLicensingServer!: string;
|
||||||
public skipActivation!: string;
|
public runnerTempPath: string | undefined;
|
||||||
public runnerTempPath!: string;
|
|
||||||
public targetPlatform!: string;
|
public targetPlatform!: string;
|
||||||
public projectPath!: string;
|
public projectPath!: string;
|
||||||
public buildProfile!: string;
|
|
||||||
public buildName!: string;
|
public buildName!: string;
|
||||||
public buildPath!: string;
|
public buildPath!: string;
|
||||||
public buildFile!: string;
|
public buildFile!: string;
|
||||||
public buildMethod!: string;
|
public buildMethod!: string;
|
||||||
public buildVersion!: string;
|
public buildVersion!: string;
|
||||||
public manualExit!: boolean;
|
|
||||||
public enableGpu!: boolean;
|
|
||||||
public androidVersionCode!: string;
|
public androidVersionCode!: string;
|
||||||
public androidKeystoreName!: string;
|
public androidKeystoreName!: string;
|
||||||
public androidKeystoreBase64!: string;
|
public androidKeystoreBase64!: string;
|
||||||
@@ -42,136 +33,83 @@ class BuildParameters {
|
|||||||
public androidKeyaliasPass!: string;
|
public androidKeyaliasPass!: string;
|
||||||
public androidTargetSdkVersion!: string;
|
public androidTargetSdkVersion!: string;
|
||||||
public androidSdkManagerParameters!: string;
|
public androidSdkManagerParameters!: string;
|
||||||
public androidExportType!: string;
|
|
||||||
public androidSymbolType!: string;
|
|
||||||
public dockerCpuLimit!: string;
|
|
||||||
public dockerMemoryLimit!: string;
|
|
||||||
public dockerIsolationMode!: string;
|
|
||||||
public containerRegistryRepository!: string;
|
|
||||||
public containerRegistryImageVersion!: string;
|
|
||||||
|
|
||||||
public customParameters!: string;
|
public customParameters!: string;
|
||||||
public sshAgent!: string;
|
public sshAgent!: string;
|
||||||
public sshPublicKeysDirectoryPath!: string;
|
public cloudRunnerCluster!: string;
|
||||||
public providerStrategy!: string;
|
public awsBaseStackName!: string;
|
||||||
public gitPrivateToken!: string;
|
public gitPrivateToken!: string;
|
||||||
public awsStackName!: string;
|
public awsStackName!: string;
|
||||||
public awsEndpoint?: string;
|
|
||||||
public awsCloudFormationEndpoint?: string;
|
|
||||||
public awsEcsEndpoint?: string;
|
|
||||||
public awsKinesisEndpoint?: string;
|
|
||||||
public awsCloudWatchLogsEndpoint?: string;
|
|
||||||
public awsS3Endpoint?: string;
|
|
||||||
public storageProvider!: string;
|
|
||||||
public rcloneRemote!: string;
|
|
||||||
public kubeConfig!: string;
|
public kubeConfig!: string;
|
||||||
public containerMemory!: string;
|
public cloudRunnerMemory!: string;
|
||||||
public containerCpu!: string;
|
public cloudRunnerCpu!: string;
|
||||||
public containerNamespace!: string;
|
|
||||||
public kubeVolumeSize!: string;
|
public kubeVolumeSize!: string;
|
||||||
public kubeVolume!: string;
|
public kubeVolume!: string;
|
||||||
public kubeStorageClass!: string;
|
public kubeStorageClass!: string;
|
||||||
public runAsHostUser!: string;
|
|
||||||
public chownFilesTo!: string;
|
public chownFilesTo!: string;
|
||||||
public commandHooks!: string;
|
public customJobHooks!: string;
|
||||||
public pullInputList!: string[];
|
public readInputFromOverrideList!: string;
|
||||||
public inputPullCommand!: string;
|
public readInputOverrideCommand!: string;
|
||||||
public cacheKey!: string;
|
public cacheKey!: string;
|
||||||
|
|
||||||
public postBuildContainerHooks!: string;
|
public postBuildSteps!: string;
|
||||||
public preBuildContainerHooks!: string;
|
public preBuildSteps!: string;
|
||||||
public customJob!: string;
|
public customJob!: string;
|
||||||
public runNumber!: string;
|
public runNumber!: string;
|
||||||
public branch!: string;
|
public branch!: string;
|
||||||
public githubRepo!: string;
|
public githubRepo!: string;
|
||||||
public cloudRunnerRepoName!: string;
|
|
||||||
public cloneDepth!: number;
|
|
||||||
public gitSha!: string;
|
public gitSha!: string;
|
||||||
public logId!: string;
|
public logId!: string;
|
||||||
public buildGuid!: string;
|
public buildGuid!: string;
|
||||||
public cloudRunnerBranch!: string;
|
public cloudRunnerBranch!: string;
|
||||||
public cloudRunnerDebug!: boolean | undefined;
|
public cloudRunnerDebug!: boolean;
|
||||||
public buildPlatform!: string | undefined;
|
public cloudRunnerBuilderPlatform!: string | undefined;
|
||||||
public isCliMode!: boolean;
|
public isCliMode!: boolean;
|
||||||
|
public retainWorkspace!: boolean;
|
||||||
public maxRetainedWorkspaces!: number;
|
public maxRetainedWorkspaces!: number;
|
||||||
public useLargePackages!: boolean;
|
public useSharedLargePackages!: boolean;
|
||||||
public useCompressionStrategy!: boolean;
|
public useLz4Compression!: boolean;
|
||||||
public garbageMaxAge!: number;
|
public garbageCollectionMaxAge!: number;
|
||||||
|
public constantGarbageCollection!: boolean;
|
||||||
public githubChecks!: boolean;
|
public githubChecks!: boolean;
|
||||||
public asyncWorkflow!: boolean;
|
|
||||||
public githubCheckId!: string;
|
|
||||||
public finalHooks!: string[];
|
|
||||||
public skipLfs!: boolean;
|
|
||||||
public skipCache!: boolean;
|
|
||||||
public cacheUnityInstallationOnMac!: boolean;
|
|
||||||
public unityHubVersionOnMac!: string;
|
|
||||||
public dockerWorkspacePath!: string;
|
|
||||||
|
|
||||||
public static shouldUseRetainedWorkspaceMode(buildParameters: BuildParameters) {
|
|
||||||
return buildParameters.maxRetainedWorkspaces > 0 && CloudRunner.lockedWorkspace !== ``;
|
|
||||||
}
|
|
||||||
|
|
||||||
static async create(): Promise<BuildParameters> {
|
static async create(): Promise<BuildParameters> {
|
||||||
const buildFile = this.parseBuildFile(Input.buildName, Input.targetPlatform, Input.androidExportType);
|
const buildFile = this.parseBuildFile(Input.buildName, Input.targetPlatform, Input.androidAppBundle);
|
||||||
const editorVersion = UnityVersioning.determineUnityVersion(Input.projectPath, Input.unityVersion);
|
const editorVersion = UnityVersioning.determineUnityVersion(Input.projectPath, Input.unityVersion);
|
||||||
const buildVersion = await Versioning.determineBuildVersion(Input.versioningStrategy, Input.specifiedVersion);
|
const buildVersion = await Versioning.determineBuildVersion(Input.versioningStrategy, Input.specifiedVersion);
|
||||||
const androidVersionCode = AndroidVersioning.determineVersionCode(buildVersion, Input.androidVersionCode);
|
const androidVersionCode = AndroidVersioning.determineVersionCode(buildVersion, Input.androidVersionCode);
|
||||||
const androidSdkManagerParameters = AndroidVersioning.determineSdkManagerParameters(Input.androidTargetSdkVersion);
|
const androidSdkManagerParameters = AndroidVersioning.determineSdkManagerParameters(Input.androidTargetSdkVersion);
|
||||||
|
|
||||||
const androidSymbolExportType = Input.androidSymbolType;
|
// Todo - Don't use process.env directly, that's what the input model class is for.
|
||||||
if (Platform.isAndroid(Input.targetPlatform)) {
|
// ---
|
||||||
switch (androidSymbolExportType) {
|
|
||||||
case 'none':
|
|
||||||
case 'public':
|
|
||||||
case 'debugging':
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error(
|
|
||||||
`Invalid androidSymbolType: ${Input.androidSymbolType}. Must be one of: none, public, debugging`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let unitySerial = '';
|
let unitySerial = '';
|
||||||
if (Input.unityLicensingServer === '') {
|
if (Input.unityLicensingServer === '') {
|
||||||
if (!Input.unitySerial && GitHub.githubInputEnabled) {
|
if (!process.env.UNITY_SERIAL && GitHub.githubInputEnabled) {
|
||||||
// No serial was present, so it is a personal license that we need to convert
|
// No serial was present, so it is a personal license that we need to convert
|
||||||
if (!Input.unityLicense) {
|
if (!process.env.UNITY_LICENSE) {
|
||||||
throw new Error(
|
throw new Error(`Missing Unity License File and no Serial was found. If this
|
||||||
`Missing Unity License File and no Serial was found. If this
|
|
||||||
is a personal license, make sure to follow the activation
|
is a personal license, make sure to follow the activation
|
||||||
steps and set the UNITY_LICENSE GitHub secret or enter a Unity
|
steps and set the UNITY_LICENSE GitHub secret or enter a Unity
|
||||||
serial number inside the UNITY_SERIAL GitHub secret.`,
|
serial number inside the UNITY_SERIAL GitHub secret.`);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
unitySerial = this.getSerialFromLicenseFile(Input.unityLicense);
|
unitySerial = this.getSerialFromLicenseFile(process.env.UNITY_LICENSE);
|
||||||
} else {
|
} else {
|
||||||
unitySerial = Input.unitySerial!;
|
unitySerial = process.env.UNITY_SERIAL!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unitySerial !== undefined && unitySerial.length === 27) {
|
|
||||||
core.setSecret(unitySerial);
|
|
||||||
core.setSecret(`${unitySerial.slice(0, -4)}XXXX`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
editorVersion,
|
editorVersion,
|
||||||
customImage: Input.customImage,
|
customImage: Input.customImage,
|
||||||
unitySerial,
|
unitySerial,
|
||||||
unityLicensingServer: Input.unityLicensingServer,
|
unityLicensingServer: Input.unityLicensingServer,
|
||||||
skipActivation: Input.skipActivation,
|
runnerTempPath: process.env.RUNNER_TEMP,
|
||||||
runnerTempPath: Input.runnerTempPath,
|
|
||||||
targetPlatform: Input.targetPlatform,
|
targetPlatform: Input.targetPlatform,
|
||||||
projectPath: Input.projectPath,
|
projectPath: Input.projectPath,
|
||||||
buildProfile: Input.buildProfile,
|
|
||||||
buildName: Input.buildName,
|
buildName: Input.buildName,
|
||||||
buildPath: `${Input.buildsPath}/${Input.targetPlatform}`,
|
buildPath: `${Input.buildsPath}/${Input.targetPlatform}`,
|
||||||
buildFile,
|
buildFile,
|
||||||
buildMethod: Input.buildMethod,
|
buildMethod: Input.buildMethod,
|
||||||
buildVersion,
|
buildVersion,
|
||||||
manualExit: Input.manualExit,
|
|
||||||
enableGpu: Input.enableGpu,
|
|
||||||
androidVersionCode,
|
androidVersionCode,
|
||||||
androidKeystoreName: Input.androidKeystoreName,
|
androidKeystoreName: Input.androidKeystoreName,
|
||||||
androidKeystoreBase64: Input.androidKeystoreBase64,
|
androidKeystoreBase64: Input.androidKeystoreBase64,
|
||||||
@@ -180,95 +118,59 @@ class BuildParameters {
|
|||||||
androidKeyaliasPass: Input.androidKeyaliasPass,
|
androidKeyaliasPass: Input.androidKeyaliasPass,
|
||||||
androidTargetSdkVersion: Input.androidTargetSdkVersion,
|
androidTargetSdkVersion: Input.androidTargetSdkVersion,
|
||||||
androidSdkManagerParameters,
|
androidSdkManagerParameters,
|
||||||
androidExportType: Input.androidExportType,
|
|
||||||
androidSymbolType: androidSymbolExportType,
|
|
||||||
customParameters: Input.customParameters,
|
customParameters: Input.customParameters,
|
||||||
sshAgent: Input.sshAgent,
|
sshAgent: Input.sshAgent,
|
||||||
sshPublicKeysDirectoryPath: Input.sshPublicKeysDirectoryPath,
|
gitPrivateToken: Input.gitPrivateToken || (await GithubCliReader.GetGitHubAuthToken()),
|
||||||
gitPrivateToken: Input.gitPrivateToken ?? (await GithubCliReader.GetGitHubAuthToken()),
|
|
||||||
runAsHostUser: Input.runAsHostUser,
|
|
||||||
chownFilesTo: Input.chownFilesTo,
|
chownFilesTo: Input.chownFilesTo,
|
||||||
dockerCpuLimit: Input.dockerCpuLimit,
|
cloudRunnerCluster: CloudRunnerOptions.cloudRunnerCluster,
|
||||||
dockerMemoryLimit: Input.dockerMemoryLimit,
|
cloudRunnerBuilderPlatform: CloudRunnerOptions.cloudRunnerBuilderPlatform,
|
||||||
dockerIsolationMode: Input.dockerIsolationMode,
|
awsBaseStackName: CloudRunnerOptions.awsBaseStackName,
|
||||||
containerRegistryRepository: Input.containerRegistryRepository,
|
|
||||||
containerRegistryImageVersion: Input.containerRegistryImageVersion,
|
|
||||||
providerStrategy: CloudRunnerOptions.providerStrategy,
|
|
||||||
buildPlatform: CloudRunnerOptions.buildPlatform,
|
|
||||||
kubeConfig: CloudRunnerOptions.kubeConfig,
|
kubeConfig: CloudRunnerOptions.kubeConfig,
|
||||||
containerMemory: CloudRunnerOptions.containerMemory,
|
cloudRunnerMemory: CloudRunnerOptions.cloudRunnerMemory,
|
||||||
containerCpu: CloudRunnerOptions.containerCpu,
|
cloudRunnerCpu: CloudRunnerOptions.cloudRunnerCpu,
|
||||||
containerNamespace: CloudRunnerOptions.containerNamespace,
|
|
||||||
kubeVolumeSize: CloudRunnerOptions.kubeVolumeSize,
|
kubeVolumeSize: CloudRunnerOptions.kubeVolumeSize,
|
||||||
kubeVolume: CloudRunnerOptions.kubeVolume,
|
kubeVolume: CloudRunnerOptions.kubeVolume,
|
||||||
postBuildContainerHooks: CloudRunnerOptions.postBuildContainerHooks,
|
postBuildSteps: CloudRunnerOptions.postBuildSteps,
|
||||||
preBuildContainerHooks: CloudRunnerOptions.preBuildContainerHooks,
|
preBuildSteps: CloudRunnerOptions.preBuildSteps,
|
||||||
customJob: CloudRunnerOptions.customJob,
|
customJob: CloudRunnerOptions.customJob,
|
||||||
runNumber: Input.runNumber,
|
runNumber: Input.runNumber,
|
||||||
branch: Input.branch.replace('/head', '') || (await GitRepoReader.GetBranch()),
|
branch: Input.branch.replace('/head', '') || (await GitRepoReader.GetBranch()),
|
||||||
cloudRunnerBranch: CloudRunnerOptions.cloudRunnerBranch.split('/').reverse()[0],
|
cloudRunnerBranch: CloudRunnerOptions.cloudRunnerBranch.split('/').reverse()[0],
|
||||||
cloudRunnerDebug: CloudRunnerOptions.cloudRunnerDebug,
|
cloudRunnerDebug: CloudRunnerOptions.cloudRunnerDebug,
|
||||||
githubRepo: (Input.githubRepo ?? (await GitRepoReader.GetRemote())) || CloudRunnerOptions.cloudRunnerRepoName,
|
githubRepo: Input.githubRepo || (await GitRepoReader.GetRemote()) || 'game-ci/unity-builder',
|
||||||
cloudRunnerRepoName: CloudRunnerOptions.cloudRunnerRepoName,
|
|
||||||
cloneDepth: Number.parseInt(CloudRunnerOptions.cloneDepth),
|
|
||||||
isCliMode: Cli.isCliMode,
|
isCliMode: Cli.isCliMode,
|
||||||
awsStackName: CloudRunnerOptions.awsStackName,
|
awsStackName: CloudRunnerOptions.awsBaseStackName,
|
||||||
awsEndpoint: CloudRunnerOptions.awsEndpoint,
|
|
||||||
awsCloudFormationEndpoint: CloudRunnerOptions.awsCloudFormationEndpoint,
|
|
||||||
awsEcsEndpoint: CloudRunnerOptions.awsEcsEndpoint,
|
|
||||||
awsKinesisEndpoint: CloudRunnerOptions.awsKinesisEndpoint,
|
|
||||||
awsCloudWatchLogsEndpoint: CloudRunnerOptions.awsCloudWatchLogsEndpoint,
|
|
||||||
awsS3Endpoint: CloudRunnerOptions.awsS3Endpoint,
|
|
||||||
storageProvider: CloudRunnerOptions.storageProvider,
|
|
||||||
rcloneRemote: CloudRunnerOptions.rcloneRemote,
|
|
||||||
gitSha: Input.gitSha,
|
gitSha: Input.gitSha,
|
||||||
logId: customAlphabet(CloudRunnerConstants.alphabet, 9)(),
|
logId: customAlphabet(CloudRunnerConstants.alphabet, 9)(),
|
||||||
buildGuid: CloudRunnerBuildGuid.generateGuid(Input.runNumber, Input.targetPlatform),
|
buildGuid: CloudRunnerBuildGuid.generateGuid(Input.runNumber, Input.targetPlatform),
|
||||||
commandHooks: CloudRunnerOptions.commandHooks,
|
customJobHooks: CloudRunnerOptions.customJobHooks(),
|
||||||
inputPullCommand: CloudRunnerOptions.inputPullCommand,
|
readInputOverrideCommand: CloudRunnerOptions.readInputOverrideCommand(),
|
||||||
pullInputList: CloudRunnerOptions.pullInputList,
|
readInputFromOverrideList: CloudRunnerOptions.readInputFromOverrideList(),
|
||||||
kubeStorageClass: CloudRunnerOptions.kubeStorageClass,
|
kubeStorageClass: CloudRunnerOptions.kubeStorageClass,
|
||||||
cacheKey: CloudRunnerOptions.cacheKey,
|
cacheKey: CloudRunnerOptions.cacheKey,
|
||||||
maxRetainedWorkspaces: Number.parseInt(CloudRunnerOptions.maxRetainedWorkspaces),
|
retainWorkspace: CloudRunnerOptions.retainWorkspaces,
|
||||||
useLargePackages: CloudRunnerOptions.useLargePackages,
|
useSharedLargePackages: CloudRunnerOptions.useSharedLargePackages,
|
||||||
useCompressionStrategy: CloudRunnerOptions.useCompressionStrategy,
|
useLz4Compression: CloudRunnerOptions.useLz4Compression,
|
||||||
garbageMaxAge: CloudRunnerOptions.garbageMaxAge,
|
maxRetainedWorkspaces: CloudRunnerOptions.maxRetainedWorkspaces,
|
||||||
|
constantGarbageCollection: CloudRunnerOptions.constantGarbageCollection,
|
||||||
|
garbageCollectionMaxAge: CloudRunnerOptions.garbageCollectionMaxAge,
|
||||||
githubChecks: CloudRunnerOptions.githubChecks,
|
githubChecks: CloudRunnerOptions.githubChecks,
|
||||||
asyncWorkflow: CloudRunnerOptions.asyncCloudRunner,
|
|
||||||
githubCheckId: CloudRunnerOptions.githubCheckId,
|
|
||||||
finalHooks: CloudRunnerOptions.finalHooks,
|
|
||||||
skipLfs: CloudRunnerOptions.skipLfs,
|
|
||||||
skipCache: CloudRunnerOptions.skipCache,
|
|
||||||
cacheUnityInstallationOnMac: Input.cacheUnityInstallationOnMac,
|
|
||||||
unityHubVersionOnMac: Input.unityHubVersionOnMac,
|
|
||||||
dockerWorkspacePath: Input.dockerWorkspacePath,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static parseBuildFile(filename: string, platform: string, androidExportType: string): string {
|
static parseBuildFile(filename, platform, androidAppBundle) {
|
||||||
if (Platform.isWindows(platform)) {
|
if (Platform.isWindows(platform)) {
|
||||||
return `${filename}.exe`;
|
return `${filename}.exe`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Platform.isAndroid(platform)) {
|
if (Platform.isAndroid(platform)) {
|
||||||
switch (androidExportType) {
|
return androidAppBundle ? `${filename}.aab` : `${filename}.apk`;
|
||||||
case `androidPackage`:
|
|
||||||
return `${filename}.apk`;
|
|
||||||
case `androidAppBundle`:
|
|
||||||
return `${filename}.aab`;
|
|
||||||
case `androidStudioProject`:
|
|
||||||
return filename;
|
|
||||||
default:
|
|
||||||
throw new Error(
|
|
||||||
`Unknown Android Export Type: ${androidExportType}. Must be one of androidPackage for apk, androidAppBundle for aab, androidStudioProject for android project`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return filename;
|
return filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
static getSerialFromLicenseFile(license: string) {
|
static getSerialFromLicenseFile(license) {
|
||||||
const startKey = `<DeveloperData Value="`;
|
const startKey = `<DeveloperData Value="`;
|
||||||
const endKey = `"/>`;
|
const endKey = `"/>`;
|
||||||
const startIndex = license.indexOf(startKey) + startKey.length;
|
const startIndex = license.indexOf(startKey) + startKey.length;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
import fs from 'node:fs';
|
import fs from 'fs';
|
||||||
import Action from './action';
|
import Action from './action';
|
||||||
import Project from './project';
|
import Project from './project';
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export class CliFunctionsRepository {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GetCliFunctions(key: any) {
|
public static GetCliFunctions(key) {
|
||||||
const results = CliFunctionsRepository.targets.find((x) => x.key === key);
|
const results = CliFunctionsRepository.targets.find((x) => x.key === key);
|
||||||
if (results === undefined || results.length === 0) {
|
if (results === undefined || results.length === 0) {
|
||||||
throw new Error(`no CLI mode found for ${key}`);
|
throw new Error(`no CLI mode found for ${key}`);
|
||||||
|
|||||||
@@ -2,23 +2,24 @@ import { Command } from 'commander-ts';
|
|||||||
import { BuildParameters, CloudRunner, ImageTag, Input } from '..';
|
import { BuildParameters, CloudRunner, ImageTag, Input } from '..';
|
||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
import { ActionYamlReader } from '../input-readers/action-yaml';
|
import { ActionYamlReader } from '../input-readers/action-yaml';
|
||||||
import CloudRunnerLogger from '../cloud-runner/services/core/cloud-runner-logger';
|
import CloudRunnerLogger from '../cloud-runner/services/cloud-runner-logger';
|
||||||
import CloudRunnerQueryOverride from '../cloud-runner/options/cloud-runner-query-override';
|
import CloudRunnerQueryOverride from '../cloud-runner/services/cloud-runner-query-override';
|
||||||
import { CliFunction, CliFunctionsRepository } from './cli-functions-repository';
|
import { CliFunction, CliFunctionsRepository } from './cli-functions-repository';
|
||||||
import { Caching } from '../cloud-runner/remote-client/caching';
|
import { Caching } from '../cloud-runner/remote-client/caching';
|
||||||
import { LfsHashing } from '../cloud-runner/services/utility/lfs-hashing';
|
import { LfsHashing } from '../cloud-runner/services/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/services/cloud-runner-options-reader';
|
||||||
import GitHub from '../github';
|
import GitHub from '../github';
|
||||||
import { OptionValues } from 'commander';
|
import { TaskParameterSerializer } from '../cloud-runner/services/task-parameter-serializer';
|
||||||
import { InputKey } from '../input';
|
import { CloudRunnerFolders } from '../cloud-runner/services/cloud-runner-folders';
|
||||||
|
import { CloudRunnerSystem } from '../cloud-runner/services/cloud-runner-system';
|
||||||
|
|
||||||
export class Cli {
|
export class Cli {
|
||||||
public static options: OptionValues | undefined;
|
public static options;
|
||||||
static get isCliMode() {
|
static get isCliMode() {
|
||||||
return Cli.options !== undefined && Cli.options.mode !== undefined && Cli.options.mode !== '';
|
return Cli.options !== undefined && Cli.options.mode !== undefined && Cli.options.mode !== '';
|
||||||
}
|
}
|
||||||
public static query(key: string, alternativeKey: string) {
|
public static query(key, alternativeKey) {
|
||||||
if (Cli.options && Cli.options[key] !== undefined) {
|
if (Cli.options && Cli.options[key] !== undefined) {
|
||||||
return Cli.options[key];
|
return Cli.options[key];
|
||||||
}
|
}
|
||||||
@@ -52,7 +53,6 @@ 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();
|
||||||
|
|
||||||
@@ -61,24 +61,22 @@ export class Cli {
|
|||||||
|
|
||||||
static async RunCli(): Promise<void> {
|
static async RunCli(): Promise<void> {
|
||||||
GitHub.githubInputEnabled = false;
|
GitHub.githubInputEnabled = false;
|
||||||
if (Cli.options!['populateOverride'] === `true`) {
|
if (Cli.options['populateOverride'] === `true`) {
|
||||||
await CloudRunnerQueryOverride.PopulateQueryOverrideInput();
|
await CloudRunnerQueryOverride.PopulateQueryOverrideInput();
|
||||||
}
|
}
|
||||||
if (Cli.options!['logInput']) {
|
if (Cli.options['logInput']) {
|
||||||
Cli.logInput();
|
Cli.logInput();
|
||||||
}
|
}
|
||||||
const results = CliFunctionsRepository.GetCliFunctions(Cli.options?.mode);
|
const results = CliFunctionsRepository.GetCliFunctions(Cli.options.mode);
|
||||||
CloudRunnerLogger.log(`Entrypoint: ${results.key}`);
|
CloudRunnerLogger.log(`Entrypoint: ${results.key}`);
|
||||||
Cli.options!.versioning = 'None';
|
Cli.options.versioning = 'None';
|
||||||
|
|
||||||
CloudRunner.buildParameters = await BuildParameters.create();
|
const buildParameter = TaskParameterSerializer.readBuildParameterFromEnvironment();
|
||||||
CloudRunner.buildParameters.buildGuid = process.env.BUILD_GUID || ``;
|
|
||||||
CloudRunnerLogger.log(`Build Params:
|
CloudRunnerLogger.log(`Build Params:
|
||||||
${JSON.stringify(CloudRunner.buildParameters, undefined, 4)}
|
${JSON.stringify(buildParameter, undefined, 4)}
|
||||||
`);
|
`);
|
||||||
CloudRunner.lockedWorkspace = process.env.LOCKED_WORKSPACE || ``;
|
CloudRunner.buildParameters = buildParameter;
|
||||||
CloudRunnerLogger.log(`Locked Workspace: ${CloudRunner.lockedWorkspace}`);
|
CloudRunner.lockedWorkspace = process.env.LOCKED_WORKSPACE;
|
||||||
await CloudRunner.setup(CloudRunner.buildParameters);
|
|
||||||
|
|
||||||
return await results.target[results.propertyKey](Cli.options);
|
return await results.target[results.propertyKey](Cli.options);
|
||||||
}
|
}
|
||||||
@@ -90,15 +88,14 @@ export class Cli {
|
|||||||
const properties = CloudRunnerOptionsReader.GetProperties();
|
const properties = CloudRunnerOptionsReader.GetProperties();
|
||||||
for (const element of properties) {
|
for (const element of properties) {
|
||||||
if (
|
if (
|
||||||
element in Input &&
|
Input[element] !== undefined &&
|
||||||
Input[element as InputKey] !== undefined &&
|
Input[element] !== '' &&
|
||||||
Input[element as InputKey] !== '' &&
|
typeof Input[element] !== `function` &&
|
||||||
typeof Input[element as InputKey] !== `function` &&
|
|
||||||
element !== 'length' &&
|
element !== 'length' &&
|
||||||
element !== 'cliOptions' &&
|
element !== 'cliOptions' &&
|
||||||
element !== 'prototype'
|
element !== 'prototype'
|
||||||
) {
|
) {
|
||||||
core.info(`${element} ${Input[element as InputKey]}`);
|
core.info(`${element} ${Input[element]}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
core.info(`\n`);
|
core.info(`\n`);
|
||||||
@@ -109,23 +106,19 @@ 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())).BuildResults;
|
return await CloudRunner.run(buildParameter, baseImage.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@CliFunction(`async-workflow`, `runs a cloud runner build`)
|
@CliFunction(`async-workflow`, `runs a cloud runner build`)
|
||||||
public static async asyncronousWorkflow(): Promise<string> {
|
public static async asyncronousWorkflow(): Promise<string> {
|
||||||
const buildParameter = await BuildParameters.create();
|
const buildParameter = await BuildParameters.create();
|
||||||
const baseImage = new ImageTag(buildParameter);
|
const baseImage = new ImageTag(buildParameter);
|
||||||
await CloudRunner.setup(buildParameter);
|
|
||||||
|
|
||||||
return (await CloudRunner.run(buildParameter, baseImage.toString())).BuildResults;
|
return await CloudRunner.run(buildParameter, baseImage.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@CliFunction(`checks-update`, `runs a cloud runner build`)
|
@CliFunction(`checks-update`, `runs a cloud runner build`)
|
||||||
public static async checksUpdate() {
|
public static async checksUpdate() {
|
||||||
const buildParameter = await BuildParameters.create();
|
|
||||||
|
|
||||||
await CloudRunner.setup(buildParameter);
|
|
||||||
const input = JSON.parse(process.env.CHECKS_UPDATE || ``);
|
const input = JSON.parse(process.env.CHECKS_UPDATE || ``);
|
||||||
core.info(`Checks Update ${process.env.CHECKS_UPDATE}`);
|
core.info(`Checks Update ${process.env.CHECKS_UPDATE}`);
|
||||||
if (input.mode === `create`) {
|
if (input.mode === `create`) {
|
||||||
@@ -172,4 +165,46 @@ 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 (!CloudRunner.buildParameters.retainWorkspace) {
|
||||||
|
await CloudRunnerSystem.Run(
|
||||||
|
`rm -r ${CloudRunnerFolders.ToLinuxFolder(CloudRunnerFolders.uniqueCloudRunnerJobFolderAbsolute)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await RemoteClient.runCustomHookFiles(`after-build`);
|
||||||
|
|
||||||
|
const parameters = await BuildParameters.create();
|
||||||
|
CloudRunner.setup(parameters);
|
||||||
|
if (parameters.constantGarbageCollection) {
|
||||||
|
await CloudRunnerSystem.Run(
|
||||||
|
`find /${CloudRunnerFolders.ToLinuxFolder(CloudRunnerFolders.buildVolumeFolder)}/ -name '*.*' -mmin +${
|
||||||
|
parameters.garbageCollectionMaxAge * 60
|
||||||
|
} -delete`,
|
||||||
|
);
|
||||||
|
await CloudRunnerSystem.Run(
|
||||||
|
`find ${CloudRunnerFolders.ToLinuxFolder(CloudRunnerFolders.cacheFolderForAllFull)} -name '*.*' -mmin +${
|
||||||
|
parameters.garbageCollectionMaxAge * 60
|
||||||
|
} -delete`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise((result) => result(``));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
290
src/model/cloud-runner/cloud-runner-options.ts
Normal file
290
src/model/cloud-runner/cloud-runner-options.ts
Normal file
@@ -0,0 +1,290 @@
|
|||||||
|
import { Cli } from '../cli/cli';
|
||||||
|
import CloudRunnerQueryOverride from './services/cloud-runner-query-override';
|
||||||
|
import GitHub from '../github';
|
||||||
|
const core = require('@actions/core');
|
||||||
|
|
||||||
|
class CloudRunnerOptions {
|
||||||
|
// ### ### ###
|
||||||
|
// Input Handling
|
||||||
|
// ### ### ###
|
||||||
|
public static getInput(query) {
|
||||||
|
if (GitHub.githubInputEnabled) {
|
||||||
|
const coreInput = core.getInput(query);
|
||||||
|
if (coreInput && coreInput !== '') {
|
||||||
|
return coreInput;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const alternativeQuery = CloudRunnerOptions.ToEnvVarFormat(query);
|
||||||
|
|
||||||
|
// Query input sources
|
||||||
|
if (Cli.query(query, alternativeQuery)) {
|
||||||
|
return Cli.query(query, alternativeQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CloudRunnerQueryOverride.query(query, alternativeQuery)) {
|
||||||
|
return CloudRunnerQueryOverride.query(query, alternativeQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.env[query] !== undefined) {
|
||||||
|
return process.env[query];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (alternativeQuery !== query && process.env[alternativeQuery] !== undefined) {
|
||||||
|
return process.env[alternativeQuery];
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ToEnvVarFormat(input: string) {
|
||||||
|
if (input.toUpperCase() === input) {
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
return input
|
||||||
|
.replace(/([A-Z])/g, ' $1')
|
||||||
|
.trim()
|
||||||
|
.toUpperCase()
|
||||||
|
.replace(/ /g, '_');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ### ### ###
|
||||||
|
// Provider parameters
|
||||||
|
// ### ### ###
|
||||||
|
|
||||||
|
static get region(): string {
|
||||||
|
return CloudRunnerOptions.getInput('region') || 'eu-west-2';
|
||||||
|
}
|
||||||
|
|
||||||
|
// ### ### ###
|
||||||
|
// GitHub parameters
|
||||||
|
// ### ### ###
|
||||||
|
static get githubChecks(): boolean {
|
||||||
|
return CloudRunnerOptions.getInput('githubChecks') || false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get githubOwner() {
|
||||||
|
return CloudRunnerOptions.getInput('githubOwner') || CloudRunnerOptions.githubRepo.split(`/`)[0] || false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get githubRepoName() {
|
||||||
|
return CloudRunnerOptions.getInput('githubRepoName') || CloudRunnerOptions.githubRepo.split(`/`)[1] || false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ### ### ###
|
||||||
|
// Git syncronization parameters
|
||||||
|
// ### ### ###
|
||||||
|
|
||||||
|
static get githubRepo() {
|
||||||
|
return CloudRunnerOptions.getInput('GITHUB_REPOSITORY') || CloudRunnerOptions.getInput('GITHUB_REPO') || undefined;
|
||||||
|
}
|
||||||
|
static get branch() {
|
||||||
|
if (CloudRunnerOptions.getInput(`GITHUB_REF`)) {
|
||||||
|
return CloudRunnerOptions.getInput(`GITHUB_REF`).replace('refs/', '').replace(`head/`, '').replace(`heads/`, '');
|
||||||
|
} else if (CloudRunnerOptions.getInput('branch')) {
|
||||||
|
return CloudRunnerOptions.getInput('branch');
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static get gitSha() {
|
||||||
|
if (CloudRunnerOptions.getInput(`GITHUB_SHA`)) {
|
||||||
|
return CloudRunnerOptions.getInput(`GITHUB_SHA`);
|
||||||
|
} else if (CloudRunnerOptions.getInput(`GitSHA`)) {
|
||||||
|
return CloudRunnerOptions.getInput(`GitSHA`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ### ### ###
|
||||||
|
// Cloud Runner parameters
|
||||||
|
// ### ### ###
|
||||||
|
|
||||||
|
static get cloudRunnerBuilderPlatform() {
|
||||||
|
const input = CloudRunnerOptions.getInput('cloudRunnerBuilderPlatform');
|
||||||
|
if (input) {
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
if (CloudRunnerOptions.cloudRunnerCluster !== 'local') {
|
||||||
|
return 'linux';
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get cloudRunnerBranch() {
|
||||||
|
return CloudRunnerOptions.getInput('cloudRunnerBranch') || 'main';
|
||||||
|
}
|
||||||
|
|
||||||
|
static get cloudRunnerCluster() {
|
||||||
|
if (Cli.isCliMode) {
|
||||||
|
return CloudRunnerOptions.getInput('cloudRunnerCluster') || 'aws';
|
||||||
|
}
|
||||||
|
|
||||||
|
return CloudRunnerOptions.getInput('cloudRunnerCluster') || 'local';
|
||||||
|
}
|
||||||
|
|
||||||
|
static get cloudRunnerCpu() {
|
||||||
|
return CloudRunnerOptions.getInput('cloudRunnerCpu');
|
||||||
|
}
|
||||||
|
|
||||||
|
static get cloudRunnerMemory() {
|
||||||
|
return CloudRunnerOptions.getInput('cloudRunnerMemory');
|
||||||
|
}
|
||||||
|
|
||||||
|
static get customJob() {
|
||||||
|
return CloudRunnerOptions.getInput('customJob') || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// ### ### ###
|
||||||
|
// Custom commands from files parameters
|
||||||
|
// ### ### ###
|
||||||
|
|
||||||
|
static get customStepFiles() {
|
||||||
|
return CloudRunnerOptions.getInput('customStepFiles')?.split(`,`) || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
static get customHookFiles() {
|
||||||
|
return CloudRunnerOptions.getInput('customHookFiles')?.split(`,`) || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// ### ### ###
|
||||||
|
// Custom commands from yaml parameters
|
||||||
|
// ### ### ###
|
||||||
|
|
||||||
|
static customJobHooks() {
|
||||||
|
return CloudRunnerOptions.getInput('customJobHooks') || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
static get postBuildSteps() {
|
||||||
|
return CloudRunnerOptions.getInput('postBuildSteps') || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
static get preBuildSteps() {
|
||||||
|
return CloudRunnerOptions.getInput('preBuildSteps') || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// ### ### ###
|
||||||
|
// Input override handling
|
||||||
|
// ### ### ###
|
||||||
|
|
||||||
|
static readInputFromOverrideList() {
|
||||||
|
return CloudRunnerOptions.getInput('readInputFromOverrideList') || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
static readInputOverrideCommand() {
|
||||||
|
const value = CloudRunnerOptions.getInput('readInputOverrideCommand');
|
||||||
|
|
||||||
|
if (value === 'gcp-secret-manager') {
|
||||||
|
return 'gcloud secrets versions access 1 --secret="{0}"';
|
||||||
|
} else if (value === 'aws-secret-manager') {
|
||||||
|
return 'aws secretsmanager get-secret-value --secret-id {0}';
|
||||||
|
}
|
||||||
|
|
||||||
|
return value || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// ### ### ###
|
||||||
|
// Aws
|
||||||
|
// ### ### ###
|
||||||
|
|
||||||
|
static get awsBaseStackName() {
|
||||||
|
return CloudRunnerOptions.getInput('awsBaseStackName') || 'game-ci';
|
||||||
|
}
|
||||||
|
|
||||||
|
// ### ### ###
|
||||||
|
// K8s
|
||||||
|
// ### ### ###
|
||||||
|
|
||||||
|
static get kubeConfig() {
|
||||||
|
return CloudRunnerOptions.getInput('kubeConfig') || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
static get kubeVolume() {
|
||||||
|
return CloudRunnerOptions.getInput('kubeVolume') || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
static get kubeVolumeSize() {
|
||||||
|
return CloudRunnerOptions.getInput('kubeVolumeSize') || '5Gi';
|
||||||
|
}
|
||||||
|
|
||||||
|
static get kubeStorageClass(): string {
|
||||||
|
return CloudRunnerOptions.getInput('kubeStorageClass') || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// ### ### ###
|
||||||
|
// Caching
|
||||||
|
// ### ### ###
|
||||||
|
|
||||||
|
static get cacheKey(): string {
|
||||||
|
return CloudRunnerOptions.getInput('cacheKey') || CloudRunnerOptions.branch;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ### ### ###
|
||||||
|
// Utility Parameters
|
||||||
|
// ### ### ###
|
||||||
|
|
||||||
|
static get cloudRunnerDebug(): boolean {
|
||||||
|
return CloudRunnerOptions.getInput(`cloudRunnerTests`) || CloudRunnerOptions.getInput(`cloudRunnerDebug`) || false;
|
||||||
|
}
|
||||||
|
static get cloudRunnerDebugTree(): boolean {
|
||||||
|
return CloudRunnerOptions.getInput(`cloudRunnerDebugTree`) || false;
|
||||||
|
}
|
||||||
|
static get cloudRunnerDebugEnv(): boolean {
|
||||||
|
return CloudRunnerOptions.getInput(`cloudRunnerDebugEnv`) || false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get watchCloudRunnerToEnd(): boolean {
|
||||||
|
if (CloudRunnerOptions.asyncCloudRunner) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CloudRunnerOptions.getInput(`watchToEnd`) || true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get asyncCloudRunner(): boolean {
|
||||||
|
return (CloudRunnerOptions.getInput('asyncCloudRunner') || `false`) === `true` || false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static get useSharedLargePackages(): boolean {
|
||||||
|
return (CloudRunnerOptions.getInput(`useSharedLargePackages`) || 'false') === 'true';
|
||||||
|
}
|
||||||
|
|
||||||
|
public static get useSharedBuilder(): boolean {
|
||||||
|
return (CloudRunnerOptions.getInput(`useSharedBuilder`) || 'true') === 'true';
|
||||||
|
}
|
||||||
|
|
||||||
|
public static get useLz4Compression(): boolean {
|
||||||
|
return (CloudRunnerOptions.getInput(`useLz4Compression`) || 'false') === 'true';
|
||||||
|
}
|
||||||
|
|
||||||
|
public static get useCleanupCron(): boolean {
|
||||||
|
return (CloudRunnerOptions.getInput(`useCleanupCron`) || 'true') === 'true';
|
||||||
|
}
|
||||||
|
|
||||||
|
// ### ### ###
|
||||||
|
// Retained Workspace
|
||||||
|
// ### ### ###
|
||||||
|
|
||||||
|
public static get retainWorkspaces(): boolean {
|
||||||
|
return CloudRunnerOptions.getInput(`retainWorkspaces`) || false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get maxRetainedWorkspaces(): number {
|
||||||
|
return Number(CloudRunnerOptions.getInput(`maxRetainedWorkspaces`)) || 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ### ### ###
|
||||||
|
// Garbage Collection
|
||||||
|
// ### ### ###
|
||||||
|
|
||||||
|
static get constantGarbageCollection(): boolean {
|
||||||
|
return CloudRunnerOptions.getInput(`constantGarbageCollection`) || true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get garbageCollectionMaxAge(): number {
|
||||||
|
return Number(CloudRunnerOptions.getInput(`garbageCollectionMaxAge`)) || 24;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CloudRunnerOptions;
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import CloudRunnerEnvironmentVariable from './cloud-runner-environment-variable';
|
import CloudRunnerEnvironmentVariable from './services/cloud-runner-environment-variable';
|
||||||
import CloudRunnerSecret from './cloud-runner-secret';
|
import CloudRunnerSecret from './services/cloud-runner-secret';
|
||||||
|
|
||||||
export class CloudRunnerStepParameters {
|
export class CloudRunnerStepState {
|
||||||
public image: string;
|
public image: string;
|
||||||
public environment: CloudRunnerEnvironmentVariable[];
|
public environment: CloudRunnerEnvironmentVariable[];
|
||||||
public secrets: CloudRunnerSecret[];
|
public secrets: CloudRunnerSecret[];
|
||||||
@@ -1,53 +1,34 @@
|
|||||||
import AwsBuildPlatform from './providers/aws';
|
import AwsBuildPlatform from './providers/aws';
|
||||||
import { BuildParameters, Input } from '..';
|
import { BuildParameters, Input } from '..';
|
||||||
import Kubernetes from './providers/k8s';
|
import Kubernetes from './providers/k8s';
|
||||||
import CloudRunnerLogger from './services/core/cloud-runner-logger';
|
import CloudRunnerLogger from './services/cloud-runner-logger';
|
||||||
import { CloudRunnerStepParameters } from './options/cloud-runner-step-parameters';
|
import { CloudRunnerStepState } from './cloud-runner-step-state';
|
||||||
import { WorkflowCompositionRoot } from './workflows/workflow-composition-root';
|
import { WorkflowCompositionRoot } from './workflows/workflow-composition-root';
|
||||||
import { CloudRunnerError } from './error/cloud-runner-error';
|
import { CloudRunnerError } from './error/cloud-runner-error';
|
||||||
import { TaskParameterSerializer } from './services/core/task-parameter-serializer';
|
import { TaskParameterSerializer } from './services/task-parameter-serializer';
|
||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
import CloudRunnerSecret from './options/cloud-runner-secret';
|
import CloudRunnerSecret from './services/cloud-runner-secret';
|
||||||
import { ProviderInterface } from './providers/provider-interface';
|
import { ProviderInterface } from './providers/provider-interface';
|
||||||
import CloudRunnerEnvironmentVariable from './options/cloud-runner-environment-variable';
|
import CloudRunnerEnvironmentVariable from './services/cloud-runner-environment-variable';
|
||||||
import TestCloudRunner from './providers/test';
|
import TestCloudRunner from './providers/test';
|
||||||
import LocalCloudRunner from './providers/local';
|
import LocalCloudRunner from './providers/local';
|
||||||
import LocalDockerCloudRunner from './providers/docker';
|
import LocalDockerCloudRunner from './providers/docker';
|
||||||
import loadProvider from './providers/provider-loader';
|
|
||||||
import GitHub from '../github';
|
import GitHub from '../github';
|
||||||
import SharedWorkspaceLocking from './services/core/shared-workspace-locking';
|
import SharedWorkspaceLocking from './services/shared-workspace-locking';
|
||||||
import { FollowLogStreamService } from './services/core/follow-log-stream-service';
|
|
||||||
import CloudRunnerResult from './services/core/cloud-runner-result';
|
|
||||||
import CloudRunnerOptions from './options/cloud-runner-options';
|
|
||||||
import ResourceTracking from './services/core/resource-tracking';
|
|
||||||
|
|
||||||
class CloudRunner {
|
class CloudRunner {
|
||||||
public static Provider: ProviderInterface;
|
public static Provider: ProviderInterface;
|
||||||
public static buildParameters: BuildParameters;
|
public static buildParameters: BuildParameters;
|
||||||
private static defaultSecrets: CloudRunnerSecret[];
|
private static defaultSecrets: CloudRunnerSecret[];
|
||||||
private static cloudRunnerEnvironmentVariables: CloudRunnerEnvironmentVariable[];
|
private static cloudRunnerEnvironmentVariables: CloudRunnerEnvironmentVariable[];
|
||||||
static lockedWorkspace: string = ``;
|
static lockedWorkspace: string | undefined;
|
||||||
public static readonly retainedWorkspacePrefix: string = `retained-workspace`;
|
public static readonly retainedWorkspacePrefix: string = `retained-workspace`;
|
||||||
|
public static githubCheckId;
|
||||||
// When true, validates AWS CloudFormation templates even when using local-docker execution
|
public static setup(buildParameters: BuildParameters) {
|
||||||
// This is set by AWS_FORCE_PROVIDER=aws-local mode
|
|
||||||
public static validateAwsTemplates: boolean = false;
|
|
||||||
public static get isCloudRunnerEnvironment() {
|
|
||||||
return process.env[`GITHUB_ACTIONS`] !== `true`;
|
|
||||||
}
|
|
||||||
public static get isCloudRunnerAsyncEnvironment() {
|
|
||||||
return process.env[`ASYNC_WORKFLOW`] === `true`;
|
|
||||||
}
|
|
||||||
public static async setup(buildParameters: BuildParameters) {
|
|
||||||
CloudRunnerLogger.setup();
|
CloudRunnerLogger.setup();
|
||||||
CloudRunnerLogger.log(`Setting up cloud runner`);
|
CloudRunnerLogger.log(`Setting up cloud runner`);
|
||||||
CloudRunner.buildParameters = buildParameters;
|
CloudRunner.buildParameters = buildParameters;
|
||||||
ResourceTracking.logAllocationSummary('setup');
|
CloudRunner.setupSelectedBuildPlatform();
|
||||||
await ResourceTracking.logDiskUsageSnapshot('setup');
|
|
||||||
if (CloudRunner.buildParameters.githubCheckId === ``) {
|
|
||||||
CloudRunner.buildParameters.githubCheckId = await GitHub.createGitHubCheck(CloudRunner.buildParameters.buildGuid);
|
|
||||||
}
|
|
||||||
await CloudRunner.setupSelectedBuildPlatform();
|
|
||||||
CloudRunner.defaultSecrets = TaskParameterSerializer.readDefaultSecrets();
|
CloudRunner.defaultSecrets = TaskParameterSerializer.readDefaultSecrets();
|
||||||
CloudRunner.cloudRunnerEnvironmentVariables =
|
CloudRunner.cloudRunnerEnvironmentVariables =
|
||||||
TaskParameterSerializer.createCloudRunnerEnvironmentVariables(buildParameters);
|
TaskParameterSerializer.createCloudRunnerEnvironmentVariables(buildParameters);
|
||||||
@@ -64,85 +45,20 @@ class CloudRunner {
|
|||||||
core.setOutput(
|
core.setOutput(
|
||||||
Input.ToEnvVarFormat(`buildArtifact`),
|
Input.ToEnvVarFormat(`buildArtifact`),
|
||||||
`build-${CloudRunner.buildParameters.buildGuid}.tar${
|
`build-${CloudRunner.buildParameters.buildGuid}.tar${
|
||||||
CloudRunner.buildParameters.useCompressionStrategy ? '.lz4' : ''
|
CloudRunner.buildParameters.useLz4Compression ? '.lz4' : ''
|
||||||
}`,
|
}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
FollowLogStreamService.Reset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async setupSelectedBuildPlatform() {
|
private static setupSelectedBuildPlatform() {
|
||||||
CloudRunnerLogger.log(`Cloud Runner platform selected ${CloudRunner.buildParameters.providerStrategy}`);
|
CloudRunnerLogger.log(`Cloud Runner platform selected ${CloudRunner.buildParameters.cloudRunnerCluster}`);
|
||||||
|
switch (CloudRunner.buildParameters.cloudRunnerCluster) {
|
||||||
// Detect LocalStack endpoints and handle AWS provider appropriately
|
|
||||||
// AWS_FORCE_PROVIDER options:
|
|
||||||
// - 'aws': Force AWS provider (requires LocalStack Pro with ECS support)
|
|
||||||
// - 'aws-local': Validate AWS templates/config but execute via local-docker (for CI without ECS)
|
|
||||||
// - unset/other: Auto-fallback to local-docker when LocalStack detected
|
|
||||||
const awsForceProvider = process.env.AWS_FORCE_PROVIDER || '';
|
|
||||||
const forceAwsProvider = awsForceProvider === 'aws' || awsForceProvider === 'true';
|
|
||||||
const useAwsLocalMode = awsForceProvider === 'aws-local';
|
|
||||||
const endpointsToCheck = [
|
|
||||||
process.env.AWS_ENDPOINT,
|
|
||||||
process.env.AWS_S3_ENDPOINT,
|
|
||||||
process.env.AWS_CLOUD_FORMATION_ENDPOINT,
|
|
||||||
process.env.AWS_ECS_ENDPOINT,
|
|
||||||
process.env.AWS_KINESIS_ENDPOINT,
|
|
||||||
process.env.AWS_CLOUD_WATCH_LOGS_ENDPOINT,
|
|
||||||
CloudRunnerOptions.awsEndpoint,
|
|
||||||
CloudRunnerOptions.awsS3Endpoint,
|
|
||||||
CloudRunnerOptions.awsCloudFormationEndpoint,
|
|
||||||
CloudRunnerOptions.awsEcsEndpoint,
|
|
||||||
CloudRunnerOptions.awsKinesisEndpoint,
|
|
||||||
CloudRunnerOptions.awsCloudWatchLogsEndpoint,
|
|
||||||
]
|
|
||||||
.filter((x) => typeof x === 'string')
|
|
||||||
.join(' ');
|
|
||||||
const isLocalStack = /localstack|localhost|127\.0\.0\.1/i.test(endpointsToCheck);
|
|
||||||
let provider = CloudRunner.buildParameters.providerStrategy;
|
|
||||||
let validateAwsTemplates = false;
|
|
||||||
|
|
||||||
if (provider === 'aws' && isLocalStack) {
|
|
||||||
if (useAwsLocalMode) {
|
|
||||||
// aws-local mode: Validate AWS templates but execute via local-docker
|
|
||||||
// This provides confidence in AWS CloudFormation without requiring LocalStack Pro
|
|
||||||
CloudRunnerLogger.log('AWS_FORCE_PROVIDER=aws-local: Validating AWS templates, executing via local-docker');
|
|
||||||
validateAwsTemplates = true;
|
|
||||||
provider = 'local-docker';
|
|
||||||
} else if (forceAwsProvider) {
|
|
||||||
// Force full AWS provider (requires LocalStack Pro with ECS support)
|
|
||||||
CloudRunnerLogger.log(
|
|
||||||
'LocalStack endpoints detected but AWS_FORCE_PROVIDER=aws; using full AWS provider (requires ECS support)',
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// Auto-fallback to local-docker
|
|
||||||
CloudRunnerLogger.log('LocalStack endpoints detected; routing provider to local-docker for this run');
|
|
||||||
CloudRunnerLogger.log(
|
|
||||||
'Note: Set AWS_FORCE_PROVIDER=aws-local to validate AWS templates with local-docker execution',
|
|
||||||
);
|
|
||||||
provider = 'local-docker';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store whether we should validate AWS templates (used by aws-local mode)
|
|
||||||
CloudRunner.validateAwsTemplates = validateAwsTemplates;
|
|
||||||
|
|
||||||
switch (provider) {
|
|
||||||
case 'k8s':
|
case 'k8s':
|
||||||
CloudRunner.Provider = new Kubernetes(CloudRunner.buildParameters);
|
CloudRunner.Provider = new Kubernetes(CloudRunner.buildParameters);
|
||||||
break;
|
break;
|
||||||
case 'aws':
|
case 'aws':
|
||||||
CloudRunner.Provider = new AwsBuildPlatform(CloudRunner.buildParameters);
|
CloudRunner.Provider = new AwsBuildPlatform(CloudRunner.buildParameters);
|
||||||
|
|
||||||
// Validate that AWS provider is actually being used when expected
|
|
||||||
if (isLocalStack && forceAwsProvider) {
|
|
||||||
CloudRunnerLogger.log('✓ AWS provider initialized with LocalStack - AWS functionality will be validated');
|
|
||||||
} else if (isLocalStack && !forceAwsProvider) {
|
|
||||||
CloudRunnerLogger.log(
|
|
||||||
'⚠ WARNING: AWS provider was requested but LocalStack detected without AWS_FORCE_PROVIDER',
|
|
||||||
);
|
|
||||||
CloudRunnerLogger.log('⚠ This may cause AWS functionality tests to fail validation');
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 'test':
|
case 'test':
|
||||||
CloudRunner.Provider = new TestCloudRunner();
|
CloudRunner.Provider = new TestCloudRunner();
|
||||||
@@ -153,51 +69,18 @@ class CloudRunner {
|
|||||||
case 'local-system':
|
case 'local-system':
|
||||||
CloudRunner.Provider = new LocalCloudRunner();
|
CloudRunner.Provider = new LocalCloudRunner();
|
||||||
break;
|
break;
|
||||||
case 'local':
|
|
||||||
CloudRunner.Provider = new LocalCloudRunner();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
// Try to load provider using the dynamic loader for unknown providers
|
|
||||||
try {
|
|
||||||
CloudRunner.Provider = await loadProvider(provider, CloudRunner.buildParameters);
|
|
||||||
} catch (error: any) {
|
|
||||||
CloudRunnerLogger.log(`Failed to load provider '${provider}' using dynamic loader: ${error.message}`);
|
|
||||||
CloudRunnerLogger.log('Falling back to local provider...');
|
|
||||||
CloudRunner.Provider = new LocalCloudRunner();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Final validation: Ensure provider matches expectations
|
|
||||||
const finalProviderName = CloudRunner.Provider.constructor.name;
|
|
||||||
if (CloudRunner.buildParameters.providerStrategy === 'aws' && finalProviderName !== 'AWSBuildEnvironment') {
|
|
||||||
CloudRunnerLogger.log(`⚠ WARNING: Expected AWS provider but got ${finalProviderName}`);
|
|
||||||
CloudRunnerLogger.log('⚠ AWS functionality tests may not be validating AWS services correctly');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static async run(buildParameters: BuildParameters, baseImage: string) {
|
static async run(buildParameters: BuildParameters, baseImage: string) {
|
||||||
if (baseImage.includes(`undefined`)) {
|
CloudRunner.setup(buildParameters);
|
||||||
throw new Error(`baseImage is undefined`);
|
|
||||||
}
|
|
||||||
await CloudRunner.setup(buildParameters);
|
|
||||||
|
|
||||||
// When aws-local mode is enabled, validate AWS CloudFormation templates
|
|
||||||
// This ensures AWS templates are correct even when executing via local-docker
|
|
||||||
if (CloudRunner.validateAwsTemplates) {
|
|
||||||
await CloudRunner.validateAwsCloudFormationTemplates();
|
|
||||||
}
|
|
||||||
await CloudRunner.Provider.setupWorkflow(
|
|
||||||
CloudRunner.buildParameters.buildGuid,
|
|
||||||
CloudRunner.buildParameters,
|
|
||||||
CloudRunner.buildParameters.branch,
|
|
||||||
CloudRunner.defaultSecrets,
|
|
||||||
);
|
|
||||||
try {
|
try {
|
||||||
if (buildParameters.maxRetainedWorkspaces > 0) {
|
CloudRunner.githubCheckId = await GitHub.createGitHubCheck(CloudRunner.buildParameters.buildGuid);
|
||||||
CloudRunner.lockedWorkspace = SharedWorkspaceLocking.NewWorkspaceName();
|
|
||||||
|
|
||||||
const result = await SharedWorkspaceLocking.GetLockedWorkspace(
|
if (buildParameters.retainWorkspace) {
|
||||||
|
CloudRunner.lockedWorkspace = `${CloudRunner.retainedWorkspacePrefix}-${CloudRunner.buildParameters.buildGuid}`;
|
||||||
|
|
||||||
|
const result = await SharedWorkspaceLocking.GetOrCreateLockedWorkspace(
|
||||||
CloudRunner.lockedWorkspace,
|
CloudRunner.lockedWorkspace,
|
||||||
CloudRunner.buildParameters.buildGuid,
|
CloudRunner.buildParameters.buildGuid,
|
||||||
CloudRunner.buildParameters,
|
CloudRunner.buildParameters,
|
||||||
@@ -211,133 +94,53 @@ class CloudRunner {
|
|||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
CloudRunnerLogger.log(`Max retained workspaces reached ${buildParameters.maxRetainedWorkspaces}`);
|
CloudRunnerLogger.log(`Max retained workspaces reached ${buildParameters.maxRetainedWorkspaces}`);
|
||||||
buildParameters.maxRetainedWorkspaces = 0;
|
buildParameters.retainWorkspace = false;
|
||||||
CloudRunner.lockedWorkspace = ``;
|
CloudRunner.lockedWorkspace = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await CloudRunner.updateStatusWithBuildParameters();
|
if (!CloudRunner.buildParameters.isCliMode) core.startGroup('Setup shared cloud runner resources');
|
||||||
const output = await new WorkflowCompositionRoot().run(
|
await CloudRunner.Provider.setupWorkflow(
|
||||||
new CloudRunnerStepParameters(
|
CloudRunner.buildParameters.buildGuid,
|
||||||
baseImage,
|
|
||||||
CloudRunner.cloudRunnerEnvironmentVariables,
|
|
||||||
CloudRunner.defaultSecrets,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
await CloudRunner.Provider.cleanupWorkflow(
|
|
||||||
CloudRunner.buildParameters,
|
CloudRunner.buildParameters,
|
||||||
CloudRunner.buildParameters.branch,
|
CloudRunner.buildParameters.branch,
|
||||||
CloudRunner.defaultSecrets,
|
CloudRunner.defaultSecrets,
|
||||||
);
|
);
|
||||||
if (!CloudRunner.buildParameters.isCliMode) core.endGroup();
|
if (!CloudRunner.buildParameters.isCliMode) core.endGroup();
|
||||||
if (buildParameters.asyncWorkflow && this.isCloudRunnerEnvironment && this.isCloudRunnerAsyncEnvironment) {
|
await GitHub.updateGitHubCheck(CloudRunner.buildParameters.buildGuid, CloudRunner.buildParameters.buildGuid);
|
||||||
await GitHub.updateGitHubCheck(CloudRunner.buildParameters.buildGuid, `success`, `success`, `completed`);
|
const output = await new WorkflowCompositionRoot().run(
|
||||||
}
|
new CloudRunnerStepState(baseImage, CloudRunner.cloudRunnerEnvironmentVariables, CloudRunner.defaultSecrets),
|
||||||
|
);
|
||||||
|
if (!CloudRunner.buildParameters.isCliMode) core.startGroup('Cleanup shared cloud runner resources');
|
||||||
|
await CloudRunner.Provider.cleanupWorkflow(
|
||||||
|
CloudRunner.buildParameters.buildGuid,
|
||||||
|
CloudRunner.buildParameters,
|
||||||
|
CloudRunner.buildParameters.branch,
|
||||||
|
CloudRunner.defaultSecrets,
|
||||||
|
);
|
||||||
|
CloudRunnerLogger.log(`Cleanup complete`);
|
||||||
|
if (!CloudRunner.buildParameters.isCliMode) core.endGroup();
|
||||||
|
await GitHub.updateGitHubCheck(CloudRunner.buildParameters.buildGuid, `success`, `success`, `completed`);
|
||||||
|
|
||||||
if (BuildParameters.shouldUseRetainedWorkspaceMode(buildParameters)) {
|
if (CloudRunner.buildParameters.retainWorkspace) {
|
||||||
const workspace = CloudRunner.lockedWorkspace || ``;
|
|
||||||
await SharedWorkspaceLocking.ReleaseWorkspace(
|
await SharedWorkspaceLocking.ReleaseWorkspace(
|
||||||
workspace,
|
CloudRunner.lockedWorkspace || ``,
|
||||||
CloudRunner.buildParameters.buildGuid,
|
CloudRunner.buildParameters.buildGuid,
|
||||||
CloudRunner.buildParameters,
|
CloudRunner.buildParameters,
|
||||||
);
|
);
|
||||||
const isLocked = await SharedWorkspaceLocking.IsWorkspaceLocked(workspace, CloudRunner.buildParameters);
|
CloudRunner.lockedWorkspace = undefined;
|
||||||
if (isLocked) {
|
|
||||||
throw new Error(
|
|
||||||
`still locked after releasing ${await SharedWorkspaceLocking.GetAllLocksForWorkspace(
|
|
||||||
workspace,
|
|
||||||
buildParameters,
|
|
||||||
)}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
CloudRunner.lockedWorkspace = ``;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await GitHub.triggerWorkflowOnComplete(CloudRunner.buildParameters.finalHooks);
|
|
||||||
|
|
||||||
if (buildParameters.constantGarbageCollection) {
|
if (buildParameters.constantGarbageCollection) {
|
||||||
CloudRunner.Provider.garbageCollect(``, true, buildParameters.garbageMaxAge, true, true);
|
CloudRunner.Provider.garbageCollect(``, true, buildParameters.garbageCollectionMaxAge, true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new CloudRunnerResult(buildParameters, output, true, true, false);
|
return output;
|
||||||
} catch (error: any) {
|
} catch (error) {
|
||||||
CloudRunnerLogger.log(JSON.stringify(error, undefined, 4));
|
await GitHub.updateGitHubCheck(CloudRunner.buildParameters.buildGuid, error, `failure`, `completed`);
|
||||||
await GitHub.updateGitHubCheck(
|
|
||||||
CloudRunner.buildParameters.buildGuid,
|
|
||||||
`Failed - Error ${error?.message || error}`,
|
|
||||||
`failure`,
|
|
||||||
`completed`,
|
|
||||||
);
|
|
||||||
if (!CloudRunner.buildParameters.isCliMode) core.endGroup();
|
if (!CloudRunner.buildParameters.isCliMode) core.endGroup();
|
||||||
await CloudRunnerError.handleException(error, CloudRunner.buildParameters, CloudRunner.defaultSecrets);
|
await CloudRunnerError.handleException(error, CloudRunner.buildParameters, CloudRunner.defaultSecrets);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validates AWS CloudFormation templates without deploying them.
|
|
||||||
* Used by aws-local mode to ensure AWS templates are correct when executing via local-docker.
|
|
||||||
* This provides confidence that AWS ECS deployments would work with the generated templates.
|
|
||||||
*/
|
|
||||||
private static async validateAwsCloudFormationTemplates() {
|
|
||||||
CloudRunnerLogger.log('=== AWS CloudFormation Template Validation (aws-local mode) ===');
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Import AWS template formations
|
|
||||||
const { BaseStackFormation } = await import('./providers/aws/cloud-formations/base-stack-formation');
|
|
||||||
const { TaskDefinitionFormation } = await import('./providers/aws/cloud-formations/task-definition-formation');
|
|
||||||
|
|
||||||
// Validate base stack template
|
|
||||||
const baseTemplate = BaseStackFormation.formation;
|
|
||||||
CloudRunnerLogger.log(`✓ Base stack template generated (${baseTemplate.length} chars)`);
|
|
||||||
|
|
||||||
// Check for required resources in base stack
|
|
||||||
const requiredBaseResources = ['AWS::EC2::VPC', 'AWS::ECS::Cluster', 'AWS::S3::Bucket', 'AWS::IAM::Role'];
|
|
||||||
for (const resource of requiredBaseResources) {
|
|
||||||
if (baseTemplate.includes(resource)) {
|
|
||||||
CloudRunnerLogger.log(` ✓ Contains ${resource}`);
|
|
||||||
} else {
|
|
||||||
throw new Error(`Base stack template missing required resource: ${resource}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate task definition template
|
|
||||||
const taskTemplate = TaskDefinitionFormation.formation;
|
|
||||||
CloudRunnerLogger.log(`✓ Task definition template generated (${taskTemplate.length} chars)`);
|
|
||||||
|
|
||||||
// Check for required resources in task definition
|
|
||||||
const requiredTaskResources = ['AWS::ECS::TaskDefinition', 'AWS::Logs::LogGroup'];
|
|
||||||
for (const resource of requiredTaskResources) {
|
|
||||||
if (taskTemplate.includes(resource)) {
|
|
||||||
CloudRunnerLogger.log(` ✓ Contains ${resource}`);
|
|
||||||
} else {
|
|
||||||
throw new Error(`Task definition template missing required resource: ${resource}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate YAML syntax by checking for common patterns
|
|
||||||
if (!baseTemplate.includes('AWSTemplateFormatVersion')) {
|
|
||||||
throw new Error('Base stack template missing AWSTemplateFormatVersion');
|
|
||||||
}
|
|
||||||
if (!taskTemplate.includes('AWSTemplateFormatVersion')) {
|
|
||||||
throw new Error('Task definition template missing AWSTemplateFormatVersion');
|
|
||||||
}
|
|
||||||
|
|
||||||
CloudRunnerLogger.log('=== AWS CloudFormation templates validated successfully ===');
|
|
||||||
CloudRunnerLogger.log('Note: Actual execution will use local-docker provider');
|
|
||||||
} catch (error: any) {
|
|
||||||
CloudRunnerLogger.log(`AWS CloudFormation template validation failed: ${error.message}`);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
export default CloudRunner;
|
export default CloudRunner;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import CloudRunnerLogger from '../services/core/cloud-runner-logger';
|
import CloudRunnerLogger from '../services/cloud-runner-logger';
|
||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
import CloudRunner from '../cloud-runner';
|
import CloudRunner from '../cloud-runner';
|
||||||
import CloudRunnerSecret from '../options/cloud-runner-secret';
|
import CloudRunnerSecret from '../services/cloud-runner-secret';
|
||||||
import BuildParameters from '../../build-parameters';
|
import BuildParameters from '../../build-parameters';
|
||||||
|
|
||||||
export class CloudRunnerError {
|
export class CloudRunnerError {
|
||||||
@@ -9,7 +9,12 @@ 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(buildParameters, buildParameters.branch, secrets);
|
await CloudRunner.Provider.cleanupWorkflow(
|
||||||
|
buildParameters.buildGuid,
|
||||||
|
buildParameters,
|
||||||
|
buildParameters.branch,
|
||||||
|
secrets,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,335 +0,0 @@
|
|||||||
import { Cli } from '../../cli/cli';
|
|
||||||
import CloudRunnerQueryOverride from './cloud-runner-query-override';
|
|
||||||
import GitHub from '../../github';
|
|
||||||
import * as core from '@actions/core';
|
|
||||||
|
|
||||||
class CloudRunnerOptions {
|
|
||||||
// ### ### ###
|
|
||||||
// Input Handling
|
|
||||||
// ### ### ###
|
|
||||||
public static getInput(query: string): string | undefined {
|
|
||||||
if (GitHub.githubInputEnabled) {
|
|
||||||
const coreInput = core.getInput(query);
|
|
||||||
if (coreInput && coreInput !== '') {
|
|
||||||
return coreInput;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const alternativeQuery = CloudRunnerOptions.ToEnvVarFormat(query);
|
|
||||||
|
|
||||||
// Query input sources
|
|
||||||
if (Cli.query(query, alternativeQuery)) {
|
|
||||||
return Cli.query(query, alternativeQuery);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (CloudRunnerQueryOverride.query(query, alternativeQuery)) {
|
|
||||||
return CloudRunnerQueryOverride.query(query, alternativeQuery);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (process.env[query] !== undefined) {
|
|
||||||
return process.env[query];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (alternativeQuery !== query && process.env[alternativeQuery] !== undefined) {
|
|
||||||
return process.env[alternativeQuery];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ToEnvVarFormat(input: string): string {
|
|
||||||
if (input.toUpperCase() === input) {
|
|
||||||
return input;
|
|
||||||
}
|
|
||||||
|
|
||||||
return input
|
|
||||||
.replace(/([A-Z])/g, ' $1')
|
|
||||||
.trim()
|
|
||||||
.toUpperCase()
|
|
||||||
.replace(/ /g, '_');
|
|
||||||
}
|
|
||||||
|
|
||||||
// ### ### ###
|
|
||||||
// Provider parameters
|
|
||||||
// ### ### ###
|
|
||||||
|
|
||||||
static get region(): string {
|
|
||||||
return CloudRunnerOptions.getInput('region') || 'eu-west-2';
|
|
||||||
}
|
|
||||||
|
|
||||||
// ### ### ###
|
|
||||||
// GitHub parameters
|
|
||||||
// ### ### ###
|
|
||||||
static get githubChecks(): boolean {
|
|
||||||
const value = CloudRunnerOptions.getInput('githubChecks');
|
|
||||||
|
|
||||||
return value === `true` || false;
|
|
||||||
}
|
|
||||||
static get githubCheckId(): string {
|
|
||||||
return CloudRunnerOptions.getInput('githubCheckId') || ``;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get githubOwner(): string {
|
|
||||||
return CloudRunnerOptions.getInput('githubOwner') || CloudRunnerOptions.githubRepo?.split(`/`)[0] || '';
|
|
||||||
}
|
|
||||||
|
|
||||||
static get githubRepoName(): string {
|
|
||||||
return CloudRunnerOptions.getInput('githubRepoName') || CloudRunnerOptions.githubRepo?.split(`/`)[1] || '';
|
|
||||||
}
|
|
||||||
|
|
||||||
static get cloudRunnerRepoName(): string {
|
|
||||||
return CloudRunnerOptions.getInput('cloudRunnerRepoName') || 'game-ci/unity-builder';
|
|
||||||
}
|
|
||||||
|
|
||||||
static get cloneDepth(): string {
|
|
||||||
return CloudRunnerOptions.getInput('cloneDepth') || '50';
|
|
||||||
}
|
|
||||||
|
|
||||||
static get finalHooks(): string[] {
|
|
||||||
return CloudRunnerOptions.getInput('finalHooks')?.split(',') || [];
|
|
||||||
}
|
|
||||||
|
|
||||||
// ### ### ###
|
|
||||||
// Git syncronization parameters
|
|
||||||
// ### ### ###
|
|
||||||
|
|
||||||
static get githubRepo(): string | undefined {
|
|
||||||
return CloudRunnerOptions.getInput('GITHUB_REPOSITORY') || CloudRunnerOptions.getInput('GITHUB_REPO') || undefined;
|
|
||||||
}
|
|
||||||
static get branch(): string {
|
|
||||||
if (CloudRunnerOptions.getInput(`GITHUB_REF`)) {
|
|
||||||
return (
|
|
||||||
CloudRunnerOptions.getInput(`GITHUB_REF`)?.replace('refs/', '').replace(`head/`, '').replace(`heads/`, '') || ``
|
|
||||||
);
|
|
||||||
} else if (CloudRunnerOptions.getInput('branch')) {
|
|
||||||
return CloudRunnerOptions.getInput('branch') || ``;
|
|
||||||
} else {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ### ### ###
|
|
||||||
// Cloud Runner parameters
|
|
||||||
// ### ### ###
|
|
||||||
|
|
||||||
static get buildPlatform(): string {
|
|
||||||
const input = CloudRunnerOptions.getInput('buildPlatform');
|
|
||||||
if (input && input !== '') {
|
|
||||||
return input;
|
|
||||||
}
|
|
||||||
if (CloudRunnerOptions.providerStrategy !== 'local') {
|
|
||||||
return 'linux';
|
|
||||||
}
|
|
||||||
|
|
||||||
return process.platform;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get cloudRunnerBranch(): string {
|
|
||||||
return CloudRunnerOptions.getInput('cloudRunnerBranch') || 'main';
|
|
||||||
}
|
|
||||||
|
|
||||||
static get providerStrategy(): string {
|
|
||||||
const provider =
|
|
||||||
CloudRunnerOptions.getInput('cloudRunnerCluster') || CloudRunnerOptions.getInput('providerStrategy');
|
|
||||||
if (Cli.isCliMode) {
|
|
||||||
return provider || 'aws';
|
|
||||||
}
|
|
||||||
|
|
||||||
return provider || 'local';
|
|
||||||
}
|
|
||||||
|
|
||||||
static get containerCpu(): string {
|
|
||||||
return CloudRunnerOptions.getInput('containerCpu') || `1024`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get containerMemory(): string {
|
|
||||||
return CloudRunnerOptions.getInput('containerMemory') || `3072`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get containerNamespace(): string {
|
|
||||||
return CloudRunnerOptions.getInput('containerNamespace') || `default`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get customJob(): string {
|
|
||||||
return CloudRunnerOptions.getInput('customJob') || '';
|
|
||||||
}
|
|
||||||
|
|
||||||
// ### ### ###
|
|
||||||
// Custom commands from files parameters
|
|
||||||
// ### ### ###
|
|
||||||
|
|
||||||
static get containerHookFiles(): string[] {
|
|
||||||
return CloudRunnerOptions.getInput('containerHookFiles')?.split(`,`) || [];
|
|
||||||
}
|
|
||||||
|
|
||||||
static get commandHookFiles(): string[] {
|
|
||||||
return CloudRunnerOptions.getInput('commandHookFiles')?.split(`,`) || [];
|
|
||||||
}
|
|
||||||
|
|
||||||
// ### ### ###
|
|
||||||
// Custom commands from yaml parameters
|
|
||||||
// ### ### ###
|
|
||||||
|
|
||||||
static get commandHooks(): string {
|
|
||||||
return CloudRunnerOptions.getInput('commandHooks') || '';
|
|
||||||
}
|
|
||||||
|
|
||||||
static get postBuildContainerHooks(): string {
|
|
||||||
return CloudRunnerOptions.getInput('postBuildContainerHooks') || '';
|
|
||||||
}
|
|
||||||
|
|
||||||
static get preBuildContainerHooks(): string {
|
|
||||||
return CloudRunnerOptions.getInput('preBuildContainerHooks') || '';
|
|
||||||
}
|
|
||||||
|
|
||||||
// ### ### ###
|
|
||||||
// Input override handling
|
|
||||||
// ### ### ###
|
|
||||||
|
|
||||||
static get pullInputList(): string[] {
|
|
||||||
return CloudRunnerOptions.getInput('pullInputList')?.split(`,`) || [];
|
|
||||||
}
|
|
||||||
|
|
||||||
static get inputPullCommand(): string {
|
|
||||||
const value = CloudRunnerOptions.getInput('inputPullCommand');
|
|
||||||
|
|
||||||
if (value === 'gcp-secret-manager') {
|
|
||||||
return 'gcloud secrets versions access 1 --secret="{0}"';
|
|
||||||
} else if (value === 'aws-secret-manager') {
|
|
||||||
return 'aws secretsmanager get-secret-value --secret-id {0}';
|
|
||||||
}
|
|
||||||
|
|
||||||
return value || '';
|
|
||||||
}
|
|
||||||
|
|
||||||
// ### ### ###
|
|
||||||
// Aws
|
|
||||||
// ### ### ###
|
|
||||||
|
|
||||||
static get awsStackName() {
|
|
||||||
return CloudRunnerOptions.getInput('awsStackName') || 'game-ci';
|
|
||||||
}
|
|
||||||
|
|
||||||
static get awsEndpoint(): string | undefined {
|
|
||||||
return CloudRunnerOptions.getInput('awsEndpoint');
|
|
||||||
}
|
|
||||||
|
|
||||||
static get awsCloudFormationEndpoint(): string | undefined {
|
|
||||||
return CloudRunnerOptions.getInput('awsCloudFormationEndpoint') || CloudRunnerOptions.awsEndpoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get awsEcsEndpoint(): string | undefined {
|
|
||||||
return CloudRunnerOptions.getInput('awsEcsEndpoint') || CloudRunnerOptions.awsEndpoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get awsKinesisEndpoint(): string | undefined {
|
|
||||||
return CloudRunnerOptions.getInput('awsKinesisEndpoint') || CloudRunnerOptions.awsEndpoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get awsCloudWatchLogsEndpoint(): string | undefined {
|
|
||||||
return CloudRunnerOptions.getInput('awsCloudWatchLogsEndpoint') || CloudRunnerOptions.awsEndpoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get awsS3Endpoint(): string | undefined {
|
|
||||||
return CloudRunnerOptions.getInput('awsS3Endpoint') || CloudRunnerOptions.awsEndpoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ### ### ###
|
|
||||||
// Storage
|
|
||||||
// ### ### ###
|
|
||||||
|
|
||||||
static get storageProvider(): string {
|
|
||||||
return CloudRunnerOptions.getInput('storageProvider') || 's3';
|
|
||||||
}
|
|
||||||
|
|
||||||
static get rcloneRemote(): string {
|
|
||||||
return CloudRunnerOptions.getInput('rcloneRemote') || '';
|
|
||||||
}
|
|
||||||
|
|
||||||
// ### ### ###
|
|
||||||
// K8s
|
|
||||||
// ### ### ###
|
|
||||||
|
|
||||||
static get kubeConfig(): string {
|
|
||||||
return CloudRunnerOptions.getInput('kubeConfig') || '';
|
|
||||||
}
|
|
||||||
|
|
||||||
static get kubeVolume(): string {
|
|
||||||
return CloudRunnerOptions.getInput('kubeVolume') || '';
|
|
||||||
}
|
|
||||||
|
|
||||||
static get kubeVolumeSize(): string {
|
|
||||||
return CloudRunnerOptions.getInput('kubeVolumeSize') || '25Gi';
|
|
||||||
}
|
|
||||||
|
|
||||||
static get kubeStorageClass(): string {
|
|
||||||
return CloudRunnerOptions.getInput('kubeStorageClass') || '';
|
|
||||||
}
|
|
||||||
|
|
||||||
// ### ### ###
|
|
||||||
// Caching
|
|
||||||
// ### ### ###
|
|
||||||
|
|
||||||
static get cacheKey(): string {
|
|
||||||
return CloudRunnerOptions.getInput('cacheKey') || CloudRunnerOptions.branch;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ### ### ###
|
|
||||||
// Utility Parameters
|
|
||||||
// ### ### ###
|
|
||||||
|
|
||||||
static get cloudRunnerDebug(): boolean {
|
|
||||||
return (
|
|
||||||
CloudRunnerOptions.getInput(`cloudRunnerTests`) === `true` ||
|
|
||||||
CloudRunnerOptions.getInput(`cloudRunnerDebug`) === `true` ||
|
|
||||||
CloudRunnerOptions.getInput(`cloudRunnerDebugTree`) === `true` ||
|
|
||||||
CloudRunnerOptions.getInput(`cloudRunnerDebugEnv`) === `true` ||
|
|
||||||
false
|
|
||||||
);
|
|
||||||
}
|
|
||||||
static get skipLfs(): boolean {
|
|
||||||
return CloudRunnerOptions.getInput(`skipLfs`) === `true`;
|
|
||||||
}
|
|
||||||
static get skipCache(): boolean {
|
|
||||||
return CloudRunnerOptions.getInput(`skipCache`) === `true`;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static get asyncCloudRunner(): boolean {
|
|
||||||
return CloudRunnerOptions.getInput('asyncCloudRunner') === 'true';
|
|
||||||
}
|
|
||||||
|
|
||||||
public static get resourceTracking(): boolean {
|
|
||||||
return CloudRunnerOptions.getInput('resourceTracking') === 'true';
|
|
||||||
}
|
|
||||||
|
|
||||||
public static get useLargePackages(): boolean {
|
|
||||||
return CloudRunnerOptions.getInput(`useLargePackages`) === `true`;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static get useSharedBuilder(): boolean {
|
|
||||||
return CloudRunnerOptions.getInput(`useSharedBuilder`) === `true`;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static get useCompressionStrategy(): boolean {
|
|
||||||
return CloudRunnerOptions.getInput(`useCompressionStrategy`) === `true`;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static get useCleanupCron(): boolean {
|
|
||||||
return (CloudRunnerOptions.getInput(`useCleanupCron`) || 'true') === 'true';
|
|
||||||
}
|
|
||||||
|
|
||||||
// ### ### ###
|
|
||||||
// Retained Workspace
|
|
||||||
// ### ### ###
|
|
||||||
|
|
||||||
public static get maxRetainedWorkspaces(): string {
|
|
||||||
return CloudRunnerOptions.getInput(`maxRetainedWorkspaces`) || `0`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ### ### ###
|
|
||||||
// Garbage Collection
|
|
||||||
// ### ### ###
|
|
||||||
|
|
||||||
static get garbageMaxAge(): number {
|
|
||||||
return Number(CloudRunnerOptions.getInput(`garbageMaxAge`)) || 24;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default CloudRunnerOptions;
|
|
||||||
@@ -1,222 +0,0 @@
|
|||||||
# Provider Loader Dynamic Imports
|
|
||||||
|
|
||||||
## What is a Provider?
|
|
||||||
|
|
||||||
A **provider** is a pluggable backend that Cloud Runner uses to run builds and workflows. Examples include **AWS**, **Kubernetes**, or local execution. Each provider implements the [ProviderInterface](https://github.com/game-ci/unity-builder/blob/main/src/model/cloud-runner/providers/provider-interface.ts), which defines the common lifecycle methods (setup, run, cleanup, garbage collection, etc.).
|
|
||||||
|
|
||||||
This abstraction makes Cloud Runner flexible: you can switch execution environments or add your own provider (via npm package, GitHub repo, or local path) without changing the rest of your pipeline.
|
|
||||||
|
|
||||||
## Dynamic Provider Loading
|
|
||||||
|
|
||||||
The provider loader now supports dynamic loading of providers from multiple sources including local file paths, GitHub repositories, and NPM packages.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- **Local File Paths**: Load providers from relative or absolute file paths
|
|
||||||
- **GitHub URLs**: Clone and load providers from GitHub repositories with automatic updates
|
|
||||||
- **NPM Packages**: Load providers from installed NPM packages
|
|
||||||
- **Automatic Updates**: GitHub repositories are automatically updated when changes are available
|
|
||||||
- **Caching**: Local caching of cloned repositories for improved performance
|
|
||||||
- **Fallback Support**: Graceful fallback to local provider if loading fails
|
|
||||||
|
|
||||||
## Usage Examples
|
|
||||||
|
|
||||||
### Loading Built-in Providers
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { ProviderLoader } from './provider-loader';
|
|
||||||
|
|
||||||
// Load built-in providers
|
|
||||||
const awsProvider = await ProviderLoader.loadProvider('aws', buildParameters);
|
|
||||||
const k8sProvider = await ProviderLoader.loadProvider('k8s', buildParameters);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Loading Local Providers
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// Load from relative path
|
|
||||||
const localProvider = await ProviderLoader.loadProvider('./my-local-provider', buildParameters);
|
|
||||||
|
|
||||||
// Load from absolute path
|
|
||||||
const absoluteProvider = await ProviderLoader.loadProvider('/path/to/provider', buildParameters);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Loading GitHub Providers
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// Load from GitHub URL
|
|
||||||
const githubProvider = await ProviderLoader.loadProvider(
|
|
||||||
'https://github.com/user/my-provider',
|
|
||||||
buildParameters
|
|
||||||
);
|
|
||||||
|
|
||||||
// Load from specific branch
|
|
||||||
const branchProvider = await ProviderLoader.loadProvider(
|
|
||||||
'https://github.com/user/my-provider/tree/develop',
|
|
||||||
buildParameters
|
|
||||||
);
|
|
||||||
|
|
||||||
// Load from specific path in repository
|
|
||||||
const pathProvider = await ProviderLoader.loadProvider(
|
|
||||||
'https://github.com/user/my-provider/tree/main/src/providers',
|
|
||||||
buildParameters
|
|
||||||
);
|
|
||||||
|
|
||||||
// Shorthand notation
|
|
||||||
const shorthandProvider = await ProviderLoader.loadProvider('user/repo', buildParameters);
|
|
||||||
const branchShorthand = await ProviderLoader.loadProvider('user/repo@develop', buildParameters);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Loading NPM Packages
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// Load from NPM package
|
|
||||||
const npmProvider = await ProviderLoader.loadProvider('my-provider-package', buildParameters);
|
|
||||||
|
|
||||||
// Load from scoped NPM package
|
|
||||||
const scopedProvider = await ProviderLoader.loadProvider('@scope/my-provider', buildParameters);
|
|
||||||
```
|
|
||||||
|
|
||||||
## Provider Interface
|
|
||||||
|
|
||||||
All providers must implement the `ProviderInterface`:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
interface ProviderInterface {
|
|
||||||
cleanupWorkflow(): Promise<void>;
|
|
||||||
setupWorkflow(buildGuid: string, buildParameters: BuildParameters, branchName: string, defaultSecretsArray: any[]): Promise<void>;
|
|
||||||
runTaskInWorkflow(buildGuid: string, task: string, workingDirectory: string, buildVolumeFolder: string, environmentVariables: any[], secrets: any[]): Promise<string>;
|
|
||||||
garbageCollect(): Promise<void>;
|
|
||||||
listResources(): Promise<ProviderResource[]>;
|
|
||||||
listWorkflow(): Promise<ProviderWorkflow[]>;
|
|
||||||
watchWorkflow(): Promise<void>;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Example Provider Implementation
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// my-provider.ts
|
|
||||||
import { ProviderInterface } from './provider-interface';
|
|
||||||
import BuildParameters from './build-parameters';
|
|
||||||
|
|
||||||
export default class MyProvider implements ProviderInterface {
|
|
||||||
constructor(private buildParameters: BuildParameters) {}
|
|
||||||
|
|
||||||
async cleanupWorkflow(): Promise<void> {
|
|
||||||
// Cleanup logic
|
|
||||||
}
|
|
||||||
|
|
||||||
async setupWorkflow(buildGuid: string, buildParameters: BuildParameters, branchName: string, defaultSecretsArray: any[]): Promise<void> {
|
|
||||||
// Setup logic
|
|
||||||
}
|
|
||||||
|
|
||||||
async runTaskInWorkflow(buildGuid: string, task: string, workingDirectory: string, buildVolumeFolder: string, environmentVariables: any[], secrets: any[]): Promise<string> {
|
|
||||||
// Task execution logic
|
|
||||||
return 'Task completed';
|
|
||||||
}
|
|
||||||
|
|
||||||
async garbageCollect(): Promise<void> {
|
|
||||||
// Garbage collection logic
|
|
||||||
}
|
|
||||||
|
|
||||||
async listResources(): Promise<ProviderResource[]> {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
async listWorkflow(): Promise<ProviderWorkflow[]> {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
async watchWorkflow(): Promise<void> {
|
|
||||||
// Watch logic
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Utility Methods
|
|
||||||
|
|
||||||
### Analyze Provider Source
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// Analyze a provider source without loading it
|
|
||||||
const sourceInfo = ProviderLoader.analyzeProviderSource('https://github.com/user/repo');
|
|
||||||
console.log(sourceInfo.type); // 'github'
|
|
||||||
console.log(sourceInfo.owner); // 'user'
|
|
||||||
console.log(sourceInfo.repo); // 'repo'
|
|
||||||
```
|
|
||||||
|
|
||||||
### Clean Up Cache
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// Clean up old cached repositories (older than 30 days)
|
|
||||||
await ProviderLoader.cleanupCache();
|
|
||||||
|
|
||||||
// Clean up repositories older than 7 days
|
|
||||||
await ProviderLoader.cleanupCache(7);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Get Available Providers
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// Get list of built-in providers
|
|
||||||
const providers = ProviderLoader.getAvailableProviders();
|
|
||||||
console.log(providers); // ['aws', 'k8s', 'test', 'local-docker', 'local-system', 'local']
|
|
||||||
```
|
|
||||||
|
|
||||||
## Supported URL Formats
|
|
||||||
|
|
||||||
### GitHub URLs
|
|
||||||
- `https://github.com/user/repo`
|
|
||||||
- `https://github.com/user/repo.git`
|
|
||||||
- `https://github.com/user/repo/tree/branch`
|
|
||||||
- `https://github.com/user/repo/tree/branch/path/to/provider`
|
|
||||||
- `git@github.com:user/repo.git`
|
|
||||||
|
|
||||||
### Shorthand GitHub References
|
|
||||||
- `user/repo`
|
|
||||||
- `user/repo@branch`
|
|
||||||
- `user/repo@branch/path/to/provider`
|
|
||||||
|
|
||||||
### Local Paths
|
|
||||||
- `./relative/path`
|
|
||||||
- `../relative/path`
|
|
||||||
- `/absolute/path`
|
|
||||||
- `C:\\path\\to\\provider` (Windows)
|
|
||||||
|
|
||||||
### NPM Packages
|
|
||||||
- `package-name`
|
|
||||||
- `@scope/package-name`
|
|
||||||
|
|
||||||
## Caching
|
|
||||||
|
|
||||||
GitHub repositories are automatically cached in the `.provider-cache` directory. The cache key is generated based on the repository owner, name, and branch. This ensures that:
|
|
||||||
|
|
||||||
1. Repositories are only cloned once
|
|
||||||
2. Updates are checked and applied automatically
|
|
||||||
3. Performance is improved for repeated loads
|
|
||||||
4. Storage is managed efficiently
|
|
||||||
|
|
||||||
## Error Handling
|
|
||||||
|
|
||||||
The provider loader includes comprehensive error handling:
|
|
||||||
|
|
||||||
- **Missing packages**: Clear error messages when providers cannot be found
|
|
||||||
- **Interface validation**: Ensures providers implement the required interface
|
|
||||||
- **Git operations**: Handles network issues and repository access problems
|
|
||||||
- **Fallback mechanism**: Falls back to local provider if loading fails
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
The provider loader can be configured through environment variables:
|
|
||||||
|
|
||||||
- `PROVIDER_CACHE_DIR`: Custom cache directory (default: `.provider-cache`)
|
|
||||||
- `GIT_TIMEOUT`: Git operation timeout in milliseconds (default: 30000)
|
|
||||||
|
|
||||||
## Best Practices
|
|
||||||
|
|
||||||
1. **Use specific branches or tags**: Always specify the branch or specific tag when loading from GitHub
|
|
||||||
2. **Implement proper error handling**: Wrap provider loading in try-catch blocks
|
|
||||||
3. **Clean up regularly**: Use the cleanup utility to manage cache size
|
|
||||||
4. **Test locally first**: Test providers locally before deploying
|
|
||||||
5. **Use semantic versioning**: Tag your provider repositories for stable versions
|
|
||||||
@@ -1,35 +1,8 @@
|
|||||||
import CloudRunnerLogger from '../../services/core/cloud-runner-logger';
|
import CloudRunnerLogger from '../../services/cloud-runner-logger';
|
||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
import {
|
import * as SDK from 'aws-sdk';
|
||||||
CloudFormation,
|
|
||||||
CreateStackCommand,
|
|
||||||
// eslint-disable-next-line import/named
|
|
||||||
CreateStackCommandInput,
|
|
||||||
DescribeStacksCommand,
|
|
||||||
// eslint-disable-next-line import/named
|
|
||||||
DescribeStacksCommandInput,
|
|
||||||
ListStacksCommand,
|
|
||||||
// eslint-disable-next-line import/named
|
|
||||||
Parameter,
|
|
||||||
UpdateStackCommand,
|
|
||||||
// eslint-disable-next-line import/named
|
|
||||||
UpdateStackCommandInput,
|
|
||||||
waitUntilStackCreateComplete,
|
|
||||||
waitUntilStackUpdateComplete,
|
|
||||||
} from '@aws-sdk/client-cloudformation';
|
|
||||||
import { BaseStackFormation } from './cloud-formations/base-stack-formation';
|
import { BaseStackFormation } from './cloud-formations/base-stack-formation';
|
||||||
import crypto from 'node:crypto';
|
const crypto = require('crypto');
|
||||||
|
|
||||||
const DEFAULT_STACK_WAIT_TIME_SECONDS = 600;
|
|
||||||
|
|
||||||
function getStackWaitTime(): number {
|
|
||||||
const overrideValue = Number(process.env.CLOUD_RUNNER_AWS_STACK_WAIT_TIME ?? '');
|
|
||||||
if (!Number.isNaN(overrideValue) && overrideValue > 0) {
|
|
||||||
return overrideValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return DEFAULT_STACK_WAIT_TIME_SECONDS;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class AWSBaseStack {
|
export class AWSBaseStack {
|
||||||
constructor(baseStackName: string) {
|
constructor(baseStackName: string) {
|
||||||
@@ -37,72 +10,52 @@ export class AWSBaseStack {
|
|||||||
}
|
}
|
||||||
private baseStackName: string;
|
private baseStackName: string;
|
||||||
|
|
||||||
async setupBaseStack(CF: CloudFormation) {
|
async setupBaseStack(CF: SDK.CloudFormation) {
|
||||||
const baseStackName = this.baseStackName;
|
const baseStackName = this.baseStackName;
|
||||||
const stackWaitTimeSeconds = getStackWaitTime();
|
|
||||||
|
|
||||||
const baseStack = BaseStackFormation.formation;
|
const baseStack = BaseStackFormation.formation;
|
||||||
|
|
||||||
// Cloud Formation Input
|
// Cloud Formation Input
|
||||||
const describeStackInput: DescribeStacksCommandInput = {
|
const describeStackInput: SDK.CloudFormation.DescribeStacksInput = {
|
||||||
StackName: baseStackName,
|
StackName: baseStackName,
|
||||||
};
|
};
|
||||||
const parametersWithoutHash: Parameter[] = [{ ParameterKey: 'EnvironmentName', ParameterValue: baseStackName }];
|
const parametersWithoutHash: SDK.CloudFormation.Parameter[] = [
|
||||||
|
{ ParameterKey: 'EnvironmentName', ParameterValue: baseStackName },
|
||||||
|
];
|
||||||
const parametersHash = crypto
|
const parametersHash = crypto
|
||||||
.createHash('md5')
|
.createHash('md5')
|
||||||
.update(baseStack + JSON.stringify(parametersWithoutHash))
|
.update(baseStack + JSON.stringify(parametersWithoutHash))
|
||||||
.digest('hex');
|
.digest('hex');
|
||||||
const parameters: Parameter[] = [
|
const parameters: SDK.CloudFormation.Parameter[] = [
|
||||||
...parametersWithoutHash,
|
...parametersWithoutHash,
|
||||||
...[{ ParameterKey: 'Version', ParameterValue: parametersHash }],
|
...[{ ParameterKey: 'Version', ParameterValue: parametersHash }],
|
||||||
];
|
];
|
||||||
const updateInput: UpdateStackCommandInput = {
|
const updateInput: SDK.CloudFormation.UpdateStackInput = {
|
||||||
StackName: baseStackName,
|
StackName: baseStackName,
|
||||||
TemplateBody: baseStack,
|
TemplateBody: baseStack,
|
||||||
Parameters: parameters,
|
Parameters: parameters,
|
||||||
Capabilities: ['CAPABILITY_IAM'],
|
Capabilities: ['CAPABILITY_IAM'],
|
||||||
};
|
};
|
||||||
const createStackInput: CreateStackCommandInput = {
|
const createStackInput: SDK.CloudFormation.CreateStackInput = {
|
||||||
StackName: baseStackName,
|
StackName: baseStackName,
|
||||||
TemplateBody: baseStack,
|
TemplateBody: baseStack,
|
||||||
Parameters: parameters,
|
Parameters: parameters,
|
||||||
Capabilities: ['CAPABILITY_IAM'],
|
Capabilities: ['CAPABILITY_IAM'],
|
||||||
};
|
};
|
||||||
|
|
||||||
const stacks = await CF.send(
|
const stacks = await CF.listStacks({
|
||||||
new ListStacksCommand({
|
StackStatusFilter: ['UPDATE_COMPLETE', 'CREATE_COMPLETE', 'ROLLBACK_COMPLETE'],
|
||||||
StackStatusFilter: [
|
}).promise();
|
||||||
'CREATE_IN_PROGRESS',
|
|
||||||
'UPDATE_IN_PROGRESS',
|
|
||||||
'UPDATE_COMPLETE',
|
|
||||||
'CREATE_COMPLETE',
|
|
||||||
'ROLLBACK_COMPLETE',
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
const stackNames = stacks.StackSummaries?.map((x) => x.StackName) || [];
|
const stackNames = stacks.StackSummaries?.map((x) => x.StackName) || [];
|
||||||
const stackExists: boolean = stackNames.includes(baseStackName);
|
const stackExists: Boolean = stackNames.includes(baseStackName) || false;
|
||||||
const describeStack = async () => {
|
const describeStack = async () => {
|
||||||
return await CF.send(new DescribeStacksCommand(describeStackInput));
|
return await CF.describeStacks(describeStackInput).promise();
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
if (!stackExists) {
|
if (!stackExists) {
|
||||||
CloudRunnerLogger.log(`${baseStackName} stack does not exist (${JSON.stringify(stackNames)})`);
|
CloudRunnerLogger.log(`${baseStackName} stack does not exist (${JSON.stringify(stackNames)})`);
|
||||||
let created = false;
|
await CF.createStack(createStackInput).promise();
|
||||||
try {
|
CloudRunnerLogger.log(`created stack (version: ${parametersHash})`);
|
||||||
await CF.send(new CreateStackCommand(createStackInput));
|
|
||||||
created = true;
|
|
||||||
} catch (error: any) {
|
|
||||||
const message = `${error?.name ?? ''} ${error?.message ?? ''}`;
|
|
||||||
if (message.includes('AlreadyExistsException')) {
|
|
||||||
CloudRunnerLogger.log(`Base stack already exists, continuing with describe`);
|
|
||||||
} else {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (created) {
|
|
||||||
CloudRunnerLogger.log(`created stack (version: ${parametersHash})`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
const CFState = await describeStack();
|
const CFState = await describeStack();
|
||||||
let stack = CFState.Stacks?.[0];
|
let stack = CFState.Stacks?.[0];
|
||||||
@@ -112,16 +65,7 @@ export class AWSBaseStack {
|
|||||||
const stackVersion = stack.Parameters?.find((x) => x.ParameterKey === 'Version')?.ParameterValue;
|
const stackVersion = stack.Parameters?.find((x) => x.ParameterKey === 'Version')?.ParameterValue;
|
||||||
|
|
||||||
if (stack.StackStatus === 'CREATE_IN_PROGRESS') {
|
if (stack.StackStatus === 'CREATE_IN_PROGRESS') {
|
||||||
CloudRunnerLogger.log(
|
await CF.waitFor('stackCreateComplete', describeStackInput).promise();
|
||||||
`Waiting up to ${stackWaitTimeSeconds}s for '${baseStackName}' CloudFormation creation to finish`,
|
|
||||||
);
|
|
||||||
await waitUntilStackCreateComplete(
|
|
||||||
{
|
|
||||||
client: CF,
|
|
||||||
maxWaitTime: stackWaitTimeSeconds,
|
|
||||||
},
|
|
||||||
describeStackInput,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stackExists) {
|
if (stackExists) {
|
||||||
@@ -129,7 +73,7 @@ export class AWSBaseStack {
|
|||||||
if (parametersHash !== stackVersion) {
|
if (parametersHash !== stackVersion) {
|
||||||
CloudRunnerLogger.log(`Attempting update of base stack`);
|
CloudRunnerLogger.log(`Attempting update of base stack`);
|
||||||
try {
|
try {
|
||||||
await CF.send(new UpdateStackCommand(updateInput));
|
await CF.updateStack(updateInput).promise();
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
if (error['message'].includes('No updates are to be performed')) {
|
if (error['message'].includes('No updates are to be performed')) {
|
||||||
CloudRunnerLogger.log(`No updates are to be performed`);
|
CloudRunnerLogger.log(`No updates are to be performed`);
|
||||||
@@ -149,16 +93,7 @@ export class AWSBaseStack {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (stack.StackStatus === 'UPDATE_IN_PROGRESS') {
|
if (stack.StackStatus === 'UPDATE_IN_PROGRESS') {
|
||||||
CloudRunnerLogger.log(
|
await CF.waitFor('stackUpdateComplete', describeStackInput).promise();
|
||||||
`Waiting up to ${stackWaitTimeSeconds}s for '${baseStackName}' CloudFormation update to finish`,
|
|
||||||
);
|
|
||||||
await waitUntilStackUpdateComplete(
|
|
||||||
{
|
|
||||||
client: CF,
|
|
||||||
maxWaitTime: stackWaitTimeSeconds,
|
|
||||||
},
|
|
||||||
describeStackInput,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CloudRunnerLogger.log('base stack is now ready');
|
CloudRunnerLogger.log('base stack is now ready');
|
||||||
|
|||||||
@@ -1,93 +0,0 @@
|
|||||||
import { CloudFormation } from '@aws-sdk/client-cloudformation';
|
|
||||||
import { ECS } from '@aws-sdk/client-ecs';
|
|
||||||
import { Kinesis } from '@aws-sdk/client-kinesis';
|
|
||||||
import { CloudWatchLogs } from '@aws-sdk/client-cloudwatch-logs';
|
|
||||||
import { S3 } from '@aws-sdk/client-s3';
|
|
||||||
import { Input } from '../../..';
|
|
||||||
import CloudRunnerOptions from '../../options/cloud-runner-options';
|
|
||||||
|
|
||||||
export class AwsClientFactory {
|
|
||||||
private static cloudFormation: CloudFormation;
|
|
||||||
private static ecs: ECS;
|
|
||||||
private static kinesis: Kinesis;
|
|
||||||
private static cloudWatchLogs: CloudWatchLogs;
|
|
||||||
private static s3: S3;
|
|
||||||
|
|
||||||
private static getCredentials() {
|
|
||||||
// Explicitly provide credentials from environment variables for LocalStack compatibility
|
|
||||||
// LocalStack accepts any credentials, but the AWS SDK needs them to be explicitly set
|
|
||||||
const accessKeyId = process.env.AWS_ACCESS_KEY_ID;
|
|
||||||
const secretAccessKey = process.env.AWS_SECRET_ACCESS_KEY;
|
|
||||||
|
|
||||||
if (accessKeyId && secretAccessKey) {
|
|
||||||
return {
|
|
||||||
accessKeyId,
|
|
||||||
secretAccessKey,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return undefined to let AWS SDK use default credential chain
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
static getCloudFormation(): CloudFormation {
|
|
||||||
if (!this.cloudFormation) {
|
|
||||||
this.cloudFormation = new CloudFormation({
|
|
||||||
region: Input.region,
|
|
||||||
endpoint: CloudRunnerOptions.awsCloudFormationEndpoint,
|
|
||||||
credentials: AwsClientFactory.getCredentials(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.cloudFormation;
|
|
||||||
}
|
|
||||||
|
|
||||||
static getECS(): ECS {
|
|
||||||
if (!this.ecs) {
|
|
||||||
this.ecs = new ECS({
|
|
||||||
region: Input.region,
|
|
||||||
endpoint: CloudRunnerOptions.awsEcsEndpoint,
|
|
||||||
credentials: AwsClientFactory.getCredentials(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.ecs;
|
|
||||||
}
|
|
||||||
|
|
||||||
static getKinesis(): Kinesis {
|
|
||||||
if (!this.kinesis) {
|
|
||||||
this.kinesis = new Kinesis({
|
|
||||||
region: Input.region,
|
|
||||||
endpoint: CloudRunnerOptions.awsKinesisEndpoint,
|
|
||||||
credentials: AwsClientFactory.getCredentials(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.kinesis;
|
|
||||||
}
|
|
||||||
|
|
||||||
static getCloudWatchLogs(): CloudWatchLogs {
|
|
||||||
if (!this.cloudWatchLogs) {
|
|
||||||
this.cloudWatchLogs = new CloudWatchLogs({
|
|
||||||
region: Input.region,
|
|
||||||
endpoint: CloudRunnerOptions.awsCloudWatchLogsEndpoint,
|
|
||||||
credentials: AwsClientFactory.getCredentials(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.cloudWatchLogs;
|
|
||||||
}
|
|
||||||
|
|
||||||
static getS3(): S3 {
|
|
||||||
if (!this.s3) {
|
|
||||||
this.s3 = new S3({
|
|
||||||
region: Input.region,
|
|
||||||
endpoint: CloudRunnerOptions.awsS3Endpoint,
|
|
||||||
forcePathStyle: true,
|
|
||||||
credentials: AwsClientFactory.getCredentials(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.s3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { TaskDefinitionFormation } from './cloud-formations/task-definition-formation';
|
import { TaskDefinitionFormation } from './cloud-formations/task-definition-formation';
|
||||||
|
|
||||||
export class AWSCloudFormationTemplates {
|
export class AWSCloudFormationTemplates {
|
||||||
public static getParameterTemplate(p1: string) {
|
public static getParameterTemplate(p1) {
|
||||||
return `
|
return `
|
||||||
${p1}:
|
${p1}:
|
||||||
Type: String
|
Type: String
|
||||||
@@ -9,7 +9,7 @@ export class AWSCloudFormationTemplates {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static getSecretTemplate(p1: string) {
|
public static getSecretTemplate(p1) {
|
||||||
return `
|
return `
|
||||||
${p1}Secret:
|
${p1}Secret:
|
||||||
Type: AWS::SecretsManager::Secret
|
Type: AWS::SecretsManager::Secret
|
||||||
@@ -19,15 +19,14 @@ export class AWSCloudFormationTemplates {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static getSecretDefinitionTemplate(p1: string, p2: string) {
|
public static getSecretDefinitionTemplate(p1, p2) {
|
||||||
return `
|
return `
|
||||||
Secrets:
|
|
||||||
- Name: '${p1}'
|
- Name: '${p1}'
|
||||||
ValueFrom: !Ref ${p2}Secret
|
ValueFrom: !Ref ${p2}Secret
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static insertAtTemplate(template: string, insertionKey: string, insertion: string) {
|
public static insertAtTemplate(template, insertionKey, insertion) {
|
||||||
const index = template.search(insertionKey) + insertionKey.length + '\n'.length;
|
const index = template.search(insertionKey) + insertionKey.length + '\n'.length;
|
||||||
template = [template.slice(0, index), insertion, template.slice(index)].join('');
|
template = [template.slice(0, index), insertion, template.slice(index)].join('');
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import CloudRunnerLogger from '../../services/core/cloud-runner-logger';
|
import CloudRunnerLogger from '../../services/cloud-runner-logger';
|
||||||
import { CloudFormation, DescribeStackEventsCommand } from '@aws-sdk/client-cloudformation';
|
import * as SDK from 'aws-sdk';
|
||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
import CloudRunner from '../../cloud-runner';
|
import CloudRunner from '../../cloud-runner';
|
||||||
|
|
||||||
export class AWSError {
|
export class AWSError {
|
||||||
static async handleStackCreationFailure(error: any, CF: CloudFormation, taskDefStackName: string) {
|
static async handleStackCreationFailure(error: any, CF: SDK.CloudFormation, taskDefStackName: string) {
|
||||||
CloudRunnerLogger.log('aws error: ');
|
CloudRunnerLogger.log('aws error: ');
|
||||||
core.error(JSON.stringify(error, undefined, 4));
|
core.error(JSON.stringify(error, undefined, 4));
|
||||||
if (CloudRunner.buildParameters.cloudRunnerDebug) {
|
if (CloudRunner.buildParameters.cloudRunnerDebug) {
|
||||||
CloudRunnerLogger.log('Getting events and resources for task stack');
|
CloudRunnerLogger.log('Getting events and resources for task stack');
|
||||||
const events = (await CF.send(new DescribeStackEventsCommand({ StackName: taskDefStackName }))).StackEvents;
|
const events = (await CF.describeStackEvents({ StackName: taskDefStackName }).promise()).StackEvents;
|
||||||
CloudRunnerLogger.log(JSON.stringify(events, undefined, 4));
|
CloudRunnerLogger.log(JSON.stringify(events, undefined, 4));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,34 +1,14 @@
|
|||||||
import {
|
import * as SDK from 'aws-sdk';
|
||||||
CloudFormation,
|
|
||||||
CreateStackCommand,
|
|
||||||
// eslint-disable-next-line import/named
|
|
||||||
CreateStackCommandInput,
|
|
||||||
DescribeStackResourcesCommand,
|
|
||||||
DescribeStacksCommand,
|
|
||||||
ListStacksCommand,
|
|
||||||
waitUntilStackCreateComplete,
|
|
||||||
} from '@aws-sdk/client-cloudformation';
|
|
||||||
import CloudRunnerAWSTaskDef from './cloud-runner-aws-task-def';
|
import CloudRunnerAWSTaskDef from './cloud-runner-aws-task-def';
|
||||||
import CloudRunnerSecret from '../../options/cloud-runner-secret';
|
import CloudRunnerSecret from '../../services/cloud-runner-secret';
|
||||||
import { AWSCloudFormationTemplates } from './aws-cloud-formation-templates';
|
import { AWSCloudFormationTemplates } from './aws-cloud-formation-templates';
|
||||||
import CloudRunnerLogger from '../../services/core/cloud-runner-logger';
|
import CloudRunnerLogger from '../../services/cloud-runner-logger';
|
||||||
import { AWSError } from './aws-error';
|
import { AWSError } from './aws-error';
|
||||||
import CloudRunner from '../../cloud-runner';
|
import CloudRunner from '../../cloud-runner';
|
||||||
import { CleanupCronFormation } from './cloud-formations/cleanup-cron-formation';
|
import { CleanupCronFormation } from './cloud-formations/cleanup-cron-formation';
|
||||||
import CloudRunnerOptions from '../../options/cloud-runner-options';
|
import CloudRunnerOptions from '../../cloud-runner-options';
|
||||||
import { TaskDefinitionFormation } from './cloud-formations/task-definition-formation';
|
import { TaskDefinitionFormation } from './cloud-formations/task-definition-formation';
|
||||||
|
|
||||||
const DEFAULT_STACK_WAIT_TIME_SECONDS = 600;
|
|
||||||
|
|
||||||
function getStackWaitTime(): number {
|
|
||||||
const overrideValue = Number(process.env.CLOUD_RUNNER_AWS_STACK_WAIT_TIME ?? '');
|
|
||||||
if (!Number.isNaN(overrideValue) && overrideValue > 0) {
|
|
||||||
return overrideValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return DEFAULT_STACK_WAIT_TIME_SECONDS;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class AWSJobStack {
|
export class AWSJobStack {
|
||||||
private baseStackName: string;
|
private baseStackName: string;
|
||||||
constructor(baseStackName: string) {
|
constructor(baseStackName: string) {
|
||||||
@@ -36,7 +16,7 @@ export class AWSJobStack {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async setupCloudFormations(
|
public async setupCloudFormations(
|
||||||
CF: CloudFormation,
|
CF: SDK.CloudFormation,
|
||||||
buildGuid: string,
|
buildGuid: string,
|
||||||
image: string,
|
image: string,
|
||||||
entrypoint: string[],
|
entrypoint: string[],
|
||||||
@@ -47,19 +27,21 @@ export class AWSJobStack {
|
|||||||
): Promise<CloudRunnerAWSTaskDef> {
|
): Promise<CloudRunnerAWSTaskDef> {
|
||||||
const taskDefStackName = `${this.baseStackName}-${buildGuid}`;
|
const taskDefStackName = `${this.baseStackName}-${buildGuid}`;
|
||||||
let taskDefCloudFormation = AWSCloudFormationTemplates.readTaskCloudFormationTemplate();
|
let taskDefCloudFormation = AWSCloudFormationTemplates.readTaskCloudFormationTemplate();
|
||||||
|
const cpu = CloudRunner.buildParameters.cloudRunnerCpu || '1024';
|
||||||
|
const memory = CloudRunner.buildParameters.cloudRunnerMemory || '3072';
|
||||||
taskDefCloudFormation = taskDefCloudFormation.replace(
|
taskDefCloudFormation = taskDefCloudFormation.replace(
|
||||||
`ContainerCpu:
|
`ContainerCpu:
|
||||||
Default: 1024`,
|
Default: 1024`,
|
||||||
`ContainerCpu:
|
`ContainerCpu:
|
||||||
Default: ${Number.parseInt(CloudRunner.buildParameters.containerCpu)}`,
|
Default: ${Number.parseInt(cpu)}`,
|
||||||
);
|
);
|
||||||
taskDefCloudFormation = taskDefCloudFormation.replace(
|
taskDefCloudFormation = taskDefCloudFormation.replace(
|
||||||
`ContainerMemory:
|
`ContainerMemory:
|
||||||
Default: 2048`,
|
Default: 2048`,
|
||||||
`ContainerMemory:
|
`ContainerMemory:
|
||||||
Default: ${Number.parseInt(CloudRunner.buildParameters.containerMemory)}`,
|
Default: ${Number.parseInt(memory)}`,
|
||||||
);
|
);
|
||||||
if (!CloudRunnerOptions.asyncCloudRunner) {
|
if (CloudRunnerOptions.watchCloudRunnerToEnd) {
|
||||||
taskDefCloudFormation = AWSCloudFormationTemplates.insertAtTemplate(
|
taskDefCloudFormation = AWSCloudFormationTemplates.insertAtTemplate(
|
||||||
taskDefCloudFormation,
|
taskDefCloudFormation,
|
||||||
'# template resources logstream',
|
'# template resources logstream',
|
||||||
@@ -134,12 +116,12 @@ export class AWSJobStack {
|
|||||||
...secretsMappedToCloudFormationParameters,
|
...secretsMappedToCloudFormationParameters,
|
||||||
];
|
];
|
||||||
CloudRunnerLogger.log(
|
CloudRunnerLogger.log(
|
||||||
`Starting AWS job with memory: ${CloudRunner.buildParameters.containerMemory} cpu: ${CloudRunner.buildParameters.containerCpu}`,
|
`Starting AWS job with memory: ${CloudRunner.buildParameters.cloudRunnerMemory} cpu: ${CloudRunner.buildParameters.cloudRunnerCpu}`,
|
||||||
);
|
);
|
||||||
let previousStackExists = true;
|
let previousStackExists = true;
|
||||||
while (previousStackExists) {
|
while (previousStackExists) {
|
||||||
previousStackExists = false;
|
previousStackExists = false;
|
||||||
const stacks = await CF.send(new ListStacksCommand({}));
|
const stacks = await CF.listStacks().promise();
|
||||||
if (!stacks.StackSummaries) {
|
if (!stacks.StackSummaries) {
|
||||||
throw new Error('Faild to get stacks');
|
throw new Error('Faild to get stacks');
|
||||||
}
|
}
|
||||||
@@ -152,37 +134,23 @@ export class AWSJobStack {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const createStackInput: CreateStackCommandInput = {
|
const createStackInput: SDK.CloudFormation.CreateStackInput = {
|
||||||
StackName: taskDefStackName,
|
StackName: taskDefStackName,
|
||||||
TemplateBody: taskDefCloudFormation,
|
TemplateBody: taskDefCloudFormation,
|
||||||
Capabilities: ['CAPABILITY_IAM'],
|
Capabilities: ['CAPABILITY_IAM'],
|
||||||
Parameters: parameters,
|
Parameters: parameters,
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const stackWaitTimeSeconds = getStackWaitTime();
|
CloudRunnerLogger.log(`Creating job aws formation ${taskDefStackName}`);
|
||||||
CloudRunnerLogger.log(
|
await CF.createStack(createStackInput).promise();
|
||||||
`Creating job aws formation ${taskDefStackName} (waiting up to ${stackWaitTimeSeconds}s for completion)`,
|
await CF.waitFor('stackCreateComplete', { StackName: taskDefStackName }).promise();
|
||||||
);
|
|
||||||
await CF.send(new CreateStackCommand(createStackInput));
|
|
||||||
await waitUntilStackCreateComplete(
|
|
||||||
{
|
|
||||||
client: CF,
|
|
||||||
maxWaitTime: stackWaitTimeSeconds,
|
|
||||||
},
|
|
||||||
{ StackName: taskDefStackName },
|
|
||||||
);
|
|
||||||
const describeStack = await CF.send(new DescribeStacksCommand({ StackName: taskDefStackName }));
|
|
||||||
for (const parameter of parameters) {
|
|
||||||
if (!describeStack.Stacks?.[0].Parameters?.some((x) => x.ParameterKey === parameter.ParameterKey)) {
|
|
||||||
throw new Error(`Parameter ${parameter.ParameterKey} not found in stack`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await AWSError.handleStackCreationFailure(error, CF, taskDefStackName);
|
await AWSError.handleStackCreationFailure(error, CF, taskDefStackName);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
const createCleanupStackInput: CreateStackCommandInput = {
|
const createCleanupStackInput: SDK.CloudFormation.CreateStackInput = {
|
||||||
StackName: `${taskDefStackName}-cleanup`,
|
StackName: `${taskDefStackName}-cleanup`,
|
||||||
TemplateBody: CleanupCronFormation.formation,
|
TemplateBody: CleanupCronFormation.formation,
|
||||||
Capabilities: ['CAPABILITY_IAM'],
|
Capabilities: ['CAPABILITY_IAM'],
|
||||||
@@ -212,7 +180,7 @@ export class AWSJobStack {
|
|||||||
if (CloudRunnerOptions.useCleanupCron) {
|
if (CloudRunnerOptions.useCleanupCron) {
|
||||||
try {
|
try {
|
||||||
CloudRunnerLogger.log(`Creating job cleanup formation`);
|
CloudRunnerLogger.log(`Creating job cleanup formation`);
|
||||||
await CF.send(new CreateStackCommand(createCleanupStackInput));
|
CF.createStack(createCleanupStackInput).promise();
|
||||||
|
|
||||||
// await CF.waitFor('stackCreateComplete', { StackName: createCleanupStackInput.StackName }).promise();
|
// await CF.waitFor('stackCreateComplete', { StackName: createCleanupStackInput.StackName }).promise();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -222,15 +190,12 @@ export class AWSJobStack {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const taskDefResources = (
|
const taskDefResources = (
|
||||||
await CF.send(
|
await CF.describeStackResources({
|
||||||
new DescribeStackResourcesCommand({
|
StackName: taskDefStackName,
|
||||||
StackName: taskDefStackName,
|
}).promise()
|
||||||
}),
|
|
||||||
)
|
|
||||||
).StackResources;
|
).StackResources;
|
||||||
|
|
||||||
const baseResources = (await CF.send(new DescribeStackResourcesCommand({ StackName: this.baseStackName })))
|
const baseResources = (await CF.describeStackResources({ StackName: this.baseStackName }).promise()).StackResources;
|
||||||
.StackResources;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
taskDefStackName,
|
taskDefStackName,
|
||||||
|
|||||||
@@ -1,58 +1,20 @@
|
|||||||
import { DescribeTasksCommand, RunTaskCommand, waitUntilTasksRunning } from '@aws-sdk/client-ecs';
|
import * as AWS from 'aws-sdk';
|
||||||
import { DescribeStreamCommand, GetRecordsCommand, GetShardIteratorCommand } from '@aws-sdk/client-kinesis';
|
import CloudRunnerEnvironmentVariable from '../../services/cloud-runner-environment-variable';
|
||||||
import CloudRunnerEnvironmentVariable from '../../options/cloud-runner-environment-variable';
|
|
||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
import CloudRunnerAWSTaskDef from './cloud-runner-aws-task-def';
|
import CloudRunnerAWSTaskDef from './cloud-runner-aws-task-def';
|
||||||
import * as zlib from 'node:zlib';
|
import * as zlib from 'zlib';
|
||||||
import CloudRunnerLogger from '../../services/core/cloud-runner-logger';
|
import CloudRunnerLogger from '../../services/cloud-runner-logger';
|
||||||
import { Input } from '../../..';
|
import { Input } from '../../..';
|
||||||
import CloudRunner from '../../cloud-runner';
|
import CloudRunner from '../../cloud-runner';
|
||||||
import { CommandHookService } from '../../services/hooks/command-hook-service';
|
import { CloudRunnerCustomHooks } from '../../services/cloud-runner-custom-hooks';
|
||||||
import { FollowLogStreamService } from '../../services/core/follow-log-stream-service';
|
import { FollowLogStreamService } from '../../services/follow-log-stream-service';
|
||||||
import CloudRunnerOptions from '../../options/cloud-runner-options';
|
import CloudRunnerOptions from '../../cloud-runner-options';
|
||||||
import GitHub from '../../../github';
|
import GitHub from '../../../github';
|
||||||
import { AwsClientFactory } from './aws-client-factory';
|
|
||||||
|
|
||||||
class AWSTaskRunner {
|
class AWSTaskRunner {
|
||||||
|
public static ECS: AWS.ECS;
|
||||||
|
public static Kinesis: AWS.Kinesis;
|
||||||
private static readonly encodedUnderscore = `$252F`;
|
private static readonly encodedUnderscore = `$252F`;
|
||||||
|
|
||||||
/**
|
|
||||||
* Transform localhost endpoints to host.docker.internal for container environments.
|
|
||||||
* When LocalStack is used, ECS tasks run in Docker containers that need to reach
|
|
||||||
* LocalStack on the host machine via host.docker.internal.
|
|
||||||
*/
|
|
||||||
private static transformEndpointsForContainer(
|
|
||||||
environment: CloudRunnerEnvironmentVariable[],
|
|
||||||
): CloudRunnerEnvironmentVariable[] {
|
|
||||||
const endpointEnvironmentNames = new Set([
|
|
||||||
'AWS_S3_ENDPOINT',
|
|
||||||
'AWS_ENDPOINT',
|
|
||||||
'AWS_CLOUD_FORMATION_ENDPOINT',
|
|
||||||
'AWS_ECS_ENDPOINT',
|
|
||||||
'AWS_KINESIS_ENDPOINT',
|
|
||||||
'AWS_CLOUD_WATCH_LOGS_ENDPOINT',
|
|
||||||
'INPUT_AWSS3ENDPOINT',
|
|
||||||
'INPUT_AWSENDPOINT',
|
|
||||||
]);
|
|
||||||
|
|
||||||
return environment.map((x) => {
|
|
||||||
let value = x.value;
|
|
||||||
if (
|
|
||||||
typeof value === 'string' &&
|
|
||||||
endpointEnvironmentNames.has(x.name) &&
|
|
||||||
(value.startsWith('http://localhost') || value.startsWith('http://127.0.0.1'))
|
|
||||||
) {
|
|
||||||
// Replace localhost with host.docker.internal so ECS containers can access host services
|
|
||||||
value = value
|
|
||||||
.replace('http://localhost', 'http://host.docker.internal')
|
|
||||||
.replace('http://127.0.0.1', 'http://host.docker.internal');
|
|
||||||
CloudRunnerLogger.log(`AWS TaskRunner: Replaced localhost with host.docker.internal for ${x.name}: ${value}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return { name: x.name, value };
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static async runTask(
|
static async runTask(
|
||||||
taskDef: CloudRunnerAWSTaskDef,
|
taskDef: CloudRunnerAWSTaskDef,
|
||||||
environment: CloudRunnerEnvironmentVariable[],
|
environment: CloudRunnerEnvironmentVariable[],
|
||||||
@@ -70,10 +32,7 @@ class AWSTaskRunner {
|
|||||||
const streamName =
|
const streamName =
|
||||||
taskDef.taskDefResources?.find((x) => x.LogicalResourceId === 'KinesisStream')?.PhysicalResourceId || '';
|
taskDef.taskDefResources?.find((x) => x.LogicalResourceId === 'KinesisStream')?.PhysicalResourceId || '';
|
||||||
|
|
||||||
// Transform localhost endpoints for container environment
|
const task = await AWSTaskRunner.ECS.runTask({
|
||||||
const transformedEnvironment = AWSTaskRunner.transformEndpointsForContainer(environment);
|
|
||||||
|
|
||||||
const runParameters = {
|
|
||||||
cluster,
|
cluster,
|
||||||
taskDefinition,
|
taskDefinition,
|
||||||
platformVersion: '1.4.0',
|
platformVersion: '1.4.0',
|
||||||
@@ -81,8 +40,8 @@ class AWSTaskRunner {
|
|||||||
containerOverrides: [
|
containerOverrides: [
|
||||||
{
|
{
|
||||||
name: taskDef.taskDefStackName,
|
name: taskDef.taskDefStackName,
|
||||||
environment: transformedEnvironment,
|
environment,
|
||||||
command: ['-c', CommandHookService.ApplyHooksToCommands(commands, CloudRunner.buildParameters)],
|
command: ['-c', CloudRunnerCustomHooks.ApplyHooksToCommands(commands, CloudRunner.buildParameters)],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -94,23 +53,16 @@ class AWSTaskRunner {
|
|||||||
securityGroups: [ContainerSecurityGroup],
|
securityGroups: [ContainerSecurityGroup],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
}).promise();
|
||||||
|
|
||||||
if (JSON.stringify(runParameters.overrides.containerOverrides).length > 8192) {
|
|
||||||
CloudRunnerLogger.log(JSON.stringify(runParameters.overrides.containerOverrides, undefined, 4));
|
|
||||||
throw new Error(`Container Overrides length must be at most 8192`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const task = await AwsClientFactory.getECS().send(new RunTaskCommand(runParameters as any));
|
|
||||||
const taskArn = task.tasks?.[0].taskArn || '';
|
const taskArn = task.tasks?.[0].taskArn || '';
|
||||||
CloudRunnerLogger.log('Cloud runner job is starting');
|
CloudRunnerLogger.log('Cloud runner job is starting');
|
||||||
await AWSTaskRunner.waitUntilTaskRunning(taskArn, cluster);
|
await AWSTaskRunner.waitUntilTaskRunning(taskArn, cluster);
|
||||||
CloudRunnerLogger.log(
|
CloudRunnerLogger.log(
|
||||||
`Cloud runner job status is running ${(await AWSTaskRunner.describeTasks(cluster, taskArn))?.lastStatus} Async:${
|
`Cloud runner job status is running ${(await AWSTaskRunner.describeTasks(cluster, taskArn))?.lastStatus} Watch:${
|
||||||
CloudRunnerOptions.asyncCloudRunner
|
CloudRunnerOptions.watchCloudRunnerToEnd
|
||||||
}`,
|
} Async:${CloudRunnerOptions.asyncCloudRunner}`,
|
||||||
);
|
);
|
||||||
if (CloudRunnerOptions.asyncCloudRunner) {
|
if (!CloudRunnerOptions.watchCloudRunnerToEnd) {
|
||||||
const shouldCleanup: boolean = false;
|
const shouldCleanup: boolean = false;
|
||||||
const output: string = '';
|
const output: string = '';
|
||||||
CloudRunnerLogger.log(`Watch Cloud Runner To End: false`);
|
CloudRunnerLogger.log(`Watch Cloud Runner To End: false`);
|
||||||
@@ -120,53 +72,39 @@ class AWSTaskRunner {
|
|||||||
|
|
||||||
CloudRunnerLogger.log(`Streaming...`);
|
CloudRunnerLogger.log(`Streaming...`);
|
||||||
const { output, shouldCleanup } = await this.streamLogsUntilTaskStops(cluster, taskArn, streamName);
|
const { output, shouldCleanup } = await this.streamLogsUntilTaskStops(cluster, taskArn, streamName);
|
||||||
let exitCode;
|
await new Promise((resolve) => resolve(5000));
|
||||||
let containerState;
|
const taskData = await AWSTaskRunner.describeTasks(cluster, taskArn);
|
||||||
let taskData;
|
const containerState = taskData.containers?.[0];
|
||||||
while (exitCode === undefined) {
|
const exitCode = containerState?.exitCode || undefined;
|
||||||
await new Promise((resolve) => setTimeout(resolve, 10000));
|
|
||||||
taskData = await AWSTaskRunner.describeTasks(cluster, taskArn);
|
|
||||||
const containers = taskData?.containers as any[] | undefined;
|
|
||||||
if (!containers || containers.length === 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
containerState = containers[0];
|
|
||||||
exitCode = containerState?.exitCode;
|
|
||||||
}
|
|
||||||
CloudRunnerLogger.log(`Container State: ${JSON.stringify(containerState, undefined, 4)}`);
|
CloudRunnerLogger.log(`Container State: ${JSON.stringify(containerState, undefined, 4)}`);
|
||||||
if (exitCode === undefined) {
|
const wasSuccessful = exitCode === 0 || (exitCode === undefined && taskData.lastStatus === 'RUNNING');
|
||||||
CloudRunnerLogger.logWarning(`Undefined exitcode for container`);
|
|
||||||
}
|
|
||||||
const wasSuccessful = exitCode === 0;
|
|
||||||
if (wasSuccessful) {
|
if (wasSuccessful) {
|
||||||
CloudRunnerLogger.log(`Cloud runner job has finished successfully`);
|
CloudRunnerLogger.log(`Cloud runner job has finished successfully`);
|
||||||
|
|
||||||
return { output, shouldCleanup };
|
return { output, shouldCleanup };
|
||||||
|
} else {
|
||||||
|
if (taskData.stoppedReason === 'Essential container in task exited' && exitCode === 1) {
|
||||||
|
throw new Error('Container exited with code 1');
|
||||||
|
}
|
||||||
|
const message = `Cloud runner job exit code ${exitCode}`;
|
||||||
|
taskData.overrides = undefined;
|
||||||
|
taskData.attachments = undefined;
|
||||||
|
CloudRunnerLogger.log(`${message} ${JSON.stringify(taskData, undefined, 4)}`);
|
||||||
|
throw new Error(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (taskData?.stoppedReason === 'Essential container in task exited' && exitCode === 1) {
|
|
||||||
throw new Error('Container exited with code 1');
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error(`Task failed`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async waitUntilTaskRunning(taskArn: string, cluster: string) {
|
private static async waitUntilTaskRunning(taskArn: string, cluster: string) {
|
||||||
try {
|
try {
|
||||||
await waitUntilTasksRunning(
|
await AWSTaskRunner.ECS.waitFor('tasksRunning', { tasks: [taskArn], cluster }).promise();
|
||||||
{
|
|
||||||
client: AwsClientFactory.getECS(),
|
|
||||||
maxWaitTime: 300,
|
|
||||||
minDelay: 5,
|
|
||||||
maxDelay: 30,
|
|
||||||
},
|
|
||||||
{ tasks: [taskArn], cluster },
|
|
||||||
);
|
|
||||||
} catch (error_) {
|
} catch (error_) {
|
||||||
const error = error_ as Error;
|
const error = error_ as Error;
|
||||||
await new Promise((resolve) => setTimeout(resolve, 3000));
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
||||||
const taskAfterError = await AWSTaskRunner.describeTasks(cluster, taskArn);
|
CloudRunnerLogger.log(
|
||||||
CloudRunnerLogger.log(`Cloud runner job has ended ${taskAfterError?.containers?.[0]?.lastStatus}`);
|
`Cloud runner job has ended ${
|
||||||
|
(await AWSTaskRunner.describeTasks(cluster, taskArn)).containers?.[0].lastStatus
|
||||||
|
}`,
|
||||||
|
);
|
||||||
|
|
||||||
core.setFailed(error);
|
core.setFailed(error);
|
||||||
core.error(error);
|
core.error(error);
|
||||||
@@ -174,31 +112,14 @@ class AWSTaskRunner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static async describeTasks(clusterName: string, taskArn: string) {
|
static async describeTasks(clusterName: string, taskArn: string) {
|
||||||
const maxAttempts = 10;
|
const tasks = await AWSTaskRunner.ECS.describeTasks({
|
||||||
let delayMs = 1000;
|
cluster: clusterName,
|
||||||
const maxDelayMs = 60000;
|
tasks: [taskArn],
|
||||||
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
}).promise();
|
||||||
try {
|
if (tasks.tasks?.[0]) {
|
||||||
const tasks = await AwsClientFactory.getECS().send(
|
return tasks.tasks?.[0];
|
||||||
new DescribeTasksCommand({ cluster: clusterName, tasks: [taskArn] }),
|
} else {
|
||||||
);
|
throw new Error('No task found');
|
||||||
if (tasks.tasks?.[0]) {
|
|
||||||
return tasks.tasks?.[0];
|
|
||||||
}
|
|
||||||
throw new Error('No task found');
|
|
||||||
} catch (error: any) {
|
|
||||||
const isThrottle = error?.name === 'ThrottlingException' || /rate exceeded/i.test(String(error?.message));
|
|
||||||
if (!isThrottle || attempt === maxAttempts) {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
const jitterMs = Math.floor(Math.random() * Math.min(1000, delayMs));
|
|
||||||
const sleepMs = delayMs + jitterMs;
|
|
||||||
CloudRunnerLogger.log(
|
|
||||||
`AWS throttled DescribeTasks (attempt ${attempt}/${maxAttempts}), backing off ${sleepMs}ms (${delayMs} + jitter ${jitterMs})`,
|
|
||||||
);
|
|
||||||
await new Promise((r) => setTimeout(r, sleepMs));
|
|
||||||
delayMs = Math.min(delayMs * 2, maxDelayMs);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,7 +129,7 @@ class AWSTaskRunner {
|
|||||||
const stream = await AWSTaskRunner.getLogStream(kinesisStreamName);
|
const stream = await AWSTaskRunner.getLogStream(kinesisStreamName);
|
||||||
let iterator = await AWSTaskRunner.getLogIterator(stream);
|
let iterator = await AWSTaskRunner.getLogIterator(stream);
|
||||||
|
|
||||||
const logBaseUrl = `https://${Input.region}.console.aws.amazon.com/cloudwatch/home?region=${Input.region}#logsV2:log-groups/log-group/${CloudRunner.buildParameters.awsStackName}${AWSTaskRunner.encodedUnderscore}${CloudRunner.buildParameters.awsStackName}-${CloudRunner.buildParameters.buildGuid}`;
|
const logBaseUrl = `https://${Input.region}.console.aws.amazon.com/cloudwatch/home?region=${Input.region}#logsV2:log-groups/log-group/${CloudRunner.buildParameters.awsBaseStackName}${AWSTaskRunner.encodedUnderscore}${CloudRunner.buildParameters.awsBaseStackName}-${CloudRunner.buildParameters.buildGuid}`;
|
||||||
CloudRunnerLogger.log(`You view the log stream on AWS Cloud Watch: ${logBaseUrl}`);
|
CloudRunnerLogger.log(`You view the log stream on AWS Cloud Watch: ${logBaseUrl}`);
|
||||||
await GitHub.updateGitHubCheck(`You view the log stream on AWS Cloud Watch: ${logBaseUrl}`, ``);
|
await GitHub.updateGitHubCheck(`You view the log stream on AWS Cloud Watch: ${logBaseUrl}`, ``);
|
||||||
let shouldReadLogs = true;
|
let shouldReadLogs = true;
|
||||||
@@ -219,9 +140,6 @@ class AWSTaskRunner {
|
|||||||
await new Promise((resolve) => setTimeout(resolve, 1500));
|
await new Promise((resolve) => setTimeout(resolve, 1500));
|
||||||
const taskData = await AWSTaskRunner.describeTasks(clusterName, taskArn);
|
const taskData = await AWSTaskRunner.describeTasks(clusterName, taskArn);
|
||||||
({ timestamp, shouldReadLogs } = AWSTaskRunner.checkStreamingShouldContinue(taskData, timestamp, shouldReadLogs));
|
({ timestamp, shouldReadLogs } = AWSTaskRunner.checkStreamingShouldContinue(taskData, timestamp, shouldReadLogs));
|
||||||
if (taskData?.lastStatus !== 'RUNNING') {
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 3500));
|
|
||||||
}
|
|
||||||
({ iterator, shouldReadLogs, output, shouldCleanup } = await AWSTaskRunner.handleLogStreamIteration(
|
({ iterator, shouldReadLogs, output, shouldCleanup } = await AWSTaskRunner.handleLogStreamIteration(
|
||||||
iterator,
|
iterator,
|
||||||
shouldReadLogs,
|
shouldReadLogs,
|
||||||
@@ -239,22 +157,9 @@ class AWSTaskRunner {
|
|||||||
output: string,
|
output: string,
|
||||||
shouldCleanup: boolean,
|
shouldCleanup: boolean,
|
||||||
) {
|
) {
|
||||||
let records: any;
|
const records = await AWSTaskRunner.Kinesis.getRecords({
|
||||||
try {
|
ShardIterator: iterator,
|
||||||
records = await AwsClientFactory.getKinesis().send(new GetRecordsCommand({ ShardIterator: iterator }));
|
}).promise();
|
||||||
} catch (error: any) {
|
|
||||||
const isThrottle = error?.name === 'ThrottlingException' || /rate exceeded/i.test(String(error?.message));
|
|
||||||
if (isThrottle) {
|
|
||||||
const baseBackoffMs = 1000;
|
|
||||||
const jitterMs = Math.floor(Math.random() * 1000);
|
|
||||||
const sleepMs = baseBackoffMs + jitterMs;
|
|
||||||
CloudRunnerLogger.log(`AWS throttled GetRecords, backing off ${sleepMs}ms (1000 + jitter ${jitterMs})`);
|
|
||||||
await new Promise((r) => setTimeout(r, sleepMs));
|
|
||||||
|
|
||||||
return { iterator, shouldReadLogs, output, shouldCleanup };
|
|
||||||
}
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
iterator = records.NextShardIterator || '';
|
iterator = records.NextShardIterator || '';
|
||||||
({ shouldReadLogs, output, shouldCleanup } = AWSTaskRunner.logRecords(
|
({ shouldReadLogs, output, shouldCleanup } = AWSTaskRunner.logRecords(
|
||||||
records,
|
records,
|
||||||
@@ -267,7 +172,7 @@ class AWSTaskRunner {
|
|||||||
return { iterator, shouldReadLogs, output, shouldCleanup };
|
return { iterator, shouldReadLogs, output, shouldCleanup };
|
||||||
}
|
}
|
||||||
|
|
||||||
private static checkStreamingShouldContinue(taskData: any, timestamp: number, shouldReadLogs: boolean) {
|
private static checkStreamingShouldContinue(taskData: AWS.ECS.Task, timestamp: number, shouldReadLogs: boolean) {
|
||||||
if (taskData?.lastStatus === 'UNKNOWN') {
|
if (taskData?.lastStatus === 'UNKNOWN') {
|
||||||
CloudRunnerLogger.log('## Cloud runner job unknwon');
|
CloudRunnerLogger.log('## Cloud runner job unknwon');
|
||||||
}
|
}
|
||||||
@@ -287,21 +192,22 @@ class AWSTaskRunner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static logRecords(
|
private static logRecords(
|
||||||
records: any,
|
records,
|
||||||
iterator: string,
|
iterator: string,
|
||||||
shouldReadLogs: boolean,
|
shouldReadLogs: boolean,
|
||||||
output: string,
|
output: string,
|
||||||
shouldCleanup: boolean,
|
shouldCleanup: boolean,
|
||||||
) {
|
) {
|
||||||
if ((records.Records ?? []).length > 0 && iterator) {
|
if (records.Records.length > 0 && iterator) {
|
||||||
for (const record of records.Records ?? []) {
|
for (let index = 0; index < records.Records.length; index++) {
|
||||||
const json = JSON.parse(
|
const json = JSON.parse(
|
||||||
zlib.gunzipSync(Buffer.from(record.Data as unknown as string, 'base64')).toString('utf8'),
|
zlib.gunzipSync(Buffer.from(records.Records[index].Data as string, 'base64')).toString('utf8'),
|
||||||
);
|
);
|
||||||
if (json.messageType === 'DATA_MESSAGE') {
|
if (json.messageType === 'DATA_MESSAGE') {
|
||||||
for (const logEvent of json.logEvents) {
|
for (let logEventsIndex = 0; logEventsIndex < json.logEvents.length; logEventsIndex++) {
|
||||||
|
const message = json.logEvents[logEventsIndex].message;
|
||||||
({ shouldReadLogs, shouldCleanup, output } = FollowLogStreamService.handleIteration(
|
({ shouldReadLogs, shouldCleanup, output } = FollowLogStreamService.handleIteration(
|
||||||
logEvent.message,
|
message,
|
||||||
shouldReadLogs,
|
shouldReadLogs,
|
||||||
shouldCleanup,
|
shouldCleanup,
|
||||||
output,
|
output,
|
||||||
@@ -315,19 +221,19 @@ class AWSTaskRunner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static async getLogStream(kinesisStreamName: string) {
|
private static async getLogStream(kinesisStreamName: string) {
|
||||||
return await AwsClientFactory.getKinesis().send(new DescribeStreamCommand({ StreamName: kinesisStreamName }));
|
return await AWSTaskRunner.Kinesis.describeStream({
|
||||||
|
StreamName: kinesisStreamName,
|
||||||
|
}).promise();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async getLogIterator(stream: any) {
|
private static async getLogIterator(stream) {
|
||||||
return (
|
return (
|
||||||
(
|
(
|
||||||
await AwsClientFactory.getKinesis().send(
|
await AWSTaskRunner.Kinesis.getShardIterator({
|
||||||
new GetShardIteratorCommand({
|
ShardIteratorType: 'TRIM_HORIZON',
|
||||||
ShardIteratorType: 'TRIM_HORIZON',
|
StreamName: stream.StreamDescription.StreamName,
|
||||||
StreamName: stream.StreamDescription?.StreamName ?? '',
|
ShardId: stream.StreamDescription.Shards[0].ShardId,
|
||||||
ShardId: stream.StreamDescription?.Shards?.[0]?.ShardId || '',
|
}).promise()
|
||||||
}),
|
|
||||||
)
|
|
||||||
).ShardIterator || ''
|
).ShardIterator || ''
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
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 get formation(): string {
|
public static readonly formation: string = `AWSTemplateFormatVersion: 2010-09-09
|
||||||
return `AWSTemplateFormatVersion: 2010-09-09
|
|
||||||
Description: ${TaskDefinitionFormation.description}
|
Description: ${TaskDefinitionFormation.description}
|
||||||
Parameters:
|
Parameters:
|
||||||
EnvironmentName:
|
EnvironmentName:
|
||||||
@@ -29,11 +26,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: ${CloudRunner.buildParameters.containerCpu}
|
Default: 1024
|
||||||
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: ${CloudRunner.buildParameters.containerMemory}
|
Default: 2048
|
||||||
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:
|
||||||
@@ -95,7 +92,7 @@ Resources:
|
|||||||
EFSVolumeConfiguration:
|
EFSVolumeConfiguration:
|
||||||
FilesystemId:
|
FilesystemId:
|
||||||
'Fn::ImportValue': !Sub '${'${EnvironmentName}'}:EfsFileStorageId'
|
'Fn::ImportValue': !Sub '${'${EnvironmentName}'}:EfsFileStorageId'
|
||||||
TransitEncryption: DISABLED
|
TransitEncryption: ENABLED
|
||||||
RequiresCompatibilities:
|
RequiresCompatibilities:
|
||||||
- FARGATE
|
- FARGATE
|
||||||
ExecutionRoleArn:
|
ExecutionRoleArn:
|
||||||
@@ -127,7 +124,8 @@ Resources:
|
|||||||
- SourceVolume: efs-data
|
- SourceVolume: efs-data
|
||||||
ContainerPath: !Ref EFSMountDirectory
|
ContainerPath: !Ref EFSMountDirectory
|
||||||
ReadOnly: false
|
ReadOnly: false
|
||||||
# template secrets p3 - container def
|
Secrets:
|
||||||
|
# template secrets p3 - container def
|
||||||
LogConfiguration:
|
LogConfiguration:
|
||||||
LogDriver: awslogs
|
LogDriver: awslogs
|
||||||
Options:
|
Options:
|
||||||
@@ -137,7 +135,6 @@ Resources:
|
|||||||
DependsOn:
|
DependsOn:
|
||||||
- LogGroup
|
- LogGroup
|
||||||
`;
|
`;
|
||||||
}
|
|
||||||
public static streamLogs = `
|
public static streamLogs = `
|
||||||
SubscriptionFilter:
|
SubscriptionFilter:
|
||||||
Type: 'AWS::Logs::SubscriptionFilter'
|
Type: 'AWS::Logs::SubscriptionFilter'
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
// eslint-disable-next-line import/named
|
import * as AWS from 'aws-sdk';
|
||||||
import { StackResource } from '@aws-sdk/client-cloudformation';
|
|
||||||
|
|
||||||
class CloudRunnerAWSTaskDef {
|
class CloudRunnerAWSTaskDef {
|
||||||
public taskDefStackName!: string;
|
public taskDefStackName!: string;
|
||||||
public taskDefCloudFormation!: string;
|
public taskDefCloudFormation!: string;
|
||||||
public taskDefResources: StackResource[] | undefined;
|
public taskDefResources: AWS.CloudFormation.StackResources | undefined;
|
||||||
public baseResources: StackResource[] | undefined;
|
public baseResources: AWS.CloudFormation.StackResources | undefined;
|
||||||
}
|
}
|
||||||
export default CloudRunnerAWSTaskDef;
|
export default CloudRunnerAWSTaskDef;
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { CloudFormation, DeleteStackCommand, waitUntilStackDeleteComplete } from '@aws-sdk/client-cloudformation';
|
import * as SDK from 'aws-sdk';
|
||||||
import CloudRunnerSecret from '../../options/cloud-runner-secret';
|
import CloudRunnerSecret from '../../services/cloud-runner-secret';
|
||||||
import CloudRunnerEnvironmentVariable from '../../options/cloud-runner-environment-variable';
|
import CloudRunnerEnvironmentVariable from '../../services/cloud-runner-environment-variable';
|
||||||
import CloudRunnerAWSTaskDef from './cloud-runner-aws-task-def';
|
import CloudRunnerAWSTaskDef from './cloud-runner-aws-task-def';
|
||||||
import AwsTaskRunner from './aws-task-runner';
|
import AwsTaskRunner from './aws-task-runner';
|
||||||
import { ProviderInterface } from '../provider-interface';
|
import { ProviderInterface } from '../provider-interface';
|
||||||
import BuildParameters from '../../../build-parameters';
|
import BuildParameters from '../../../build-parameters';
|
||||||
import CloudRunnerLogger from '../../services/core/cloud-runner-logger';
|
import CloudRunnerLogger from '../../services/cloud-runner-logger';
|
||||||
import { AWSJobStack as AwsJobStack } from './aws-job-stack';
|
import { AWSJobStack as AwsJobStack } from './aws-job-stack';
|
||||||
import { AWSBaseStack as AwsBaseStack } from './aws-base-stack';
|
import { AWSBaseStack as AwsBaseStack } from './aws-base-stack';
|
||||||
import { Input } from '../../..';
|
import { Input } from '../../..';
|
||||||
@@ -13,26 +13,13 @@ import { GarbageCollectionService } from './services/garbage-collection-service'
|
|||||||
import { ProviderResource } from '../provider-resource';
|
import { ProviderResource } from '../provider-resource';
|
||||||
import { ProviderWorkflow } from '../provider-workflow';
|
import { ProviderWorkflow } from '../provider-workflow';
|
||||||
import { TaskService } from './services/task-service';
|
import { TaskService } from './services/task-service';
|
||||||
import CloudRunnerOptions from '../../options/cloud-runner-options';
|
import CloudRunnerOptions from '../../cloud-runner-options';
|
||||||
import { AwsClientFactory } from './aws-client-factory';
|
|
||||||
import ResourceTracking from '../../services/core/resource-tracking';
|
|
||||||
|
|
||||||
const DEFAULT_STACK_WAIT_TIME_SECONDS = 600;
|
|
||||||
|
|
||||||
function getStackWaitTime(): number {
|
|
||||||
const overrideValue = Number(process.env.CLOUD_RUNNER_AWS_STACK_WAIT_TIME ?? '');
|
|
||||||
if (!Number.isNaN(overrideValue) && overrideValue > 0) {
|
|
||||||
return overrideValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return DEFAULT_STACK_WAIT_TIME_SECONDS;
|
|
||||||
}
|
|
||||||
|
|
||||||
class AWSBuildEnvironment implements ProviderInterface {
|
class AWSBuildEnvironment implements ProviderInterface {
|
||||||
private baseStackName: string;
|
private baseStackName: string;
|
||||||
|
|
||||||
constructor(buildParameters: BuildParameters) {
|
constructor(buildParameters: BuildParameters) {
|
||||||
this.baseStackName = buildParameters.awsStackName;
|
this.baseStackName = buildParameters.awsBaseStackName;
|
||||||
}
|
}
|
||||||
async listResources(): Promise<ProviderResource[]> {
|
async listResources(): Promise<ProviderResource[]> {
|
||||||
await TaskService.getCloudFormationJobStacks();
|
await TaskService.getCloudFormationJobStacks();
|
||||||
@@ -70,6 +57,8 @@ 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
|
||||||
@@ -86,11 +75,7 @@ class AWSBuildEnvironment implements ProviderInterface {
|
|||||||
branchName: string,
|
branchName: string,
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
defaultSecretsArray: { ParameterKey: string; EnvironmentVariable: string; ParameterValue: string }[],
|
defaultSecretsArray: { ParameterKey: string; EnvironmentVariable: string; ParameterValue: string }[],
|
||||||
) {
|
) {}
|
||||||
process.env.AWS_REGION = Input.region;
|
|
||||||
const CF = AwsClientFactory.getCloudFormation();
|
|
||||||
await new AwsBaseStack(this.baseStackName).setupBaseStack(CF);
|
|
||||||
}
|
|
||||||
|
|
||||||
async runTaskInWorkflow(
|
async runTaskInWorkflow(
|
||||||
buildGuid: string,
|
buildGuid: string,
|
||||||
@@ -102,14 +87,15 @@ class AWSBuildEnvironment implements ProviderInterface {
|
|||||||
secrets: CloudRunnerSecret[],
|
secrets: CloudRunnerSecret[],
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
process.env.AWS_REGION = Input.region;
|
process.env.AWS_REGION = Input.region;
|
||||||
ResourceTracking.logAllocationSummary('aws workflow');
|
const ECS = new SDK.ECS();
|
||||||
await ResourceTracking.logDiskUsageSnapshot('aws workflow (host)');
|
const CF = new SDK.CloudFormation();
|
||||||
AwsClientFactory.getECS();
|
AwsTaskRunner.ECS = ECS;
|
||||||
const CF = AwsClientFactory.getCloudFormation();
|
AwsTaskRunner.Kinesis = new SDK.Kinesis();
|
||||||
AwsClientFactory.getKinesis();
|
|
||||||
CloudRunnerLogger.log(`AWS Region: ${CF.config.region}`);
|
CloudRunnerLogger.log(`AWS Region: ${CF.config.region}`);
|
||||||
const entrypoint = ['/bin/sh'];
|
const entrypoint = ['/bin/sh'];
|
||||||
const startTimeMs = Date.now();
|
const startTimeMs = Date.now();
|
||||||
|
|
||||||
|
await new AwsBaseStack(this.baseStackName).setupBaseStack(CF);
|
||||||
const taskDef = await new AwsJobStack(this.baseStackName).setupCloudFormations(
|
const taskDef = await new AwsJobStack(this.baseStackName).setupCloudFormations(
|
||||||
CF,
|
CF,
|
||||||
buildGuid,
|
buildGuid,
|
||||||
@@ -143,32 +129,20 @@ class AWSBuildEnvironment implements ProviderInterface {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async cleanupResources(CF: CloudFormation, taskDef: CloudRunnerAWSTaskDef) {
|
async cleanupResources(CF: SDK.CloudFormation, taskDef: CloudRunnerAWSTaskDef) {
|
||||||
const stackWaitTimeSeconds = getStackWaitTime();
|
CloudRunnerLogger.log('Cleanup starting');
|
||||||
CloudRunnerLogger.log(`Cleanup starting (waiting up to ${stackWaitTimeSeconds}s for stack deletion)`);
|
await CF.deleteStack({
|
||||||
await CF.send(new DeleteStackCommand({ StackName: taskDef.taskDefStackName }));
|
StackName: taskDef.taskDefStackName,
|
||||||
|
}).promise();
|
||||||
if (CloudRunnerOptions.useCleanupCron) {
|
if (CloudRunnerOptions.useCleanupCron) {
|
||||||
await CF.send(new DeleteStackCommand({ StackName: `${taskDef.taskDefStackName}-cleanup` }));
|
await CF.deleteStack({
|
||||||
|
StackName: `${taskDef.taskDefStackName}-cleanup`,
|
||||||
|
}).promise();
|
||||||
}
|
}
|
||||||
|
|
||||||
await waitUntilStackDeleteComplete(
|
await CF.waitFor('stackDeleteComplete', {
|
||||||
{
|
StackName: taskDef.taskDefStackName,
|
||||||
client: CF,
|
}).promise();
|
||||||
maxWaitTime: stackWaitTimeSeconds,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
StackName: taskDef.taskDefStackName,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
await waitUntilStackDeleteComplete(
|
|
||||||
{
|
|
||||||
client: CF,
|
|
||||||
maxWaitTime: stackWaitTimeSeconds,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
StackName: `${taskDef.taskDefStackName}-cleanup`,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
CloudRunnerLogger.log(`Deleted Stack: ${taskDef.taskDefStackName}`);
|
CloudRunnerLogger.log(`Deleted Stack: ${taskDef.taskDefStackName}`);
|
||||||
CloudRunnerLogger.log('Cleanup complete');
|
CloudRunnerLogger.log('Cleanup complete');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
import { DeleteStackCommand, DescribeStackResourcesCommand } from '@aws-sdk/client-cloudformation';
|
import AWS from 'aws-sdk';
|
||||||
import { DeleteLogGroupCommand } from '@aws-sdk/client-cloudwatch-logs';
|
|
||||||
import { StopTaskCommand } from '@aws-sdk/client-ecs';
|
|
||||||
import Input from '../../../../input';
|
import Input from '../../../../input';
|
||||||
import CloudRunnerLogger from '../../../services/core/cloud-runner-logger';
|
import CloudRunnerLogger from '../../../services/cloud-runner-logger';
|
||||||
import { TaskService } from './task-service';
|
import { TaskService } from './task-service';
|
||||||
import { AwsClientFactory } from '../aws-client-factory';
|
|
||||||
|
|
||||||
export class GarbageCollectionService {
|
export class GarbageCollectionService {
|
||||||
static isOlderThan1day(date: Date) {
|
static isOlderThan1day(date: any) {
|
||||||
const ageDate = new Date(date.getTime() - Date.now());
|
const ageDate = new Date(date.getTime() - Date.now());
|
||||||
|
|
||||||
return ageDate.getDay() > 0;
|
return ageDate.getDay() > 0;
|
||||||
@@ -15,25 +12,23 @@ export class GarbageCollectionService {
|
|||||||
|
|
||||||
public static async cleanup(deleteResources = false, OneDayOlderOnly: boolean = false) {
|
public static async cleanup(deleteResources = false, OneDayOlderOnly: boolean = false) {
|
||||||
process.env.AWS_REGION = Input.region;
|
process.env.AWS_REGION = Input.region;
|
||||||
const CF = AwsClientFactory.getCloudFormation();
|
const CF = new AWS.CloudFormation();
|
||||||
const ecs = AwsClientFactory.getECS();
|
const ecs = new AWS.ECS();
|
||||||
const cwl = AwsClientFactory.getCloudWatchLogs();
|
const cwl = new AWS.CloudWatchLogs();
|
||||||
const taskDefinitionsInUse = new Array();
|
const taskDefinitionsInUse = new Array();
|
||||||
const tasks = await TaskService.getTasks();
|
const tasks = await TaskService.getTasks();
|
||||||
|
|
||||||
for (const task of tasks) {
|
for (const task of tasks) {
|
||||||
const { taskElement, element } = task;
|
const { taskElement, element } = task;
|
||||||
taskDefinitionsInUse.push(taskElement.taskDefinitionArn);
|
taskDefinitionsInUse.push(taskElement.taskDefinitionArn);
|
||||||
if (deleteResources && (!OneDayOlderOnly || GarbageCollectionService.isOlderThan1day(taskElement.createdAt!))) {
|
if (deleteResources && (!OneDayOlderOnly || GarbageCollectionService.isOlderThan1day(taskElement.CreatedAt))) {
|
||||||
CloudRunnerLogger.log(`Stopping task ${taskElement.containers?.[0].name}`);
|
CloudRunnerLogger.log(`Stopping task ${taskElement.containers?.[0].name}`);
|
||||||
await ecs.send(new StopTaskCommand({ task: taskElement.taskArn || '', cluster: element }));
|
await ecs.stopTask({ task: taskElement.taskArn || '', cluster: element }).promise();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const jobStacks = await TaskService.getCloudFormationJobStacks();
|
const jobStacks = await TaskService.getCloudFormationJobStacks();
|
||||||
for (const element of jobStacks) {
|
for (const element of jobStacks) {
|
||||||
if (
|
if (
|
||||||
(await CF.send(new DescribeStackResourcesCommand({ StackName: element.StackName }))).StackResources?.some(
|
(await CF.describeStackResources({ StackName: element.StackName }).promise()).StackResources?.some(
|
||||||
(x) => x.ResourceType === 'AWS::ECS::TaskDefinition' && taskDefinitionsInUse.includes(x.PhysicalResourceId),
|
(x) => x.ResourceType === 'AWS::ECS::TaskDefinition' && taskDefinitionsInUse.includes(x.PhysicalResourceId),
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
@@ -41,29 +36,25 @@ export class GarbageCollectionService {
|
|||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (deleteResources && (!OneDayOlderOnly || GarbageCollectionService.isOlderThan1day(element.CreationTime))) {
|
||||||
if (
|
|
||||||
deleteResources &&
|
|
||||||
(!OneDayOlderOnly || (element.CreationTime && GarbageCollectionService.isOlderThan1day(element.CreationTime)))
|
|
||||||
) {
|
|
||||||
if (element.StackName === 'game-ci' || element.TemplateDescription === 'Game-CI base stack') {
|
if (element.StackName === 'game-ci' || element.TemplateDescription === 'Game-CI base stack') {
|
||||||
CloudRunnerLogger.log(`Skipping ${element.StackName} ignore list`);
|
CloudRunnerLogger.log(`Skipping ${element.StackName} ignore list`);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
CloudRunnerLogger.log(`Deleting ${element.logGroupName}`);
|
||||||
CloudRunnerLogger.log(`Deleting ${element.StackName}`);
|
const deleteStackInput: AWS.CloudFormation.DeleteStackInput = { StackName: element.StackName };
|
||||||
await CF.send(new DeleteStackCommand({ StackName: element.StackName }));
|
await CF.deleteStack(deleteStackInput).promise();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const logGroups = await TaskService.getLogGroups();
|
const logGroups = await TaskService.getLogGroups();
|
||||||
for (const element of logGroups) {
|
for (const element of logGroups) {
|
||||||
if (
|
if (
|
||||||
deleteResources &&
|
deleteResources &&
|
||||||
(!OneDayOlderOnly || GarbageCollectionService.isOlderThan1day(new Date(element.creationTime!)))
|
(!OneDayOlderOnly || GarbageCollectionService.isOlderThan1day(new Date(element.createdAt)))
|
||||||
) {
|
) {
|
||||||
CloudRunnerLogger.log(`Deleting ${element.logGroupName}`);
|
CloudRunnerLogger.log(`Deleting ${element.logGroupName}`);
|
||||||
await cwl.send(new DeleteLogGroupCommand({ logGroupName: element.logGroupName || '' }));
|
await cwl.deleteLogGroup({ logGroupName: element.logGroupName || '' }).promise();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,22 +1,10 @@
|
|||||||
import {
|
import AWS from 'aws-sdk';
|
||||||
DescribeStackResourcesCommand,
|
|
||||||
DescribeStacksCommand,
|
|
||||||
ListStacksCommand,
|
|
||||||
} from '@aws-sdk/client-cloudformation';
|
|
||||||
import type { StackSummary } from '@aws-sdk/client-cloudformation';
|
|
||||||
// eslint-disable-next-line import/named
|
|
||||||
import { DescribeLogGroupsCommand, DescribeLogGroupsCommandInput } from '@aws-sdk/client-cloudwatch-logs';
|
|
||||||
import type { LogGroup } from '@aws-sdk/client-cloudwatch-logs';
|
|
||||||
import { DescribeTasksCommand, ListClustersCommand, ListTasksCommand } from '@aws-sdk/client-ecs';
|
|
||||||
import type { Task } from '@aws-sdk/client-ecs';
|
|
||||||
import { ListObjectsV2Command } from '@aws-sdk/client-s3';
|
|
||||||
import Input from '../../../../input';
|
import Input from '../../../../input';
|
||||||
import CloudRunnerLogger from '../../../services/core/cloud-runner-logger';
|
import CloudRunnerLogger from '../../../services/cloud-runner-logger';
|
||||||
import { BaseStackFormation } from '../cloud-formations/base-stack-formation';
|
import { BaseStackFormation } from '../cloud-formations/base-stack-formation';
|
||||||
import AwsTaskRunner from '../aws-task-runner';
|
import AwsTaskRunner from '../aws-task-runner';
|
||||||
|
import { ListObjectsRequest } from 'aws-sdk/clients/s3';
|
||||||
import CloudRunner from '../../../cloud-runner';
|
import CloudRunner from '../../../cloud-runner';
|
||||||
import { AwsClientFactory } from '../aws-client-factory';
|
|
||||||
import SharedWorkspaceLocking from '../../../services/core/shared-workspace-locking';
|
|
||||||
|
|
||||||
export class TaskService {
|
export class TaskService {
|
||||||
static async watch() {
|
static async watch() {
|
||||||
@@ -29,25 +17,21 @@ export class TaskService {
|
|||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
public static async getCloudFormationJobStacks(): Promise<StackSummary[]> {
|
public static async getCloudFormationJobStacks() {
|
||||||
const result: StackSummary[] = [];
|
const result: any[] = [];
|
||||||
CloudRunnerLogger.log(``);
|
CloudRunnerLogger.log(``);
|
||||||
CloudRunnerLogger.log(`List Cloud Formation Stacks`);
|
CloudRunnerLogger.log(`List Cloud Formation Stacks`);
|
||||||
process.env.AWS_REGION = Input.region;
|
process.env.AWS_REGION = Input.region;
|
||||||
const CF = AwsClientFactory.getCloudFormation();
|
const CF = new AWS.CloudFormation();
|
||||||
const stacks =
|
const stacks =
|
||||||
(await CF.send(new ListStacksCommand({}))).StackSummaries?.filter(
|
(await CF.listStacks().promise()).StackSummaries?.filter(
|
||||||
(_x) =>
|
(_x) =>
|
||||||
_x.StackStatus !== 'DELETE_COMPLETE' && _x.TemplateDescription !== BaseStackFormation.baseStackDecription,
|
_x.StackStatus !== 'DELETE_COMPLETE' && _x.TemplateDescription !== BaseStackFormation.baseStackDecription,
|
||||||
) || [];
|
) || [];
|
||||||
CloudRunnerLogger.log(``);
|
CloudRunnerLogger.log(``);
|
||||||
CloudRunnerLogger.log(`Cloud Formation Stacks ${stacks.length}`);
|
CloudRunnerLogger.log(`Cloud Formation Stacks ${stacks.length}`);
|
||||||
for (const element of stacks) {
|
for (const element of stacks) {
|
||||||
if (!element.CreationTime) {
|
const ageDate: Date = new Date(Date.now() - element.CreationTime.getTime());
|
||||||
CloudRunnerLogger.log(`${element.StackName} due to undefined CreationTime`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const ageDate: Date = new Date(Date.now() - (element.CreationTime?.getTime() ?? 0));
|
|
||||||
|
|
||||||
CloudRunnerLogger.log(
|
CloudRunnerLogger.log(
|
||||||
`Task Stack ${element.StackName} - Age D${Math.floor(
|
`Task Stack ${element.StackName} - Age D${Math.floor(
|
||||||
@@ -57,18 +41,14 @@ export class TaskService {
|
|||||||
result.push(element);
|
result.push(element);
|
||||||
}
|
}
|
||||||
const baseStacks =
|
const baseStacks =
|
||||||
(await CF.send(new ListStacksCommand({}))).StackSummaries?.filter(
|
(await CF.listStacks().promise()).StackSummaries?.filter(
|
||||||
(_x) =>
|
(_x) =>
|
||||||
_x.StackStatus !== 'DELETE_COMPLETE' && _x.TemplateDescription === BaseStackFormation.baseStackDecription,
|
_x.StackStatus !== 'DELETE_COMPLETE' && _x.TemplateDescription === BaseStackFormation.baseStackDecription,
|
||||||
) || [];
|
) || [];
|
||||||
CloudRunnerLogger.log(``);
|
CloudRunnerLogger.log(``);
|
||||||
CloudRunnerLogger.log(`Base Stacks ${baseStacks.length}`);
|
CloudRunnerLogger.log(`Base Stacks ${baseStacks.length}`);
|
||||||
for (const element of baseStacks) {
|
for (const element of baseStacks) {
|
||||||
if (!element.CreationTime) {
|
const ageDate: Date = new Date(Date.now() - element.CreationTime.getTime());
|
||||||
CloudRunnerLogger.log(`${element.StackName} due to undefined CreationTime`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const ageDate: Date = new Date(Date.now() - (element.CreationTime?.getTime() ?? 0));
|
|
||||||
|
|
||||||
CloudRunnerLogger.log(
|
CloudRunnerLogger.log(
|
||||||
`Task Stack ${element.StackName} - Age D${Math.floor(
|
`Task Stack ${element.StackName} - Age D${Math.floor(
|
||||||
@@ -81,35 +61,23 @@ export class TaskService {
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
public static async getTasks(): Promise<{ taskElement: Task; element: string }[]> {
|
public static async getTasks() {
|
||||||
const result: { taskElement: Task; element: string }[] = [];
|
const result: any[] = [];
|
||||||
CloudRunnerLogger.log(``);
|
CloudRunnerLogger.log(``);
|
||||||
CloudRunnerLogger.log(`List Tasks`);
|
CloudRunnerLogger.log(`List Tasks`);
|
||||||
process.env.AWS_REGION = Input.region;
|
process.env.AWS_REGION = Input.region;
|
||||||
const ecs = AwsClientFactory.getECS();
|
const ecs = new AWS.ECS();
|
||||||
const clusters: string[] = [];
|
const clusters = (await ecs.listClusters().promise()).clusterArns || [];
|
||||||
{
|
|
||||||
let nextToken: string | undefined;
|
|
||||||
do {
|
|
||||||
const clusterResponse = await ecs.send(new ListClustersCommand({ nextToken }));
|
|
||||||
clusters.push(...(clusterResponse.clusterArns ?? []));
|
|
||||||
nextToken = clusterResponse.nextToken;
|
|
||||||
} while (nextToken);
|
|
||||||
}
|
|
||||||
CloudRunnerLogger.log(`Task Clusters ${clusters.length}`);
|
CloudRunnerLogger.log(`Task Clusters ${clusters.length}`);
|
||||||
for (const element of clusters) {
|
for (const element of clusters) {
|
||||||
const taskArns: string[] = [];
|
const input: AWS.ECS.ListTasksRequest = {
|
||||||
{
|
cluster: element,
|
||||||
let nextToken: string | undefined;
|
};
|
||||||
do {
|
|
||||||
const taskResponse = await ecs.send(new ListTasksCommand({ cluster: element, nextToken }));
|
const list = (await ecs.listTasks(input).promise()).taskArns || [];
|
||||||
taskArns.push(...(taskResponse.taskArns ?? []));
|
if (list.length > 0) {
|
||||||
nextToken = taskResponse.nextToken;
|
const describeInput: AWS.ECS.DescribeTasksRequest = { tasks: list, cluster: element };
|
||||||
} while (nextToken);
|
const describeList = (await ecs.describeTasks(describeInput).promise()).tasks || [];
|
||||||
}
|
|
||||||
if (taskArns.length > 0) {
|
|
||||||
const describeInput = { tasks: taskArns, cluster: element };
|
|
||||||
const describeList = (await ecs.send(new DescribeTasksCommand(describeInput))).tasks || [];
|
|
||||||
if (describeList.length === 0) {
|
if (describeList.length === 0) {
|
||||||
CloudRunnerLogger.log(`No Tasks`);
|
CloudRunnerLogger.log(`No Tasks`);
|
||||||
continue;
|
continue;
|
||||||
@@ -119,6 +87,8 @@ export class TaskService {
|
|||||||
if (taskElement === undefined) {
|
if (taskElement === undefined) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
taskElement.overrides = {};
|
||||||
|
taskElement.attachments = [];
|
||||||
if (taskElement.createdAt === undefined) {
|
if (taskElement.createdAt === undefined) {
|
||||||
CloudRunnerLogger.log(`Skipping ${taskElement.taskDefinitionArn} no createdAt date`);
|
CloudRunnerLogger.log(`Skipping ${taskElement.taskDefinitionArn} no createdAt date`);
|
||||||
continue;
|
continue;
|
||||||
@@ -133,51 +103,37 @@ export class TaskService {
|
|||||||
}
|
}
|
||||||
public static async awsDescribeJob(job: string) {
|
public static async awsDescribeJob(job: string) {
|
||||||
process.env.AWS_REGION = Input.region;
|
process.env.AWS_REGION = Input.region;
|
||||||
const CF = AwsClientFactory.getCloudFormation();
|
const CF = new AWS.CloudFormation();
|
||||||
try {
|
const stack = (await CF.listStacks().promise()).StackSummaries?.find((_x) => _x.StackName === job) || undefined;
|
||||||
const stack =
|
const stackInfo = (await CF.describeStackResources({ StackName: job }).promise()) || undefined;
|
||||||
(await CF.send(new ListStacksCommand({}))).StackSummaries?.find((_x) => _x.StackName === job) || undefined;
|
const stackInfo2 = (await CF.describeStacks({ StackName: job }).promise()) || undefined;
|
||||||
const stackInfo = (await CF.send(new DescribeStackResourcesCommand({ StackName: job }))) || undefined;
|
if (stack === undefined) {
|
||||||
const stackInfo2 = (await CF.send(new DescribeStacksCommand({ StackName: job }))) || undefined;
|
throw new Error('stack not defined');
|
||||||
if (stack === undefined) {
|
}
|
||||||
throw new Error('stack not defined');
|
const ageDate: Date = new Date(Date.now() - stack.CreationTime.getTime());
|
||||||
}
|
const message = `
|
||||||
if (!stack.CreationTime) {
|
|
||||||
CloudRunnerLogger.log(`${stack.StackName} due to undefined CreationTime`);
|
|
||||||
}
|
|
||||||
const ageDate: Date = new Date(Date.now() - (stack.CreationTime?.getTime() ?? 0));
|
|
||||||
const message = `
|
|
||||||
Task Stack ${stack.StackName}
|
Task Stack ${stack.StackName}
|
||||||
Age D${Math.floor(ageDate.getHours() / 24)} H${ageDate.getHours()} M${ageDate.getMinutes()}
|
Age D${Math.floor(ageDate.getHours() / 24)} H${ageDate.getHours()} M${ageDate.getMinutes()}
|
||||||
${JSON.stringify(stack, undefined, 4)}
|
${JSON.stringify(stack, undefined, 4)}
|
||||||
${JSON.stringify(stackInfo, undefined, 4)}
|
${JSON.stringify(stackInfo, undefined, 4)}
|
||||||
${JSON.stringify(stackInfo2, undefined, 4)}
|
${JSON.stringify(stackInfo2, undefined, 4)}
|
||||||
`;
|
`;
|
||||||
CloudRunnerLogger.log(message);
|
CloudRunnerLogger.log(message);
|
||||||
|
|
||||||
return message;
|
return message;
|
||||||
} catch (error) {
|
|
||||||
CloudRunnerLogger.error(
|
|
||||||
`Failed to describe job ${job}: ${error instanceof Error ? error.message : String(error)}`,
|
|
||||||
);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
public static async getLogGroups(): Promise<LogGroup[]> {
|
public static async getLogGroups() {
|
||||||
const result: LogGroup[] = [];
|
const result: any[] = [];
|
||||||
process.env.AWS_REGION = Input.region;
|
process.env.AWS_REGION = Input.region;
|
||||||
const cwl = AwsClientFactory.getCloudWatchLogs();
|
const ecs = new AWS.CloudWatchLogs();
|
||||||
let logStreamInput: DescribeLogGroupsCommandInput = {
|
let logStreamInput: AWS.CloudWatchLogs.DescribeLogGroupsRequest = {
|
||||||
/* logGroupNamePrefix: 'game-ci' */
|
/* logGroupNamePrefix: 'game-ci' */
|
||||||
};
|
};
|
||||||
let logGroupsDescribe = await cwl.send(new DescribeLogGroupsCommand(logStreamInput));
|
let logGroupsDescribe = await ecs.describeLogGroups(logStreamInput).promise();
|
||||||
const logGroups = logGroupsDescribe.logGroups || [];
|
const logGroups = logGroupsDescribe.logGroups || [];
|
||||||
while (logGroupsDescribe.nextToken) {
|
while (logGroupsDescribe.nextToken) {
|
||||||
logStreamInput = {
|
logStreamInput = { /* logGroupNamePrefix: 'game-ci',*/ nextToken: logGroupsDescribe.nextToken };
|
||||||
/* logGroupNamePrefix: 'game-ci',*/
|
logGroupsDescribe = await ecs.describeLogGroups(logStreamInput).promise();
|
||||||
nextToken: logGroupsDescribe.nextToken,
|
|
||||||
};
|
|
||||||
logGroupsDescribe = await cwl.send(new DescribeLogGroupsCommand(logStreamInput));
|
|
||||||
logGroups.push(...(logGroupsDescribe?.logGroups || []));
|
logGroups.push(...(logGroupsDescribe?.logGroups || []));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,22 +155,14 @@ export class TaskService {
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
public static async getLocks(): Promise<Array<{ Key: string }>> {
|
public static async getLocks() {
|
||||||
process.env.AWS_REGION = Input.region;
|
process.env.AWS_REGION = Input.region;
|
||||||
if (CloudRunner.buildParameters.storageProvider === 'rclone') {
|
const s3 = new AWS.S3();
|
||||||
// eslint-disable-next-line no-unused-vars
|
const listRequest: ListObjectsRequest = {
|
||||||
type ListObjectsFunction = (prefix: string) => Promise<string[]>;
|
Bucket: CloudRunner.buildParameters.awsBaseStackName,
|
||||||
const objects = await (SharedWorkspaceLocking as unknown as { listObjects: ListObjectsFunction }).listObjects('');
|
|
||||||
|
|
||||||
return objects.map((x: string) => ({ Key: x }));
|
|
||||||
}
|
|
||||||
const s3 = AwsClientFactory.getS3();
|
|
||||||
const listRequest = {
|
|
||||||
Bucket: CloudRunner.buildParameters.awsStackName,
|
|
||||||
};
|
};
|
||||||
|
const results = await s3.listObjects(listRequest).promise();
|
||||||
|
|
||||||
const results = await s3.send(new ListObjectsV2Command(listRequest));
|
return results.Contents || [];
|
||||||
|
|
||||||
return (results.Contents || []).map((object) => ({ Key: object.Key || '' }));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,19 @@
|
|||||||
import BuildParameters from '../../../build-parameters';
|
import BuildParameters from '../../../build-parameters';
|
||||||
import CloudRunnerEnvironmentVariable from '../../options/cloud-runner-environment-variable';
|
import CloudRunnerEnvironmentVariable from '../../services/cloud-runner-environment-variable';
|
||||||
import CloudRunnerLogger from '../../services/core/cloud-runner-logger';
|
import CloudRunnerLogger from '../../services/cloud-runner-logger';
|
||||||
import { ProviderInterface } from '../provider-interface';
|
import { ProviderInterface } from '../provider-interface';
|
||||||
import CloudRunnerSecret from '../../options/cloud-runner-secret';
|
import CloudRunnerSecret from '../../services/cloud-runner-secret';
|
||||||
import Docker from '../../../docker';
|
import Docker from '../../../docker';
|
||||||
import { Action } from '../../..';
|
import { Action } from '../../..';
|
||||||
import { writeFileSync } from 'node:fs';
|
import { writeFileSync } from 'fs';
|
||||||
import CloudRunner from '../../cloud-runner';
|
import CloudRunner from '../../cloud-runner';
|
||||||
import { ProviderResource } from '../provider-resource';
|
import { ProviderResource } from '../provider-resource';
|
||||||
import { ProviderWorkflow } from '../provider-workflow';
|
import { ProviderWorkflow } from '../provider-workflow';
|
||||||
import { CloudRunnerSystem } from '../../services/core/cloud-runner-system';
|
import { CloudRunnerSystem } from '../../services/cloud-runner-system';
|
||||||
import * as fs from 'node:fs';
|
import * as fs from 'fs';
|
||||||
import { CommandHookService } from '../../services/hooks/command-hook-service';
|
|
||||||
import { StringKeyValuePair } from '../../../shared-types';
|
|
||||||
|
|
||||||
class LocalDockerCloudRunner implements ProviderInterface {
|
class LocalDockerCloudRunner implements ProviderInterface {
|
||||||
public buildParameters!: BuildParameters;
|
public buildParameters: BuildParameters | undefined;
|
||||||
|
|
||||||
listResources(): Promise<ProviderResource[]> {
|
listResources(): Promise<ProviderResource[]> {
|
||||||
return new Promise((resolve) => resolve([]));
|
return new Promise((resolve) => resolve([]));
|
||||||
@@ -41,6 +39,7 @@ 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,
|
||||||
@@ -51,14 +50,14 @@ class LocalDockerCloudRunner implements ProviderInterface {
|
|||||||
if (
|
if (
|
||||||
fs.existsSync(
|
fs.existsSync(
|
||||||
`${workspace}/cloud-runner-cache/cache/build/build-${buildParameters.buildGuid}.tar${
|
`${workspace}/cloud-runner-cache/cache/build/build-${buildParameters.buildGuid}.tar${
|
||||||
CloudRunner.buildParameters.useCompressionStrategy ? '.lz4' : ''
|
CloudRunner.buildParameters.useLz4Compression ? '.lz4' : ''
|
||||||
}`,
|
}`,
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
await CloudRunnerSystem.Run(`ls ${workspace}/cloud-runner-cache/cache/build/`);
|
await CloudRunnerSystem.Run(`ls ${workspace}/cloud-runner-cache/cache/build/`);
|
||||||
await CloudRunnerSystem.Run(
|
await CloudRunnerSystem.Run(
|
||||||
`rm -r ${workspace}/cloud-runner-cache/cache/build/build-${buildParameters.buildGuid}.tar${
|
`rm -r ${workspace}/cloud-runner-cache/cache/build/build-${buildParameters.buildGuid}.tar${
|
||||||
CloudRunner.buildParameters.useCompressionStrategy ? '.lz4' : ''
|
CloudRunner.buildParameters.useLz4Compression ? '.lz4' : ''
|
||||||
}`,
|
}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -87,37 +86,12 @@ class LocalDockerCloudRunner implements ProviderInterface {
|
|||||||
CloudRunnerLogger.log(commands);
|
CloudRunnerLogger.log(commands);
|
||||||
|
|
||||||
const { workspace, actionFolder } = Action;
|
const { workspace, actionFolder } = Action;
|
||||||
const content: StringKeyValuePair[] = [];
|
const content: any[] = [];
|
||||||
for (const x of secrets) {
|
for (const x of secrets) {
|
||||||
content.push({ name: x.EnvironmentVariable, value: x.ParameterValue });
|
content.push({ name: x.EnvironmentVariable, value: x.ParameterValue });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace localhost with host.docker.internal for LocalStack endpoints (similar to K8s)
|
|
||||||
// This allows Docker containers to access LocalStack running on the host
|
|
||||||
const endpointEnvironmentNames = new Set([
|
|
||||||
'AWS_S3_ENDPOINT',
|
|
||||||
'AWS_ENDPOINT',
|
|
||||||
'AWS_CLOUD_FORMATION_ENDPOINT',
|
|
||||||
'AWS_ECS_ENDPOINT',
|
|
||||||
'AWS_KINESIS_ENDPOINT',
|
|
||||||
'AWS_CLOUD_WATCH_LOGS_ENDPOINT',
|
|
||||||
'INPUT_AWSS3ENDPOINT',
|
|
||||||
'INPUT_AWSENDPOINT',
|
|
||||||
]);
|
|
||||||
for (const x of environment) {
|
for (const x of environment) {
|
||||||
let value = x.value;
|
content.push({ name: x.name, value: x.value });
|
||||||
if (
|
|
||||||
typeof value === 'string' &&
|
|
||||||
endpointEnvironmentNames.has(x.name) &&
|
|
||||||
(value.startsWith('http://localhost') || value.startsWith('http://127.0.0.1'))
|
|
||||||
) {
|
|
||||||
// Replace localhost with host.docker.internal so containers can access host services
|
|
||||||
value = value
|
|
||||||
.replace('http://localhost', 'http://host.docker.internal')
|
|
||||||
.replace('http://127.0.0.1', 'http://host.docker.internal');
|
|
||||||
CloudRunnerLogger.log(`Replaced localhost with host.docker.internal for ${x.name}: ${value}`);
|
|
||||||
}
|
|
||||||
content.push({ name: x.name, value });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (this.buildParameters?.cloudRunnerIntegrationTests) {
|
// if (this.buildParameters?.cloudRunnerIntegrationTests) {
|
||||||
@@ -137,22 +111,14 @@ class LocalDockerCloudRunner implements ProviderInterface {
|
|||||||
|
|
||||||
// core.info(JSON.stringify({ workspace, actionFolder, ...this.buildParameters, ...content }, undefined, 4));
|
// core.info(JSON.stringify({ workspace, actionFolder, ...this.buildParameters, ...content }, undefined, 4));
|
||||||
const entrypointFilePath = `start.sh`;
|
const entrypointFilePath = `start.sh`;
|
||||||
|
const fileContents = `#!/bin/bash
|
||||||
// Use #!/bin/sh for POSIX compatibility (Alpine-based images like rclone/rclone don't have bash)
|
|
||||||
const fileContents = `#!/bin/sh
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
mkdir -p /github/workspace/cloud-runner-cache
|
mkdir -p /github/workspace/cloud-runner-cache
|
||||||
mkdir -p /data/cache
|
mkdir -p /data/cache
|
||||||
cp -a /github/workspace/cloud-runner-cache/. ${sharedFolder}
|
cp -a /github/workspace/cloud-runner-cache/. ${sharedFolder}
|
||||||
${CommandHookService.ApplyHooksToCommands(commands, this.buildParameters)}
|
${commands}
|
||||||
# Only copy cache directory, exclude retained workspaces to avoid running out of disk space
|
cp -a ${sharedFolder}. /github/workspace/cloud-runner-cache/
|
||||||
if [ -d "${sharedFolder}cache" ]; then
|
|
||||||
cp -a ${sharedFolder}cache/. /github/workspace/cloud-runner-cache/cache/ || true
|
|
||||||
fi
|
|
||||||
# Copy test files from /data/ root to workspace for test assertions
|
|
||||||
# This allows tests to write files to /data/ and have them available in the workspace
|
|
||||||
find ${sharedFolder} -maxdepth 1 -type f -name "test-*" -exec cp -a {} /github/workspace/cloud-runner-cache/ \\; || true
|
|
||||||
`;
|
`;
|
||||||
writeFileSync(`${workspace}/${entrypointFilePath}`, fileContents, {
|
writeFileSync(`${workspace}/${entrypointFilePath}`, fileContents, {
|
||||||
flag: 'w',
|
flag: 'w',
|
||||||
@@ -165,7 +131,7 @@ find ${sharedFolder} -maxdepth 1 -type f -name "test-*" -exec cp -a {} /github/w
|
|||||||
if (fs.existsSync(`${workspace}/cloud-runner-cache`)) {
|
if (fs.existsSync(`${workspace}/cloud-runner-cache`)) {
|
||||||
await CloudRunnerSystem.Run(`ls ${workspace}/cloud-runner-cache && du -sh ${workspace}/cloud-runner-cache`);
|
await CloudRunnerSystem.Run(`ls ${workspace}/cloud-runner-cache && du -sh ${workspace}/cloud-runner-cache`);
|
||||||
}
|
}
|
||||||
const exitCode = await Docker.run(
|
await Docker.run(
|
||||||
image,
|
image,
|
||||||
{ workspace, actionFolder, ...this.buildParameters },
|
{ workspace, actionFolder, ...this.buildParameters },
|
||||||
false,
|
false,
|
||||||
@@ -184,12 +150,6 @@ find ${sharedFolder} -maxdepth 1 -type f -name "test-*" -exec cp -a {} /github/w
|
|||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Docker doesn't exit on fail now so adding this to ensure behavior is unchanged
|
|
||||||
// TODO: Is there a helpful way to consume the exit code or is it best to except
|
|
||||||
if (exitCode !== 0) {
|
|
||||||
throw new Error(`Build failed with exit code ${exitCode}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return myOutput;
|
return myOutput;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,30 +2,25 @@ import * as k8s from '@kubernetes/client-node';
|
|||||||
import { BuildParameters } from '../../..';
|
import { BuildParameters } from '../../..';
|
||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
import { ProviderInterface } from '../provider-interface';
|
import { ProviderInterface } from '../provider-interface';
|
||||||
import CloudRunnerSecret from '../../options/cloud-runner-secret';
|
import CloudRunnerSecret from '../../services/cloud-runner-secret';
|
||||||
import KubernetesStorage from './kubernetes-storage';
|
import KubernetesStorage from './kubernetes-storage';
|
||||||
import CloudRunnerEnvironmentVariable from '../../options/cloud-runner-environment-variable';
|
import CloudRunnerEnvironmentVariable from '../../services/cloud-runner-environment-variable';
|
||||||
import KubernetesTaskRunner from './kubernetes-task-runner';
|
import KubernetesTaskRunner from './kubernetes-task-runner';
|
||||||
import KubernetesSecret from './kubernetes-secret';
|
import KubernetesSecret from './kubernetes-secret';
|
||||||
import KubernetesJobSpecFactory from './kubernetes-job-spec-factory';
|
import KubernetesJobSpecFactory from './kubernetes-job-spec-factory';
|
||||||
import KubernetesServiceAccount from './kubernetes-service-account';
|
import KubernetesServiceAccount from './kubernetes-service-account';
|
||||||
import CloudRunnerLogger from '../../services/core/cloud-runner-logger';
|
import CloudRunnerLogger from '../../services/cloud-runner-logger';
|
||||||
import { CoreV1Api } from '@kubernetes/client-node';
|
import { CoreV1Api } from '@kubernetes/client-node';
|
||||||
import CloudRunner from '../../cloud-runner';
|
import CloudRunner from '../../cloud-runner';
|
||||||
import { ProviderResource } from '../provider-resource';
|
import { ProviderResource } from '../provider-resource';
|
||||||
import { ProviderWorkflow } from '../provider-workflow';
|
import { ProviderWorkflow } from '../provider-workflow';
|
||||||
import { RemoteClientLogger } from '../../remote-client/remote-client-logger';
|
import KubernetesPods from './kubernetes-pods';
|
||||||
import { KubernetesRole } from './kubernetes-role';
|
|
||||||
import { CloudRunnerSystem } from '../../services/core/cloud-runner-system';
|
|
||||||
import ResourceTracking from '../../services/core/resource-tracking';
|
|
||||||
|
|
||||||
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 = '';
|
||||||
@@ -36,37 +31,18 @@ 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
|
||||||
constructor(buildParameters: BuildParameters) {
|
constructor(buildParameters: BuildParameters) {
|
||||||
Kubernetes.Instance = this;
|
Kubernetes.Instance = this;
|
||||||
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 = buildParameters.containerNamespace ? buildParameters.containerNamespace : '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);
|
||||||
@@ -118,8 +94,16 @@ class Kubernetes implements ProviderInterface {
|
|||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
this.buildParameters = buildParameters;
|
this.buildParameters = buildParameters;
|
||||||
this.cleanupCronJobName = `unity-builder-cronjob-${buildParameters.buildGuid}`;
|
const id = buildParameters.retainWorkspace ? CloudRunner.lockedWorkspace : buildParameters.buildGuid;
|
||||||
|
this.pvcName = `unity-builder-pvc-${id}`;
|
||||||
|
this.cleanupCronJobName = `unity-builder-cronjob-${id}`;
|
||||||
this.serviceAccountName = `service-account-${buildParameters.buildGuid}`;
|
this.serviceAccountName = `service-account-${buildParameters.buildGuid}`;
|
||||||
|
await KubernetesStorage.createPersistentVolumeClaim(
|
||||||
|
buildParameters,
|
||||||
|
this.pvcName,
|
||||||
|
this.kubeClient,
|
||||||
|
this.namespace,
|
||||||
|
);
|
||||||
|
|
||||||
await KubernetesServiceAccount.createServiceAccount(this.serviceAccountName, this.namespace, this.kubeClient);
|
await KubernetesServiceAccount.createServiceAccount(this.serviceAccountName, this.namespace, this.kubeClient);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -138,222 +122,76 @@ class Kubernetes implements ProviderInterface {
|
|||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
try {
|
try {
|
||||||
CloudRunnerLogger.log('Cloud Runner K8s workflow!');
|
CloudRunnerLogger.log('Cloud Runner K8s workflow!');
|
||||||
ResourceTracking.logAllocationSummary('k8s workflow');
|
|
||||||
await ResourceTracking.logDiskUsageSnapshot('k8s workflow (host)');
|
|
||||||
await ResourceTracking.logK3dNodeDiskUsage('k8s workflow (before job)');
|
|
||||||
|
|
||||||
// Setup
|
// Setup
|
||||||
const id =
|
|
||||||
BuildParameters && BuildParameters.shouldUseRetainedWorkspaceMode(this.buildParameters)
|
|
||||||
? CloudRunner.lockedWorkspace
|
|
||||||
: this.buildParameters.buildGuid;
|
|
||||||
this.pvcName = `unity-builder-pvc-${id}`;
|
|
||||||
await KubernetesStorage.createPersistentVolumeClaim(
|
|
||||||
this.buildParameters,
|
|
||||||
this.pvcName,
|
|
||||||
this.kubeClient,
|
|
||||||
this.namespace,
|
|
||||||
);
|
|
||||||
this.buildGuid = buildGuid;
|
this.buildGuid = buildGuid;
|
||||||
this.secretName = `build-credentials-${this.buildGuid}`;
|
this.secretName = `build-credentials-${this.buildGuid}`;
|
||||||
this.jobName = `unity-builder-job-${this.buildGuid}`;
|
this.jobName = `unity-builder-job-${this.buildGuid}`;
|
||||||
this.containerName = `main`;
|
this.containerName = `main`;
|
||||||
await KubernetesSecret.createSecret(secrets, this.secretName, this.namespace, this.kubeClient);
|
await KubernetesSecret.createSecret(secrets, this.secretName, this.namespace, this.kubeClient);
|
||||||
|
await this.createNamespacedJob(commands, image, mountdir, workingdir, environment, secrets);
|
||||||
// For tests, clean up old images before creating job to free space for image pull
|
this.setPodNameAndContainerName(await Kubernetes.findPodFromJob(this.kubeClient, this.jobName, this.namespace));
|
||||||
// IMPORTANT: Preserve the Unity image to avoid re-pulling it
|
CloudRunnerLogger.log('Watching pod until running');
|
||||||
if (process.env['cloudRunnerTests'] === 'true') {
|
await KubernetesTaskRunner.watchUntilPodRunning(this.kubeClient, this.podName, this.namespace);
|
||||||
try {
|
|
||||||
CloudRunnerLogger.log('Cleaning up old images in k3d node before pulling new image...');
|
|
||||||
const { CloudRunnerSystem: CloudRunnerSystemModule } = await import(
|
|
||||||
'../../services/core/cloud-runner-system'
|
|
||||||
);
|
|
||||||
|
|
||||||
// Aggressive cleanup: remove stopped containers and non-Unity images
|
|
||||||
// IMPORTANT: Preserve Unity images (unityci/editor) to avoid re-pulling the 3.9GB image
|
|
||||||
const K3D_NODE_CONTAINERS = ['k3d-unity-builder-agent-0', 'k3d-unity-builder-server-0'];
|
|
||||||
const cleanupCommands: string[] = [];
|
|
||||||
|
|
||||||
for (const NODE of K3D_NODE_CONTAINERS) {
|
|
||||||
// Remove all stopped containers (this frees runtime space but keeps images)
|
|
||||||
cleanupCommands.push(
|
|
||||||
`docker exec ${NODE} sh -c "crictl rm --all 2>/dev/null || true" || true`,
|
|
||||||
`docker exec ${NODE} sh -c "for img in $(crictl images -q 2>/dev/null); do repo=$(crictl inspecti $img --format '{{.repo}}' 2>/dev/null || echo ''); if echo "$repo" | grep -qvE 'unityci/editor|unity'; then crictl rmi $img 2>/dev/null || true; fi; done" || true`,
|
|
||||||
`docker exec ${NODE} sh -c "crictl rmi --prune 2>/dev/null || true" || true`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const cmd of cleanupCommands) {
|
|
||||||
try {
|
|
||||||
await CloudRunnerSystemModule.Run(cmd, true, true);
|
|
||||||
} catch (cmdError) {
|
|
||||||
// Ignore individual command failures - cleanup is best effort
|
|
||||||
CloudRunnerLogger.log(`Cleanup command failed (non-fatal): ${cmdError}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CloudRunnerLogger.log('Cleanup completed (containers and non-Unity images removed, Unity images preserved)');
|
|
||||||
} catch (cleanupError) {
|
|
||||||
CloudRunnerLogger.logWarning(`Failed to cleanup images before job creation: ${cleanupError}`);
|
|
||||||
|
|
||||||
// Continue anyway - image might already be cached
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let output = '';
|
let output = '';
|
||||||
try {
|
// eslint-disable-next-line no-constant-condition
|
||||||
// Before creating the job, verify we have the Unity image cached on the agent node
|
while (true) {
|
||||||
// If not cached, try to ensure it's available to avoid disk pressure during pull
|
try {
|
||||||
if (process.env['cloudRunnerTests'] === 'true' && image.includes('unityci/editor')) {
|
CloudRunnerLogger.log('Pod running, streaming logs');
|
||||||
|
output = await KubernetesTaskRunner.runTask(
|
||||||
|
this.kubeConfig,
|
||||||
|
this.kubeClient,
|
||||||
|
this.jobName,
|
||||||
|
this.podName,
|
||||||
|
'main',
|
||||||
|
this.namespace,
|
||||||
|
);
|
||||||
|
const running = await KubernetesPods.IsPodRunning(this.podName, this.namespace, this.kubeClient);
|
||||||
|
|
||||||
|
if (!running) {
|
||||||
|
CloudRunnerLogger.log(`Pod not found, assumed ended!`);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
CloudRunnerLogger.log('Pod still running, recovering stream...');
|
||||||
|
}
|
||||||
|
await this.cleanupTaskResources();
|
||||||
|
} catch (error: any) {
|
||||||
|
let errorParsed;
|
||||||
try {
|
try {
|
||||||
const { CloudRunnerSystem: CloudRunnerSystemModule2 } = await import(
|
errorParsed = JSON.parse(error);
|
||||||
'../../services/core/cloud-runner-system'
|
} catch {
|
||||||
);
|
errorParsed = error;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if image is cached on agent node (where pods run)
|
const reason = errorParsed.reason || errorParsed.response?.body?.reason || ``;
|
||||||
const agentImageCheck = await CloudRunnerSystemModule2.Run(
|
const errorMessage = errorParsed.message || reason;
|
||||||
`docker exec k3d-unity-builder-agent-0 sh -c "crictl images | grep -q unityci/editor && echo 'cached' || echo 'not_cached'" || echo 'not_cached'`,
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (agentImageCheck.includes('not_cached')) {
|
const continueStreaming =
|
||||||
// Check if image is on server node
|
errorMessage.includes(`dial timeout, backstop`) ||
|
||||||
const serverImageCheck = await CloudRunnerSystemModule2.Run(
|
errorMessage.includes(`HttpError: HTTP request failed`) ||
|
||||||
`docker exec k3d-unity-builder-server-0 sh -c "crictl images | grep -q unityci/editor && echo 'cached' || echo 'not_cached'" || echo 'not_cached'`,
|
errorMessage.includes(`an error occurred when try to find container`) ||
|
||||||
true,
|
errorMessage.includes(`not found`) ||
|
||||||
true,
|
errorMessage.includes(`Not Found`);
|
||||||
);
|
if (continueStreaming) {
|
||||||
|
CloudRunnerLogger.log('Log Stream Container Not Found');
|
||||||
// Check available disk space on agent node
|
await new Promise((resolve) => resolve(5000));
|
||||||
const diskInfo = await CloudRunnerSystemModule2.Run(
|
continue;
|
||||||
'docker exec k3d-unity-builder-agent-0 sh -c "df -h /var/lib/rancher/k3s 2>/dev/null | tail -1 || df -h / 2>/dev/null | tail -1 || echo unknown" || echo unknown',
|
} else {
|
||||||
true,
|
CloudRunnerLogger.log(`error running k8s workflow ${error}`);
|
||||||
true,
|
throw error;
|
||||||
);
|
|
||||||
|
|
||||||
CloudRunnerLogger.logWarning(
|
|
||||||
`Unity image not cached on agent node (where pods run). Server node: ${
|
|
||||||
serverImageCheck.includes('cached') ? 'has image' : 'no image'
|
|
||||||
}. Disk info: ${diskInfo.trim()}. Pod will attempt to pull image (3.9GB) which may fail due to disk pressure.`,
|
|
||||||
);
|
|
||||||
|
|
||||||
// If image is on server but not agent, log a warning
|
|
||||||
// NOTE: We don't attempt to pull here because:
|
|
||||||
// 1. Pulling a 3.9GB image can take several minutes and block the test
|
|
||||||
// 2. If there's not enough disk space, the pull will hang indefinitely
|
|
||||||
// 3. The pod will attempt to pull during scheduling anyway
|
|
||||||
// 4. If the pull fails, Kubernetes will provide proper error messages
|
|
||||||
if (serverImageCheck.includes('cached')) {
|
|
||||||
CloudRunnerLogger.logWarning(
|
|
||||||
'Unity image exists on server node but not agent node. Pod will attempt to pull during scheduling. If pull fails due to disk pressure, ensure cleanup runs before this test.',
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// Image not on either node - check if we have enough space to pull
|
|
||||||
// Extract available space from disk info
|
|
||||||
const availableSpaceMatch = diskInfo.match(/(\d+(?:\.\d+)?)\s*([gkm]?i?b)/i);
|
|
||||||
if (availableSpaceMatch) {
|
|
||||||
const availableValue = Number.parseFloat(availableSpaceMatch[1]);
|
|
||||||
const availableUnit = availableSpaceMatch[2].toUpperCase();
|
|
||||||
let availableGB = availableValue;
|
|
||||||
|
|
||||||
if (availableUnit.includes('M')) {
|
|
||||||
availableGB = availableValue / 1024;
|
|
||||||
} else if (availableUnit.includes('K')) {
|
|
||||||
availableGB = availableValue / (1024 * 1024);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unity image is ~3.9GB, need at least 4.5GB to be safe
|
|
||||||
if (availableGB < 4.5) {
|
|
||||||
CloudRunnerLogger.logWarning(
|
|
||||||
`CRITICAL: Unity image not cached and only ${availableGB.toFixed(
|
|
||||||
2,
|
|
||||||
)}GB available. Image pull (3.9GB) will likely fail. Consider running cleanup or ensuring pre-pull step succeeds.`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
CloudRunnerLogger.log('Unity image is cached on agent node - pod should start without pulling');
|
|
||||||
}
|
|
||||||
} catch (checkError) {
|
|
||||||
// Ignore check errors - continue with job creation
|
|
||||||
CloudRunnerLogger.logWarning(`Failed to verify Unity image cache: ${checkError}`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CloudRunnerLogger.log('Job does not exist');
|
|
||||||
await this.createJob(commands, image, mountdir, workingdir, environment, secrets);
|
|
||||||
CloudRunnerLogger.log('Watching pod until running');
|
|
||||||
await KubernetesTaskRunner.watchUntilPodRunning(this.kubeClient, this.podName, this.namespace);
|
|
||||||
|
|
||||||
CloudRunnerLogger.log('Pod is running');
|
|
||||||
output += await KubernetesTaskRunner.runTask(
|
|
||||||
this.kubeConfig,
|
|
||||||
this.kubeClient,
|
|
||||||
this.jobName,
|
|
||||||
this.podName,
|
|
||||||
this.containerName,
|
|
||||||
this.namespace,
|
|
||||||
);
|
|
||||||
} catch (error: any) {
|
|
||||||
CloudRunnerLogger.log(`error running k8s workflow ${error}`);
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
||||||
CloudRunnerLogger.log(
|
|
||||||
JSON.stringify(
|
|
||||||
(await this.kubeClient.listNamespacedEvent(this.namespace)).body.items
|
|
||||||
.map((x) => {
|
|
||||||
return {
|
|
||||||
message: x.message || ``,
|
|
||||||
name: x.metadata.name || ``,
|
|
||||||
reason: x.reason || ``,
|
|
||||||
};
|
|
||||||
})
|
|
||||||
.filter((x) => x.name.includes(this.podName)),
|
|
||||||
undefined,
|
|
||||||
4,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
await this.cleanupTaskResources();
|
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.cleanupTaskResources();
|
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
CloudRunnerLogger.log('Running job failed');
|
CloudRunnerLogger.log('Running job failed');
|
||||||
core.error(JSON.stringify(error, undefined, 4));
|
core.error(JSON.stringify(error, undefined, 4));
|
||||||
|
await this.cleanupTaskResources();
|
||||||
// await this.cleanupTaskResources();
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async createJob(
|
|
||||||
commands: string,
|
|
||||||
image: string,
|
|
||||||
mountdir: string,
|
|
||||||
workingdir: string,
|
|
||||||
environment: CloudRunnerEnvironmentVariable[],
|
|
||||||
secrets: CloudRunnerSecret[],
|
|
||||||
) {
|
|
||||||
await this.createNamespacedJob(commands, image, mountdir, workingdir, environment, secrets);
|
|
||||||
const find = await Kubernetes.findPodFromJob(this.kubeClient, this.jobName, this.namespace);
|
|
||||||
this.setPodNameAndContainerName(find);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async doesJobExist(name: string) {
|
|
||||||
const jobs = await this.kubeClientBatch.listNamespacedJob(this.namespace);
|
|
||||||
|
|
||||||
return jobs.body.items.some((x) => x.metadata?.name === name);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async doesFailedJobExist() {
|
|
||||||
const podStatus = await this.kubeClient.readNamespacedPodStatus(this.podName, this.namespace);
|
|
||||||
|
|
||||||
return podStatus.body.status?.phase === `Failed`;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async createNamespacedJob(
|
private async createNamespacedJob(
|
||||||
commands: string,
|
commands: string,
|
||||||
image: string,
|
image: string,
|
||||||
@@ -377,19 +215,14 @@ class Kubernetes implements ProviderInterface {
|
|||||||
this.pvcName,
|
this.pvcName,
|
||||||
this.jobName,
|
this.jobName,
|
||||||
k8s,
|
k8s,
|
||||||
this.containerName,
|
|
||||||
this.ip,
|
|
||||||
);
|
);
|
||||||
await new Promise((promise) => setTimeout(promise, 15000));
|
await new Promise((promise) => setTimeout(promise, 15000));
|
||||||
|
await this.kubeClientBatch.createNamespacedJob(this.namespace, jobSpec);
|
||||||
// await KubernetesRole.createRole(this.serviceAccountName, this.namespace, this.rbacAuthorizationV1Api);
|
|
||||||
|
|
||||||
const result = await this.kubeClientBatch.createNamespacedJob(this.namespace, jobSpec);
|
|
||||||
CloudRunnerLogger.log(`Build job created`);
|
CloudRunnerLogger.log(`Build job created`);
|
||||||
await new Promise((promise) => setTimeout(promise, 5000));
|
await new Promise((promise) => setTimeout(promise, 5000));
|
||||||
CloudRunnerLogger.log('Job created');
|
CloudRunnerLogger.log('Job created');
|
||||||
|
|
||||||
return result.body.metadata?.name;
|
return;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
CloudRunnerLogger.log(`Error occured creating job: ${error}`);
|
CloudRunnerLogger.log(`Error occured creating job: ${error}`);
|
||||||
throw error;
|
throw error;
|
||||||
@@ -399,7 +232,7 @@ class Kubernetes implements ProviderInterface {
|
|||||||
|
|
||||||
setPodNameAndContainerName(pod: k8s.V1Pod) {
|
setPodNameAndContainerName(pod: k8s.V1Pod) {
|
||||||
this.podName = pod.metadata?.name || '';
|
this.podName = pod.metadata?.name || '';
|
||||||
this.containerName = pod.status?.containerStatuses?.[0].name || this.containerName;
|
this.containerName = pod.status?.containerStatuses?.[0].name || '';
|
||||||
}
|
}
|
||||||
|
|
||||||
async cleanupTaskResources() {
|
async cleanupTaskResources() {
|
||||||
@@ -407,7 +240,6 @@ 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`) {
|
||||||
@@ -426,13 +258,14 @@ 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 && BuildParameters.shouldUseRetainedWorkspaceMode(buildParameters)) {
|
if (buildParameters.retainWorkspace) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
CloudRunnerLogger.log(`deleting PVC`);
|
CloudRunnerLogger.log(`deleting PVC`);
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import { V1EnvVar, V1EnvVarSource, V1SecretKeySelector } from '@kubernetes/client-node';
|
import { V1EnvVar, V1EnvVarSource, V1SecretKeySelector } from '@kubernetes/client-node';
|
||||||
import BuildParameters from '../../../build-parameters';
|
import BuildParameters from '../../../build-parameters';
|
||||||
import { CommandHookService } from '../../services/hooks/command-hook-service';
|
import { CloudRunnerCustomHooks } from '../../services/cloud-runner-custom-hooks';
|
||||||
import CloudRunnerEnvironmentVariable from '../../options/cloud-runner-environment-variable';
|
import CloudRunnerEnvironmentVariable from '../../services/cloud-runner-environment-variable';
|
||||||
import CloudRunnerSecret from '../../options/cloud-runner-secret';
|
import CloudRunnerSecret from '../../services/cloud-runner-secret';
|
||||||
import CloudRunner from '../../cloud-runner';
|
import CloudRunner from '../../cloud-runner';
|
||||||
import CloudRunnerLogger from '../../services/core/cloud-runner-logger';
|
|
||||||
|
|
||||||
class KubernetesJobSpecFactory {
|
class KubernetesJobSpecFactory {
|
||||||
static getJobSpec(
|
static getJobSpec(
|
||||||
@@ -16,48 +15,67 @@ class KubernetesJobSpecFactory {
|
|||||||
secrets: CloudRunnerSecret[],
|
secrets: CloudRunnerSecret[],
|
||||||
buildGuid: string,
|
buildGuid: string,
|
||||||
buildParameters: BuildParameters,
|
buildParameters: BuildParameters,
|
||||||
secretName: string,
|
secretName,
|
||||||
pvcName: string,
|
pvcName,
|
||||||
jobName: string,
|
jobName,
|
||||||
k8s: any,
|
k8s,
|
||||||
containerName: string,
|
|
||||||
ip: string = '',
|
|
||||||
) {
|
) {
|
||||||
const endpointEnvironmentNames = new Set([
|
environment.push(
|
||||||
'AWS_S3_ENDPOINT',
|
...[
|
||||||
'AWS_ENDPOINT',
|
{
|
||||||
'AWS_CLOUD_FORMATION_ENDPOINT',
|
name: 'GITHUB_SHA',
|
||||||
'AWS_ECS_ENDPOINT',
|
value: buildGuid,
|
||||||
'AWS_KINESIS_ENDPOINT',
|
},
|
||||||
'AWS_CLOUD_WATCH_LOGS_ENDPOINT',
|
{
|
||||||
'INPUT_AWSS3ENDPOINT',
|
name: 'GITHUB_WORKSPACE',
|
||||||
'INPUT_AWSENDPOINT',
|
value: '/data/repo',
|
||||||
]);
|
},
|
||||||
|
{
|
||||||
// Determine the LocalStack hostname to use for K8s pods
|
name: 'PROJECT_PATH',
|
||||||
// Priority: K8S_LOCALSTACK_HOST env var > localstack-main (container name on shared network)
|
value: buildParameters.projectPath,
|
||||||
// Note: Using K8S_LOCALSTACK_HOST instead of LOCALSTACK_HOST to avoid conflict with awslocal CLI
|
},
|
||||||
const localstackHost = process.env['K8S_LOCALSTACK_HOST'] || 'localstack-main';
|
{
|
||||||
CloudRunnerLogger.log(`K8s pods will use LocalStack host: ${localstackHost}`);
|
name: 'BUILD_PATH',
|
||||||
|
value: buildParameters.buildPath,
|
||||||
const adjustedEnvironment = environment.map((x) => {
|
},
|
||||||
let value = x.value;
|
{
|
||||||
if (
|
name: 'BUILD_FILE',
|
||||||
typeof value === 'string' &&
|
value: buildParameters.buildFile,
|
||||||
endpointEnvironmentNames.has(x.name) &&
|
},
|
||||||
(value.startsWith('http://localhost') || value.startsWith('http://127.0.0.1'))
|
{
|
||||||
) {
|
name: 'BUILD_NAME',
|
||||||
// Replace localhost with the LocalStack container hostname
|
value: buildParameters.buildName,
|
||||||
// When k3d and LocalStack are on the same Docker network, pods can reach LocalStack by container name
|
},
|
||||||
value = value
|
{
|
||||||
.replace('http://localhost', `http://${localstackHost}`)
|
name: 'BUILD_METHOD',
|
||||||
.replace('http://127.0.0.1', `http://${localstackHost}`);
|
value: buildParameters.buildMethod,
|
||||||
CloudRunnerLogger.log(`Replaced localhost with ${localstackHost} for ${x.name}: ${value}`);
|
},
|
||||||
}
|
{
|
||||||
|
name: 'CUSTOM_PARAMETERS',
|
||||||
return { name: x.name, value } as CloudRunnerEnvironmentVariable;
|
value: buildParameters.customParameters,
|
||||||
});
|
},
|
||||||
|
{
|
||||||
|
name: 'CHOWN_FILES_TO',
|
||||||
|
value: buildParameters.chownFilesTo,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'BUILD_TARGET',
|
||||||
|
value: buildParameters.targetPlatform,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ANDROID_VERSION_CODE',
|
||||||
|
value: buildParameters.androidVersionCode.toString(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ANDROID_KEYSTORE_NAME',
|
||||||
|
value: buildParameters.androidKeystoreName,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ANDROID_KEYALIAS_NAME',
|
||||||
|
value: buildParameters.androidKeyaliasName,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
);
|
||||||
const job = new k8s.V1Job();
|
const job = new k8s.V1Job();
|
||||||
job.apiVersion = 'batch/v1';
|
job.apiVersion = 'batch/v1';
|
||||||
job.kind = 'Job';
|
job.kind = 'Job';
|
||||||
@@ -68,16 +86,10 @@ class KubernetesJobSpecFactory {
|
|||||||
buildGuid,
|
buildGuid,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Reduce TTL for tests to free up resources faster (default 9999s = ~2.8 hours)
|
|
||||||
// For CI/test environments, use shorter TTL (300s = 5 minutes) to prevent disk pressure
|
|
||||||
const jobTTL = process.env['cloudRunnerTests'] === 'true' ? 300 : 9999;
|
|
||||||
job.spec = {
|
job.spec = {
|
||||||
ttlSecondsAfterFinished: jobTTL,
|
|
||||||
backoffLimit: 0,
|
backoffLimit: 0,
|
||||||
template: {
|
template: {
|
||||||
spec: {
|
spec: {
|
||||||
terminationGracePeriodSeconds: 90, // Give PreStopHook (60s sleep) time to complete
|
|
||||||
volumes: [
|
volumes: [
|
||||||
{
|
{
|
||||||
name: 'build-mount',
|
name: 'build-mount',
|
||||||
@@ -88,44 +100,20 @@ class KubernetesJobSpecFactory {
|
|||||||
],
|
],
|
||||||
containers: [
|
containers: [
|
||||||
{
|
{
|
||||||
ttlSecondsAfterFinished: 9999,
|
name: 'main',
|
||||||
name: containerName,
|
|
||||||
image,
|
image,
|
||||||
imagePullPolicy: process.env['cloudRunnerTests'] === 'true' ? 'IfNotPresent' : 'Always',
|
|
||||||
command: ['/bin/sh'],
|
command: ['/bin/sh'],
|
||||||
args: [
|
args: ['-c', CloudRunnerCustomHooks.ApplyHooksToCommands(command, CloudRunner.buildParameters)],
|
||||||
'-c',
|
|
||||||
`${CommandHookService.ApplyHooksToCommands(`${command}\nsleep 2m`, CloudRunner.buildParameters)}`,
|
|
||||||
],
|
|
||||||
|
|
||||||
workingDir: `${workingDirectory}`,
|
workingDir: `${workingDirectory}`,
|
||||||
resources: {
|
resources: {
|
||||||
requests: (() => {
|
requests: {
|
||||||
// Use smaller resource requests for lightweight hook containers
|
memory: buildParameters.cloudRunnerMemory || '750M',
|
||||||
// Hook containers typically use utility images like aws-cli, rclone, etc.
|
cpu: buildParameters.cloudRunnerCpu || '1',
|
||||||
const lightweightImages = ['amazon/aws-cli', 'rclone/rclone', 'steamcmd/steamcmd', 'ubuntu'];
|
},
|
||||||
const isLightweightContainer = lightweightImages.some((lightImage) => image.includes(lightImage));
|
|
||||||
|
|
||||||
if (isLightweightContainer && process.env['cloudRunnerTests'] === 'true') {
|
|
||||||
// For test environments, use minimal resources for hook containers
|
|
||||||
return {
|
|
||||||
memory: '128Mi',
|
|
||||||
cpu: '100m', // 0.1 CPU
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// For main build containers, use the configured resources
|
|
||||||
const memoryMB = Number.parseInt(buildParameters.containerMemory);
|
|
||||||
const cpuMB = Number.parseInt(buildParameters.containerCpu);
|
|
||||||
|
|
||||||
return {
|
|
||||||
memory: !Number.isNaN(memoryMB) && memoryMB > 0 ? `${memoryMB / 1024}G` : '750M',
|
|
||||||
cpu: !Number.isNaN(cpuMB) && cpuMB > 0 ? `${cpuMB / 1024}` : '1',
|
|
||||||
};
|
|
||||||
})(),
|
|
||||||
},
|
},
|
||||||
env: [
|
env: [
|
||||||
...adjustedEnvironment.map((x) => {
|
...environment.map((x) => {
|
||||||
const environmentVariable = new V1EnvVar();
|
const environmentVariable = new V1EnvVar();
|
||||||
environmentVariable.name = x.name;
|
environmentVariable.name = x.name;
|
||||||
environmentVariable.value = x.value;
|
environmentVariable.value = x.value;
|
||||||
@@ -143,21 +131,22 @@ class KubernetesJobSpecFactory {
|
|||||||
|
|
||||||
return environmentVariable;
|
return environmentVariable;
|
||||||
}),
|
}),
|
||||||
{ name: 'LOG_SERVICE_IP', value: ip },
|
|
||||||
],
|
],
|
||||||
volumeMounts: [
|
volumeMounts: [
|
||||||
{
|
{
|
||||||
name: 'build-mount',
|
name: 'build-mount',
|
||||||
mountPath: `${mountdir}`,
|
mountPath: `/${mountdir}`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
lifecycle: {
|
lifecycle: {
|
||||||
preStop: {
|
preStop: {
|
||||||
exec: {
|
exec: {
|
||||||
command: [
|
command: [
|
||||||
'/bin/sh',
|
'bin/bash',
|
||||||
'-c',
|
'-c',
|
||||||
'sleep 60; cd /data/builder/action/steps && chmod +x /steps/return_license.sh 2>/dev/null || true; /steps/return_license.sh 2>/dev/null || true',
|
`cd /data/builder/action/steps;
|
||||||
|
chmod +x /return_license.sh;
|
||||||
|
/return_license.sh;`,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -165,42 +154,11 @@ class KubernetesJobSpecFactory {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
restartPolicy: 'Never',
|
restartPolicy: 'Never',
|
||||||
|
|
||||||
// Add tolerations for CI/test environments to allow scheduling even with disk pressure
|
|
||||||
// This is acceptable for CI where we aggressively clean up disk space
|
|
||||||
tolerations: [
|
|
||||||
{
|
|
||||||
key: 'node.kubernetes.io/disk-pressure',
|
|
||||||
operator: 'Exists',
|
|
||||||
effect: 'NoSchedule',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (process.env['CLOUD_RUNNER_MINIKUBE']) {
|
job.spec.template.spec.containers[0].resources.requests[`ephemeral-storage`] = '5Gi';
|
||||||
job.spec.template.spec.volumes[0] = {
|
|
||||||
name: 'build-mount',
|
|
||||||
hostPath: {
|
|
||||||
path: `/data`,
|
|
||||||
type: `Directory`,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set ephemeral-storage request to a reasonable value to prevent evictions
|
|
||||||
// For tests, don't set a request (or use minimal 128Mi) since k3d nodes have very limited disk space
|
|
||||||
// Kubernetes will use whatever is available without a request, which is better for constrained environments
|
|
||||||
// For production, use 2Gi to allow for larger builds
|
|
||||||
// The node needs some free space headroom, so requesting too much causes evictions
|
|
||||||
// With node at 96% usage and only ~2.7GB free, we can't request much without triggering evictions
|
|
||||||
if (process.env['cloudRunnerTests'] !== 'true') {
|
|
||||||
// Only set ephemeral-storage request for production builds
|
|
||||||
job.spec.template.spec.containers[0].resources.requests[`ephemeral-storage`] = '2Gi';
|
|
||||||
}
|
|
||||||
|
|
||||||
// For tests, don't set ephemeral-storage request - let Kubernetes use available space
|
|
||||||
|
|
||||||
return job;
|
return job;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import CloudRunnerLogger from '../../services/core/cloud-runner-logger';
|
import CloudRunnerLogger from '../../services/cloud-runner-logger';
|
||||||
import { CoreV1Api } from '@kubernetes/client-node';
|
import { CoreV1Api } from '@kubernetes/client-node';
|
||||||
class KubernetesPods {
|
class KubernetesPods {
|
||||||
public static async IsPodRunning(podName: string, namespace: string, kubeClient: CoreV1Api) {
|
public static async IsPodRunning(podName: string, namespace: string, kubeClient: CoreV1Api) {
|
||||||
@@ -7,188 +7,11 @@ class KubernetesPods {
|
|||||||
const phase = pods[0]?.status?.phase || 'undefined status';
|
const phase = pods[0]?.status?.phase || 'undefined status';
|
||||||
CloudRunnerLogger.log(`Getting pod status: ${phase}`);
|
CloudRunnerLogger.log(`Getting pod status: ${phase}`);
|
||||||
if (phase === `Failed`) {
|
if (phase === `Failed`) {
|
||||||
const pod = pods[0];
|
throw new Error(`K8s pod failed`);
|
||||||
const containerStatuses = pod.status?.containerStatuses || [];
|
|
||||||
const conditions = pod.status?.conditions || [];
|
|
||||||
const events = (await kubeClient.listNamespacedEvent(namespace)).body.items
|
|
||||||
.filter((x) => x.involvedObject?.name === podName)
|
|
||||||
.map((x) => ({
|
|
||||||
message: x.message || '',
|
|
||||||
reason: x.reason || '',
|
|
||||||
type: x.type || '',
|
|
||||||
}));
|
|
||||||
|
|
||||||
const errorDetails: string[] = [];
|
|
||||||
errorDetails.push(`Pod: ${podName}`, `Phase: ${phase}`);
|
|
||||||
|
|
||||||
if (conditions.length > 0) {
|
|
||||||
errorDetails.push(
|
|
||||||
`Conditions: ${JSON.stringify(
|
|
||||||
conditions.map((c) => ({ type: c.type, status: c.status, reason: c.reason, message: c.message })),
|
|
||||||
undefined,
|
|
||||||
2,
|
|
||||||
)}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let containerExitCode: number | undefined;
|
|
||||||
let containerSucceeded = false;
|
|
||||||
|
|
||||||
if (containerStatuses.length > 0) {
|
|
||||||
for (const [index, cs] of containerStatuses.entries()) {
|
|
||||||
if (cs.state?.waiting) {
|
|
||||||
errorDetails.push(
|
|
||||||
`Container ${index} (${cs.name}) waiting: ${cs.state.waiting.reason} - ${cs.state.waiting.message || ''}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (cs.state?.terminated) {
|
|
||||||
const exitCode = cs.state.terminated.exitCode;
|
|
||||||
containerExitCode = exitCode;
|
|
||||||
if (exitCode === 0) {
|
|
||||||
containerSucceeded = true;
|
|
||||||
}
|
|
||||||
errorDetails.push(
|
|
||||||
`Container ${index} (${cs.name}) terminated: ${cs.state.terminated.reason} - ${
|
|
||||||
cs.state.terminated.message || ''
|
|
||||||
} (exit code: ${exitCode})`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (events.length > 0) {
|
|
||||||
errorDetails.push(`Recent events: ${JSON.stringify(events.slice(-5), undefined, 2)}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if only PreStopHook failed but container succeeded
|
|
||||||
const hasPreStopHookFailure = events.some((event) => event.reason === 'FailedPreStopHook');
|
|
||||||
const wasKilled = events.some((event) => event.reason === 'Killing');
|
|
||||||
const hasExceededGracePeriod = events.some((event) => event.reason === 'ExceededGracePeriod');
|
|
||||||
|
|
||||||
// If container succeeded (exit code 0), PreStopHook failure is non-critical
|
|
||||||
// Also check if pod was killed but container might have succeeded
|
|
||||||
if (containerSucceeded && containerExitCode === 0) {
|
|
||||||
// Container succeeded - PreStopHook failure is non-critical
|
|
||||||
if (hasPreStopHookFailure) {
|
|
||||||
CloudRunnerLogger.logWarning(
|
|
||||||
`Pod ${podName} marked as Failed due to PreStopHook failure, but container exited successfully (exit code 0). This is non-fatal.`,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
CloudRunnerLogger.log(
|
|
||||||
`Pod ${podName} container succeeded (exit code 0), but pod phase is Failed. Checking details...`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
CloudRunnerLogger.log(`Pod details: ${errorDetails.join('\n')}`);
|
|
||||||
|
|
||||||
// Don't throw error - container succeeded, PreStopHook failure is non-critical
|
|
||||||
return false; // Pod is not running, but we don't treat it as a failure
|
|
||||||
}
|
|
||||||
|
|
||||||
// If pod was killed and we have PreStopHook failure, wait for container status
|
|
||||||
// The container might have succeeded but status hasn't been updated yet
|
|
||||||
if (wasKilled && hasPreStopHookFailure && (containerExitCode === undefined || !containerSucceeded)) {
|
|
||||||
CloudRunnerLogger.log(
|
|
||||||
`Pod ${podName} was killed with PreStopHook failure. Waiting for container status to determine if container succeeded...`,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Wait a bit for container status to become available (up to 30 seconds)
|
|
||||||
for (let index = 0; index < 6; index++) {
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 5000));
|
|
||||||
try {
|
|
||||||
const updatedPod = (await kubeClient.listNamespacedPod(namespace)).body.items.find(
|
|
||||||
(x) => podName === x.metadata?.name,
|
|
||||||
);
|
|
||||||
if (updatedPod?.status?.containerStatuses && updatedPod.status.containerStatuses.length > 0) {
|
|
||||||
const updatedContainerStatus = updatedPod.status.containerStatuses[0];
|
|
||||||
if (updatedContainerStatus.state?.terminated) {
|
|
||||||
const updatedExitCode = updatedContainerStatus.state.terminated.exitCode;
|
|
||||||
if (updatedExitCode === 0) {
|
|
||||||
CloudRunnerLogger.logWarning(
|
|
||||||
`Pod ${podName} container succeeded (exit code 0) after waiting. PreStopHook failure is non-fatal.`,
|
|
||||||
);
|
|
||||||
|
|
||||||
return false; // Pod is not running, but container succeeded
|
|
||||||
} else {
|
|
||||||
CloudRunnerLogger.log(
|
|
||||||
`Pod ${podName} container failed with exit code ${updatedExitCode} after waiting.`,
|
|
||||||
);
|
|
||||||
errorDetails.push(`Container terminated after wait: exit code ${updatedExitCode}`);
|
|
||||||
containerExitCode = updatedExitCode;
|
|
||||||
containerSucceeded = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (waitError) {
|
|
||||||
CloudRunnerLogger.log(`Error while waiting for container status: ${waitError}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we still don't have container status after waiting, but only PreStopHook failed,
|
|
||||||
// be lenient - the container might have succeeded but status wasn't updated
|
|
||||||
if (containerExitCode === undefined && hasPreStopHookFailure && !hasExceededGracePeriod) {
|
|
||||||
CloudRunnerLogger.logWarning(
|
|
||||||
`Pod ${podName} container status not available after waiting, but only PreStopHook failed (no ExceededGracePeriod). Assuming container may have succeeded.`,
|
|
||||||
);
|
|
||||||
|
|
||||||
return false; // Be lenient - PreStopHook failure alone is not fatal
|
|
||||||
}
|
|
||||||
CloudRunnerLogger.log(
|
|
||||||
`Container status check completed. Exit code: ${containerExitCode}, PreStopHook failure: ${hasPreStopHookFailure}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we only have PreStopHook failure and no actual container failure, be lenient
|
|
||||||
if (hasPreStopHookFailure && !hasExceededGracePeriod && containerExitCode === undefined) {
|
|
||||||
CloudRunnerLogger.logWarning(
|
|
||||||
`Pod ${podName} has PreStopHook failure but no container failure detected. Treating as non-fatal.`,
|
|
||||||
);
|
|
||||||
|
|
||||||
return false; // PreStopHook failure alone is not fatal if container status is unclear
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if pod was evicted due to disk pressure - this is an infrastructure issue
|
|
||||||
const wasEvicted = errorDetails.some(
|
|
||||||
(detail) => detail.toLowerCase().includes('evicted') || detail.toLowerCase().includes('diskpressure'),
|
|
||||||
);
|
|
||||||
if (wasEvicted) {
|
|
||||||
const evictionMessage = `Pod ${podName} was evicted due to disk pressure. This is a test infrastructure issue - the cluster doesn't have enough disk space.`;
|
|
||||||
CloudRunnerLogger.logWarning(evictionMessage);
|
|
||||||
CloudRunnerLogger.log(`Pod details: ${errorDetails.join('\n')}`);
|
|
||||||
throw new Error(
|
|
||||||
`${evictionMessage}\nThis indicates the test environment needs more disk space or better cleanup.\n${errorDetails.join(
|
|
||||||
'\n',
|
|
||||||
)}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exit code 137 (128 + 9) means SIGKILL - container was killed by system (often OOM)
|
|
||||||
// If this happened with PreStopHook failure, it might be a resource issue, not a real failure
|
|
||||||
// Be lenient if we only have PreStopHook/ExceededGracePeriod issues
|
|
||||||
if (containerExitCode === 137 && (hasPreStopHookFailure || hasExceededGracePeriod)) {
|
|
||||||
CloudRunnerLogger.logWarning(
|
|
||||||
`Pod ${podName} was killed (exit code 137 - likely OOM or resource limit) with PreStopHook/grace period issues. This may be a resource constraint issue rather than a build failure.`,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Still log the details but don't fail the test - the build might have succeeded before being killed
|
|
||||||
CloudRunnerLogger.log(`Pod details: ${errorDetails.join('\n')}`);
|
|
||||||
|
|
||||||
return false; // Don't treat system kills as test failures if only PreStopHook issues
|
|
||||||
}
|
|
||||||
|
|
||||||
const errorMessage = `K8s pod failed\n${errorDetails.join('\n')}`;
|
|
||||||
CloudRunnerLogger.log(errorMessage);
|
|
||||||
throw new Error(errorMessage);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return running;
|
return running;
|
||||||
}
|
}
|
||||||
public static async GetPodStatus(podName: string, namespace: string, kubeClient: CoreV1Api) {
|
|
||||||
const pods = (await kubeClient.listNamespacedPod(namespace)).body.items.find((x) => podName === x.metadata?.name);
|
|
||||||
const phase = pods?.status?.phase || 'undefined status';
|
|
||||||
|
|
||||||
return phase;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default KubernetesPods;
|
export default KubernetesPods;
|
||||||
|
|||||||
@@ -1,53 +0,0 @@
|
|||||||
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 };
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user