mirror of
https://github.com/game-ci/unity-builder.git
synced 2025-07-04 12:25:19 -04:00
Cloud runner develop - Stabilizes kubernetes provider (#531)
* fixes * fixes * fixes * fixes * fixes * check for startup message in workflows * check for startup message in workflows * check for startup message in workflows * check for startup message in workflows * check for startup message in workflows * check for startup message in workflows * Update cloud-runner-ci-pipeline.yml * Update cloud-runner-ci-pipeline.yml * no storage class specified * log file path * log file path * log file path * log file path * log file path * log file path * log file path * log file path * updates * log file path * latest develop * log file path * log file path * Update package.json * log file path * log file path * log file path * log file path * log file path * log file path * log file path * log file path * log file path * log file path * log file path * log file path * log file path * log file path * stream logs through standard input and new remote client cli command * stream logs through standard input and new remote client cli command * stream logs through standard input and new remote client cli command * stream logs through standard input and new remote client cli command * stream logs through standard input and new remote client cli command * stream logs through standard input and new remote client cli command * stream logs through standard input and new remote client cli command * stream logs through standard input and new remote client cli command * stream logs through standard input and new remote client cli command * stream logs through standard input and new remote client cli command * stream logs through standard input and new remote client cli command * stream logs through standard input and new remote client cli command * stream logs through standard input and new remote client cli command * stream logs through standard input and new remote client cli command * stream logs through standard input and new remote client cli command * update pipeline to use k3s * version: 'latest' * fixes * disable aws pipe for now * disable aws pipe for now * disable aws pipe for now * disable aws pipe for now * disable aws pipe for now * disable aws pipe for now * disable aws pipe for now * disable aws pipe for now * disable aws pipe for now * disable aws pipe for now * push k8s logs to LOG SERVICE IP * push k8s logs to LOG SERVICE IP * push k8s logs to LOG SERVICE IP * push k8s logs to LOG SERVICE IP * push k8s logs to LOG SERVICE IP * push k8s logs to LOG SERVICE IP * push k8s logs to LOG SERVICE IP * push k8s logs to LOG SERVICE IP * tests * tests * tests * tests * tests * tests * tests * tests * tests * tests * tests * tests * tests * tests * tests * tests * tests * podname logs for log service * podname logs for log service * podname logs for log service * podname logs for log service * podname logs for log service * podname logs for log service * podname logs for log service * podname logs for log service * podname logs for log service * hashed logs * hashed logs * hashed logs * hashed logs * hashed logs * hashed logs * no wait, just repeat logs * no wait, just repeat logs * remove typo - double await * test fix - kubernetes - name typo in github yaml * test fix - kubernetes - name typo in github yaml * check missing log file * check missing log file * Push to steam test * Push to steam test * Fix path * k8s reliable log hashing * k8s reliable log hashing * k8s reliable log hashing * hashed logging k8s * hashed logging k8s * hashed logging k8s * hashed logging k8s * hashed logging k8s * hashed logging k8s * Include log chunk when task runner sees log update, clarify if we can pull logs from same line or next line * Include log chunk when task runner sees log update, clarify if we can pull logs from same line or next line * Include log chunk when task runner sees log update, clarify if we can pull logs from same line or next line * Include log chunk when task runner sees log update, clarify if we can pull logs from same line or next line * Include log chunk when task runner sees log update, clarify if we can pull logs from same line or next line * Fix exit flow for k8s job * hash comparison logging for log complete in k8s flow * Interrupt k8s logs when logs found * cleanup async parameter * cleanup async parameter * cleanup async parameter * fixes * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix * fix
This commit is contained in:
parent
2800d14403
commit
e73b48fb38
@ -1,22 +1,11 @@
|
|||||||
{
|
{
|
||||||
"plugins": [
|
"plugins": ["jest", "@typescript-eslint", "prettier", "unicorn"],
|
||||||
"jest",
|
"extends": ["plugin:unicorn/recommended", "plugin:github/recommended", "plugin:prettier/recommended"],
|
||||||
"@typescript-eslint",
|
|
||||||
"prettier",
|
|
||||||
"unicorn"
|
|
||||||
],
|
|
||||||
"extends": [
|
|
||||||
"plugin:unicorn/recommended",
|
|
||||||
"plugin:github/recommended",
|
|
||||||
"plugin:prettier/recommended"
|
|
||||||
],
|
|
||||||
"parser": "@typescript-eslint/parser",
|
"parser": "@typescript-eslint/parser",
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
"ecmaVersion": 2020,
|
"ecmaVersion": 2020,
|
||||||
"sourceType": "module",
|
"sourceType": "module",
|
||||||
"extraFileExtensions": [
|
"extraFileExtensions": [".mjs"],
|
||||||
".mjs"
|
|
||||||
],
|
|
||||||
"ecmaFeatures": {
|
"ecmaFeatures": {
|
||||||
"impliedStrict": true
|
"impliedStrict": true
|
||||||
},
|
},
|
||||||
@ -33,10 +22,7 @@
|
|||||||
// Namespaces or sometimes needed
|
// Namespaces or sometimes needed
|
||||||
"import/no-namespace": "off",
|
"import/no-namespace": "off",
|
||||||
// Properly format comments
|
// Properly format comments
|
||||||
"spaced-comment": [
|
"spaced-comment": ["error", "always"],
|
||||||
"error",
|
|
||||||
"always"
|
|
||||||
],
|
|
||||||
"lines-around-comment": [
|
"lines-around-comment": [
|
||||||
"error",
|
"error",
|
||||||
{
|
{
|
||||||
@ -71,12 +57,7 @@
|
|||||||
// Enforce camelCase
|
// Enforce camelCase
|
||||||
"camelcase": "error",
|
"camelcase": "error",
|
||||||
// Allow forOfStatements
|
// Allow forOfStatements
|
||||||
"no-restricted-syntax": [
|
"no-restricted-syntax": ["error", "ForInStatement", "LabeledStatement", "WithStatement"],
|
||||||
"error",
|
|
||||||
"ForInStatement",
|
|
||||||
"LabeledStatement",
|
|
||||||
"WithStatement"
|
|
||||||
],
|
|
||||||
// Continue is viable in forOf loops in generators
|
// Continue is viable in forOf loops in generators
|
||||||
"no-continue": "off",
|
"no-continue": "off",
|
||||||
// From experience, named exports are almost always desired. I got tired of this rule
|
// From experience, named exports are almost always desired. I got tired of this rule
|
||||||
|
154
.github/workflows/cloud-runner-ci-pipeline.yml
vendored
154
.github/workflows/cloud-runner-ci-pipeline.yml
vendored
@ -18,42 +18,37 @@ env:
|
|||||||
GCP_PROJECT: unitykubernetesbuilder
|
GCP_PROJECT: unitykubernetesbuilder
|
||||||
GCP_LOG_FILE: ${{ github.workspace }}/cloud-runner-logs.txt
|
GCP_LOG_FILE: ${{ github.workspace }}/cloud-runner-logs.txt
|
||||||
AWS_REGION: eu-west-2
|
AWS_REGION: eu-west-2
|
||||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
|
||||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
|
||||||
AWS_DEFAULT_REGION: eu-west-2
|
AWS_DEFAULT_REGION: eu-west-2
|
||||||
AWS_STACK_NAME: game-ci-team-pipelines
|
AWS_STACK_NAME: game-ci-team-pipelines
|
||||||
CLOUD_RUNNER_BRANCH: ${{ github.ref }}
|
CLOUD_RUNNER_BRANCH: ${{ github.ref }}
|
||||||
DEBUG: true
|
DEBUG: true
|
||||||
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
|
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
|
||||||
|
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
|
||||||
|
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
|
||||||
PROJECT_PATH: test-project
|
PROJECT_PATH: test-project
|
||||||
UNITY_VERSION: 2019.3.15f1
|
UNITY_VERSION: 2019.3.15f1
|
||||||
USE_IL2CPP: false
|
USE_IL2CPP: false
|
||||||
USE_GKE_GCLOUD_AUTH_PLUGIN: true
|
USE_GKE_GCLOUD_AUTH_PLUGIN: true
|
||||||
GIT_PRIVATE_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }}
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
smokeTests:
|
tests:
|
||||||
name: Smoke Tests
|
name: Tests
|
||||||
if: github.event.event_type != 'pull_request_target'
|
if: github.event.event_type != 'pull_request_target'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
test:
|
test:
|
||||||
#- 'cloud-runner-async-workflow'
|
- 'cloud-runner-end2end-locking'
|
||||||
|
- 'cloud-runner-end2end-caching'
|
||||||
|
- 'cloud-runner-end2end-retaining'
|
||||||
- 'cloud-runner-caching'
|
- 'cloud-runner-caching'
|
||||||
# - 'cloud-runner-end2end-caching'
|
|
||||||
# - 'cloud-runner-end2end-retaining'
|
|
||||||
- 'cloud-runner-environment'
|
- 'cloud-runner-environment'
|
||||||
|
- 'cloud-runner-image'
|
||||||
- 'cloud-runner-hooks'
|
- 'cloud-runner-hooks'
|
||||||
- 'cloud-runner-local-persistence'
|
- 'cloud-runner-local-persistence'
|
||||||
- 'cloud-runner-locking-core'
|
- 'cloud-runner-locking-core'
|
||||||
- 'cloud-runner-locking-get-locked'
|
- 'cloud-runner-locking-get-locked'
|
||||||
providerStrategy:
|
|
||||||
#- aws
|
|
||||||
- local-docker
|
|
||||||
#- k8s
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout (default)
|
- name: Checkout (default)
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@ -65,58 +60,85 @@ jobs:
|
|||||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||||
aws-region: eu-west-2
|
aws-region: eu-west-2
|
||||||
- uses: google-github-actions/auth@v1
|
|
||||||
if: matrix.providerStrategy == 'k8s'
|
|
||||||
with:
|
|
||||||
credentials_json: ${{ secrets.GOOGLE_SERVICE_ACCOUNT_KEY }}
|
|
||||||
- name: 'Set up Cloud SDK'
|
|
||||||
if: matrix.providerStrategy == 'k8s'
|
|
||||||
uses: 'google-github-actions/setup-gcloud@v1.1.0'
|
|
||||||
- name: Get GKE cluster credentials
|
|
||||||
if: matrix.providerStrategy == 'k8s'
|
|
||||||
run: |
|
|
||||||
export USE_GKE_GCLOUD_AUTH_PLUGIN=True
|
|
||||||
gcloud components install gke-gcloud-auth-plugin
|
|
||||||
gcloud container clusters get-credentials $GKE_CLUSTER --zone $GKE_ZONE --project $GKE_PROJECT
|
|
||||||
- run: yarn
|
- run: yarn
|
||||||
- run: yarn run test "${{ matrix.test }}" --detectOpenHandles --forceExit --runInBand
|
- run: yarn run test "${{ matrix.test }}" --detectOpenHandles --forceExit --runInBand
|
||||||
timeout-minutes: 35
|
timeout-minutes: 60
|
||||||
env:
|
env:
|
||||||
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
|
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
|
||||||
|
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
|
||||||
|
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
|
||||||
PROJECT_PATH: test-project
|
PROJECT_PATH: test-project
|
||||||
TARGET_PLATFORM: StandaloneWindows64
|
TARGET_PLATFORM: StandaloneWindows64
|
||||||
cloudRunnerTests: true
|
cloudRunnerTests: true
|
||||||
versioning: None
|
versioning: None
|
||||||
CLOUD_RUNNER_CLUSTER: ${{ matrix.providerStrategy }}
|
KUBE_STORAGE_CLASS: local-path
|
||||||
tests:
|
PROVIDER_STRATEGY: local-docker
|
||||||
# needs:
|
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||||
# - smokeTests
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||||
# - buildTargetTests
|
GIT_PRIVATE_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }}
|
||||||
name: Integration Tests
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- run: |
|
||||||
|
docker volume rm $(docker volume ls -qf dangling=true)
|
||||||
|
docker rmi $(docker images -q -f dangling=true)
|
||||||
|
docker rm $(docker ps -aqf status=exited)
|
||||||
|
docker rmi $(docker images -q)
|
||||||
|
k8sTests:
|
||||||
|
name: K8s Tests
|
||||||
|
if: github.event.event_type != 'pull_request_target'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
test:
|
||||||
|
# - 'cloud-runner-async-workflow'
|
||||||
|
- 'cloud-runner-end2end-locking'
|
||||||
|
- 'cloud-runner-end2end-caching'
|
||||||
|
- 'cloud-runner-end2end-retaining'
|
||||||
|
- 'cloud-runner-kubernetes'
|
||||||
|
- 'cloud-runner-environment'
|
||||||
|
- 'cloud-runner-github-checks'
|
||||||
|
steps:
|
||||||
|
- name: Checkout (default)
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
lfs: false
|
||||||
|
- run: yarn
|
||||||
|
- name: actions-k3s
|
||||||
|
uses: debianmaster/actions-k3s@v1.0.5
|
||||||
|
with:
|
||||||
|
version: 'latest'
|
||||||
|
- run: yarn run test "${{ matrix.test }}" --detectOpenHandles --forceExit --runInBand
|
||||||
|
timeout-minutes: 60
|
||||||
|
env:
|
||||||
|
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
|
||||||
|
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
|
||||||
|
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
|
||||||
|
PROJECT_PATH: test-project
|
||||||
|
TARGET_PLATFORM: StandaloneWindows64
|
||||||
|
cloudRunnerTests: true
|
||||||
|
versioning: None
|
||||||
|
KUBE_STORAGE_CLASS: local-path
|
||||||
|
PROVIDER_STRATEGY: k8s
|
||||||
|
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||||
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||||
|
GIT_PRIVATE_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }}
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
awsTests:
|
||||||
|
name: AWS Tests
|
||||||
if: github.event.event_type != 'pull_request_target'
|
if: github.event.event_type != 'pull_request_target'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
providerStrategy:
|
|
||||||
- aws
|
|
||||||
- local-docker
|
|
||||||
- k8s
|
|
||||||
test:
|
test:
|
||||||
- 'cloud-runner-async-workflow'
|
|
||||||
#- 'cloud-runner-caching'
|
|
||||||
- 'cloud-runner-end2end-locking'
|
- 'cloud-runner-end2end-locking'
|
||||||
- 'cloud-runner-end2end-caching'
|
- 'cloud-runner-end2end-caching'
|
||||||
- 'cloud-runner-end2end-retaining'
|
- 'cloud-runner-end2end-retaining'
|
||||||
- 'cloud-runner-environment'
|
- 'cloud-runner-environment'
|
||||||
#- 'cloud-runner-hooks'
|
|
||||||
- 'cloud-runner-s3-steps'
|
- 'cloud-runner-s3-steps'
|
||||||
#- 'cloud-runner-local-persistence'
|
|
||||||
#- 'cloud-runner-locking-core'
|
|
||||||
#- 'cloud-runner-locking-get-locked'
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout (default)
|
- name: Checkout (default)
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
lfs: false
|
lfs: false
|
||||||
- name: Configure AWS Credentials
|
- name: Configure AWS Credentials
|
||||||
@ -125,29 +147,24 @@ jobs:
|
|||||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||||
aws-region: eu-west-2
|
aws-region: eu-west-2
|
||||||
- uses: google-github-actions/auth@v1
|
|
||||||
if: matrix.providerStrategy == 'k8s'
|
|
||||||
with:
|
|
||||||
credentials_json: ${{ secrets.GOOGLE_SERVICE_ACCOUNT_KEY }}
|
|
||||||
- name: 'Set up Cloud SDK'
|
|
||||||
if: matrix.providerStrategy == 'k8s'
|
|
||||||
uses: 'google-github-actions/setup-gcloud@v1.1.0'
|
|
||||||
- name: Get GKE cluster credentials
|
|
||||||
if: matrix.providerStrategy == 'k8s'
|
|
||||||
run: |
|
|
||||||
export USE_GKE_GCLOUD_AUTH_PLUGIN=True
|
|
||||||
gcloud components install gke-gcloud-auth-plugin
|
|
||||||
gcloud container clusters get-credentials $GKE_CLUSTER --zone $GKE_ZONE --project $GKE_PROJECT
|
|
||||||
- run: yarn
|
- run: yarn
|
||||||
- run: yarn run test "${{ matrix.test }}" --detectOpenHandles --forceExit --runInBand
|
- run: yarn run test "${{ matrix.test }}" --detectOpenHandles --forceExit --runInBand
|
||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
env:
|
env:
|
||||||
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
|
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
|
||||||
|
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
|
||||||
|
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
|
||||||
PROJECT_PATH: test-project
|
PROJECT_PATH: test-project
|
||||||
TARGET_PLATFORM: StandaloneWindows64
|
TARGET_PLATFORM: StandaloneWindows64
|
||||||
cloudRunnerTests: true
|
cloudRunnerTests: true
|
||||||
versioning: None
|
versioning: None
|
||||||
PROVIDER_STRATEGY: ${{ matrix.providerStrategy }}
|
KUBE_STORAGE_CLASS: local-path
|
||||||
|
PROVIDER_STRATEGY: aws
|
||||||
|
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||||
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||||
|
GIT_PRIVATE_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }}
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
buildTargetTests:
|
buildTargetTests:
|
||||||
name: Local Build Target Tests
|
name: Local Build Target Tests
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@ -164,7 +181,7 @@ jobs:
|
|||||||
- StandaloneLinux64 # Build a Linux 64-bit standalone.
|
- StandaloneLinux64 # Build a Linux 64-bit standalone.
|
||||||
- WebGL # WebGL.
|
- WebGL # WebGL.
|
||||||
- iOS # Build an iOS player.
|
- iOS # Build an iOS player.
|
||||||
- Android # Build an Android .apk.
|
# - Android # Build an Android .apk.
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout (default)
|
- name: Checkout (default)
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@ -175,7 +192,13 @@ jobs:
|
|||||||
id: unity-build
|
id: unity-build
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
env:
|
env:
|
||||||
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
|
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
|
||||||
|
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
|
||||||
|
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
|
||||||
|
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||||
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||||
|
GIT_PRIVATE_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }}
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
with:
|
with:
|
||||||
cloudRunnerTests: true
|
cloudRunnerTests: true
|
||||||
versioning: None
|
versioning: None
|
||||||
@ -188,3 +211,8 @@ jobs:
|
|||||||
name: ${{ matrix.providerStrategy }} Build (${{ matrix.targetPlatform }})
|
name: ${{ matrix.providerStrategy }} Build (${{ matrix.targetPlatform }})
|
||||||
path: ${{ steps.unity-build.outputs.BUILD_ARTIFACT }}
|
path: ${{ steps.unity-build.outputs.BUILD_ARTIFACT }}
|
||||||
retention-days: 14
|
retention-days: 14
|
||||||
|
- run: |
|
||||||
|
docker volume rm $(docker volume ls -qf dangling=true)
|
||||||
|
docker rmi $(docker images -q -f dangling=true)
|
||||||
|
docker rm $(docker ps -aqf status=exited)
|
||||||
|
docker rmi $(docker images -q)
|
||||||
|
@ -186,11 +186,11 @@ inputs:
|
|||||||
description:
|
description:
|
||||||
'[CloudRunner] Either local, k8s or aws can be used to run builds on a remote cluster. Additional parameters must
|
'[CloudRunner] Either local, k8s or aws can be used to run builds on a remote cluster. Additional parameters must
|
||||||
be configured.'
|
be configured.'
|
||||||
cloudRunnerCpu:
|
containerCpu:
|
||||||
default: ''
|
default: ''
|
||||||
required: false
|
required: false
|
||||||
description: '[CloudRunner] Amount of CPU time to assign the remote build container'
|
description: '[CloudRunner] Amount of CPU time to assign the remote build container'
|
||||||
cloudRunnerMemory:
|
containerMemory:
|
||||||
default: ''
|
default: ''
|
||||||
required: false
|
required: false
|
||||||
description: '[CloudRunner] Amount of memory to assign the remote build container'
|
description: '[CloudRunner] Amount of memory to assign the remote build container'
|
||||||
|
BIN
dist/index.js
generated
vendored
BIN
dist/index.js
generated
vendored
Binary file not shown.
BIN
dist/index.js.map
generated
vendored
BIN
dist/index.js.map
generated
vendored
Binary file not shown.
9
dist/platforms/ubuntu/steps/activate.sh
vendored
9
dist/platforms/ubuntu/steps/activate.sh
vendored
@ -1,5 +1,14 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# if blankproject folder doesn't exist create it
|
||||||
|
if [ ! -d "/BlankProject" ]; then
|
||||||
|
mkdir /BlankProject
|
||||||
|
fi
|
||||||
|
# if blankproject folder doesn't exist create it
|
||||||
|
if [ ! -d "/BlankProject/Assets" ]; then
|
||||||
|
mkdir /BlankProject/Assets
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ -n "$UNITY_SERIAL" && -n "$UNITY_EMAIL" && -n "$UNITY_PASSWORD" ]]; then
|
if [[ -n "$UNITY_SERIAL" && -n "$UNITY_EMAIL" && -n "$UNITY_PASSWORD" ]]; then
|
||||||
#
|
#
|
||||||
# SERIAL LICENSE MODE
|
# SERIAL LICENSE MODE
|
||||||
|
12
package.json
12
package.json
@ -7,14 +7,14 @@
|
|||||||
"author": "Webber <webber@takken.io>",
|
"author": "Webber <webber@takken.io>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepare": "lefthook install && npx husky uninstall -y",
|
"prepare": "lefthook install",
|
||||||
"build": "yarn && tsc && ncc build lib --source-map --license licenses.txt",
|
"build": "yarn && tsc && ncc build lib --source-map --license licenses.txt",
|
||||||
"lint": "prettier --check \"src/**/*.{js,ts}\" && eslint src/**/*.ts",
|
"lint": "prettier --check \"src/**/*.{js,ts}\" && eslint src/**/*.ts",
|
||||||
"format": "prettier --write \"src/**/*.{js,ts}\"",
|
"format": "prettier --write \"src/**/*.{js,ts}\"",
|
||||||
"cli": "yarn ts-node src/index.ts -m cli",
|
"cli": "yarn ts-node src/index.ts -m cli",
|
||||||
"gcp-secrets-tests": "cross-env providerStrategy=aws cloudRunnerTests=true readInputOverrideCommand=\"gcp-secret-manager\" populateOverride=true readInputFromOverrideList=UNITY_EMAIL,UNITY_SERIAL,UNITY_PASSWORD yarn test -i -t \"cloud runner\"",
|
"gcp-secrets-tests": "cross-env providerStrategy=aws cloudRunnerTests=true inputPullCommand=\"gcp-secret-manager\" populateOverride=true pullInputList=UNITY_EMAIL,UNITY_SERIAL,UNITY_PASSWORD yarn test -i -t \"cloud runner\"",
|
||||||
"gcp-secrets-cli": "cross-env cloudRunnerTests=true readInputOverrideCommand=\"gcp-secret-manager\" yarn ts-node src/index.ts -m cli --populateOverride true --readInputFromOverrideList UNITY_EMAIL,UNITY_SERIAL,UNITY_PASSWORD",
|
"gcp-secrets-cli": "cross-env cloudRunnerTests=true USE_IL2CPP=false inputPullCommand=\"gcp-secret-manager\" yarn ts-node src/index.ts -m cli --populateOverride true --pullInputList UNITY_EMAIL,UNITY_SERIAL,UNITY_PASSWORD",
|
||||||
"aws-secrets-cli": "cross-env cloudRunnerTests=true readInputOverrideCommand=\"aws-secret-manager\" yarn ts-node src/index.ts -m cli --populateOverride true --readInputFromOverrideList UNITY_EMAIL,UNITY_SERIAL,UNITY_PASSWORD",
|
"aws-secrets-cli": "cross-env cloudRunnerTests=true inputPullCommand=\"aws-secret-manager\" yarn ts-node src/index.ts -m cli --populateOverride true --pullInputList UNITY_EMAIL,UNITY_SERIAL,UNITY_PASSWORD",
|
||||||
"cli-aws": "cross-env providerStrategy=aws yarn run test-cli",
|
"cli-aws": "cross-env providerStrategy=aws yarn run test-cli",
|
||||||
"cli-k8s": "cross-env providerStrategy=k8s yarn run test-cli",
|
"cli-k8s": "cross-env providerStrategy=k8s yarn run test-cli",
|
||||||
"test-cli": "cross-env cloudRunnerTests=true yarn ts-node src/index.ts -m cli --projectPath test-project",
|
"test-cli": "cross-env cloudRunnerTests=true yarn ts-node src/index.ts -m cli --projectPath test-project",
|
||||||
@ -40,9 +40,11 @@
|
|||||||
"commander": "^9.0.0",
|
"commander": "^9.0.0",
|
||||||
"commander-ts": "^0.2.0",
|
"commander-ts": "^0.2.0",
|
||||||
"kubernetes-client": "^9.0.0",
|
"kubernetes-client": "^9.0.0",
|
||||||
|
"md5": "^2.3.0",
|
||||||
"nanoid": "^3.3.1",
|
"nanoid": "^3.3.1",
|
||||||
"reflect-metadata": "^0.1.13",
|
"reflect-metadata": "^0.1.13",
|
||||||
"semver": "^7.5.2",
|
"semver": "^7.5.2",
|
||||||
|
"ts-md5": "^1.3.1",
|
||||||
"unity-changeset": "^2.0.0",
|
"unity-changeset": "^2.0.0",
|
||||||
"uuid": "^9.0.0",
|
"uuid": "^9.0.0",
|
||||||
"yaml": "^2.2.2"
|
"yaml": "^2.2.2"
|
||||||
@ -69,7 +71,7 @@
|
|||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"prettier": "^2.5.1",
|
"prettier": "^2.5.1",
|
||||||
"ts-jest": "^27.1.3",
|
"ts-jest": "^27.1.3",
|
||||||
"ts-node": "10.4.0",
|
"ts-node": "10.8.1",
|
||||||
"typescript": "4.7.4",
|
"typescript": "4.7.4",
|
||||||
"yarn-audit-fix": "^9.3.8"
|
"yarn-audit-fix": "^9.3.8"
|
||||||
},
|
},
|
||||||
|
15
scripts/game-ci.bat
Normal file
15
scripts/game-ci.bat
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
echo "installing game-ci cli"
|
||||||
|
if exist %UserProfile%\AppData\LocalLow\game-ci\ (
|
||||||
|
echo Installed Updating
|
||||||
|
git -C %UserProfile%\AppData\LocalLow\game-ci\ fetch
|
||||||
|
git -C %UserProfile%\AppData\LocalLow\game-ci\ reset --hard
|
||||||
|
git -C %UserProfile%\AppData\LocalLow\game-ci\ pull
|
||||||
|
git -C %UserProfile%\AppData\LocalLow\game-ci\ branch
|
||||||
|
) else (
|
||||||
|
echo Not Installed Downloading...
|
||||||
|
mkdir %UserProfile%\AppData\LocalLow\game-ci\
|
||||||
|
git clone https://github.com/game-ci/unity-builder %UserProfile%\AppData\LocalLow\game-ci\
|
||||||
|
)
|
||||||
|
|
||||||
|
call yarn --cwd %UserProfile%\AppData\LocalLow\game-ci\ install
|
||||||
|
call yarn --cwd %UserProfile%\AppData\LocalLow\game-ci\ run gcp-secrets-cli %* --projectPath %cd% --awsStackName game-ci-cli
|
@ -34,6 +34,7 @@ async function runMain() {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
await CloudRunner.run(buildParameters, baseImage.toString());
|
await CloudRunner.run(buildParameters, baseImage.toString());
|
||||||
|
exitCode = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set output
|
// Set output
|
||||||
|
@ -10,8 +10,6 @@ import { LfsHashing } from '../cloud-runner/services/utility/lfs-hashing';
|
|||||||
import { RemoteClient } from '../cloud-runner/remote-client';
|
import { RemoteClient } from '../cloud-runner/remote-client';
|
||||||
import CloudRunnerOptionsReader from '../cloud-runner/options/cloud-runner-options-reader';
|
import CloudRunnerOptionsReader from '../cloud-runner/options/cloud-runner-options-reader';
|
||||||
import GitHub from '../github';
|
import GitHub from '../github';
|
||||||
import { CloudRunnerFolders } from '../cloud-runner/options/cloud-runner-folders';
|
|
||||||
import { CloudRunnerSystem } from '../cloud-runner/services/core/cloud-runner-system';
|
|
||||||
import { OptionValues } from 'commander';
|
import { OptionValues } from 'commander';
|
||||||
import { InputKey } from '../input';
|
import { InputKey } from '../input';
|
||||||
|
|
||||||
@ -54,6 +52,7 @@ export class Cli {
|
|||||||
program.option('--cachePushTo <cachePushTo>', 'cache push to caching folder');
|
program.option('--cachePushTo <cachePushTo>', 'cache push to caching folder');
|
||||||
program.option('--artifactName <artifactName>', 'caching artifact name');
|
program.option('--artifactName <artifactName>', 'caching artifact name');
|
||||||
program.option('--select <select>', 'select a particular resource');
|
program.option('--select <select>', 'select a particular resource');
|
||||||
|
program.option('--logFile <logFile>', 'output to log file (log stream only)');
|
||||||
program.parse(process.argv);
|
program.parse(process.argv);
|
||||||
Cli.options = program.opts();
|
Cli.options = program.opts();
|
||||||
|
|
||||||
@ -110,7 +109,7 @@ export class Cli {
|
|||||||
const buildParameter = await BuildParameters.create();
|
const buildParameter = await BuildParameters.create();
|
||||||
const baseImage = new ImageTag(buildParameter);
|
const baseImage = new ImageTag(buildParameter);
|
||||||
|
|
||||||
return await CloudRunner.run(buildParameter, baseImage.toString());
|
return (await CloudRunner.run(buildParameter, baseImage.toString())).BuildResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
@CliFunction(`async-workflow`, `runs a cloud runner build`)
|
@CliFunction(`async-workflow`, `runs a cloud runner build`)
|
||||||
@ -119,7 +118,7 @@ export class Cli {
|
|||||||
const baseImage = new ImageTag(buildParameter);
|
const baseImage = new ImageTag(buildParameter);
|
||||||
await CloudRunner.setup(buildParameter);
|
await CloudRunner.setup(buildParameter);
|
||||||
|
|
||||||
return await CloudRunner.run(buildParameter, baseImage.toString());
|
return (await CloudRunner.run(buildParameter, baseImage.toString())).BuildResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
@CliFunction(`checks-update`, `runs a cloud runner build`)
|
@CliFunction(`checks-update`, `runs a cloud runner build`)
|
||||||
@ -173,31 +172,4 @@ export class Cli {
|
|||||||
|
|
||||||
return await CloudRunner.Provider.watchWorkflow();
|
return await CloudRunner.Provider.watchWorkflow();
|
||||||
}
|
}
|
||||||
|
|
||||||
@CliFunction(`remote-cli-post-build`, `runs a cloud runner build`)
|
|
||||||
public static async PostCLIBuild(): Promise<string> {
|
|
||||||
core.info(`Running POST build tasks`);
|
|
||||||
|
|
||||||
await Caching.PushToCache(
|
|
||||||
CloudRunnerFolders.ToLinuxFolder(`${CloudRunnerFolders.cacheFolderForCacheKeyFull}/Library`),
|
|
||||||
CloudRunnerFolders.ToLinuxFolder(CloudRunnerFolders.libraryFolderAbsolute),
|
|
||||||
`lib-${CloudRunner.buildParameters.buildGuid}`,
|
|
||||||
);
|
|
||||||
|
|
||||||
await Caching.PushToCache(
|
|
||||||
CloudRunnerFolders.ToLinuxFolder(`${CloudRunnerFolders.cacheFolderForCacheKeyFull}/build`),
|
|
||||||
CloudRunnerFolders.ToLinuxFolder(CloudRunnerFolders.projectBuildFolderAbsolute),
|
|
||||||
`build-${CloudRunner.buildParameters.buildGuid}`,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!BuildParameters.shouldUseRetainedWorkspaceMode(CloudRunner.buildParameters)) {
|
|
||||||
await CloudRunnerSystem.Run(
|
|
||||||
`rm -r ${CloudRunnerFolders.ToLinuxFolder(CloudRunnerFolders.uniqueCloudRunnerJobFolderAbsolute)}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
await RemoteClient.runCustomHookFiles(`after-build`);
|
|
||||||
|
|
||||||
return new Promise((result) => result(``));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ import LocalDockerCloudRunner from './providers/docker';
|
|||||||
import GitHub from '../github';
|
import GitHub from '../github';
|
||||||
import SharedWorkspaceLocking from './services/core/shared-workspace-locking';
|
import SharedWorkspaceLocking from './services/core/shared-workspace-locking';
|
||||||
import { FollowLogStreamService } from './services/core/follow-log-stream-service';
|
import { FollowLogStreamService } from './services/core/follow-log-stream-service';
|
||||||
|
import CloudRunnerResult from './services/core/cloud-runner-result';
|
||||||
|
|
||||||
class CloudRunner {
|
class CloudRunner {
|
||||||
public static Provider: ProviderInterface;
|
public static Provider: ProviderInterface;
|
||||||
@ -83,15 +84,16 @@ class CloudRunner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static async run(buildParameters: BuildParameters, baseImage: string) {
|
static async run(buildParameters: BuildParameters, baseImage: string) {
|
||||||
|
if (baseImage.includes(`undefined`)) {
|
||||||
|
throw new Error(`baseImage is undefined`);
|
||||||
|
}
|
||||||
await CloudRunner.setup(buildParameters);
|
await CloudRunner.setup(buildParameters);
|
||||||
if (!CloudRunner.buildParameters.isCliMode) core.startGroup('Setup shared cloud runner resources');
|
|
||||||
await CloudRunner.Provider.setupWorkflow(
|
await CloudRunner.Provider.setupWorkflow(
|
||||||
CloudRunner.buildParameters.buildGuid,
|
CloudRunner.buildParameters.buildGuid,
|
||||||
CloudRunner.buildParameters,
|
CloudRunner.buildParameters,
|
||||||
CloudRunner.buildParameters.branch,
|
CloudRunner.buildParameters.branch,
|
||||||
CloudRunner.defaultSecrets,
|
CloudRunner.defaultSecrets,
|
||||||
);
|
);
|
||||||
if (!CloudRunner.buildParameters.isCliMode) core.endGroup();
|
|
||||||
try {
|
try {
|
||||||
if (buildParameters.maxRetainedWorkspaces > 0) {
|
if (buildParameters.maxRetainedWorkspaces > 0) {
|
||||||
CloudRunner.lockedWorkspace = SharedWorkspaceLocking.NewWorkspaceName();
|
CloudRunner.lockedWorkspace = SharedWorkspaceLocking.NewWorkspaceName();
|
||||||
@ -114,11 +116,7 @@ class CloudRunner {
|
|||||||
CloudRunner.lockedWorkspace = ``;
|
CloudRunner.lockedWorkspace = ``;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const content = { ...CloudRunner.buildParameters };
|
await CloudRunner.updateStatusWithBuildParameters();
|
||||||
content.gitPrivateToken = ``;
|
|
||||||
content.unitySerial = ``;
|
|
||||||
const jsonContent = JSON.stringify(content, undefined, 4);
|
|
||||||
await GitHub.updateGitHubCheck(jsonContent, CloudRunner.buildParameters.buildGuid);
|
|
||||||
const output = await new WorkflowCompositionRoot().run(
|
const output = await new WorkflowCompositionRoot().run(
|
||||||
new CloudRunnerStepParameters(
|
new CloudRunnerStepParameters(
|
||||||
baseImage,
|
baseImage,
|
||||||
@ -126,16 +124,15 @@ class CloudRunner {
|
|||||||
CloudRunner.defaultSecrets,
|
CloudRunner.defaultSecrets,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
if (!CloudRunner.buildParameters.isCliMode) core.startGroup('Cleanup shared cloud runner resources');
|
|
||||||
await CloudRunner.Provider.cleanupWorkflow(
|
await CloudRunner.Provider.cleanupWorkflow(
|
||||||
CloudRunner.buildParameters.buildGuid,
|
|
||||||
CloudRunner.buildParameters,
|
CloudRunner.buildParameters,
|
||||||
CloudRunner.buildParameters.branch,
|
CloudRunner.buildParameters.branch,
|
||||||
CloudRunner.defaultSecrets,
|
CloudRunner.defaultSecrets,
|
||||||
);
|
);
|
||||||
CloudRunnerLogger.log(`Cleanup complete`);
|
|
||||||
if (!CloudRunner.buildParameters.isCliMode) core.endGroup();
|
if (!CloudRunner.buildParameters.isCliMode) core.endGroup();
|
||||||
|
if (buildParameters.asyncWorkflow && this.isCloudRunnerEnvironment && this.isCloudRunnerAsyncEnvironment) {
|
||||||
await GitHub.updateGitHubCheck(CloudRunner.buildParameters.buildGuid, `success`, `success`, `completed`);
|
await GitHub.updateGitHubCheck(CloudRunner.buildParameters.buildGuid, `success`, `success`, `completed`);
|
||||||
|
}
|
||||||
|
|
||||||
if (BuildParameters.shouldUseRetainedWorkspaceMode(buildParameters)) {
|
if (BuildParameters.shouldUseRetainedWorkspaceMode(buildParameters)) {
|
||||||
const workspace = CloudRunner.lockedWorkspace || ``;
|
const workspace = CloudRunner.lockedWorkspace || ``;
|
||||||
@ -162,7 +159,7 @@ class CloudRunner {
|
|||||||
CloudRunner.Provider.garbageCollect(``, true, buildParameters.garbageMaxAge, true, true);
|
CloudRunner.Provider.garbageCollect(``, true, buildParameters.garbageMaxAge, true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return output;
|
return new CloudRunnerResult(buildParameters, output, true, true, false);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
CloudRunnerLogger.log(JSON.stringify(error, undefined, 4));
|
CloudRunnerLogger.log(JSON.stringify(error, undefined, 4));
|
||||||
await GitHub.updateGitHubCheck(
|
await GitHub.updateGitHubCheck(
|
||||||
@ -176,5 +173,15 @@ class CloudRunner {
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static async updateStatusWithBuildParameters() {
|
||||||
|
const content = { ...CloudRunner.buildParameters };
|
||||||
|
content.gitPrivateToken = ``;
|
||||||
|
content.unitySerial = ``;
|
||||||
|
content.unityEmail = ``;
|
||||||
|
content.unityPassword = ``;
|
||||||
|
const jsonContent = JSON.stringify(content, undefined, 4);
|
||||||
|
await GitHub.updateGitHubCheck(jsonContent, CloudRunner.buildParameters.buildGuid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
export default CloudRunner;
|
export default CloudRunner;
|
||||||
|
@ -9,12 +9,7 @@ export class CloudRunnerError {
|
|||||||
CloudRunnerLogger.error(JSON.stringify(error, undefined, 4));
|
CloudRunnerLogger.error(JSON.stringify(error, undefined, 4));
|
||||||
core.setFailed('Cloud Runner failed');
|
core.setFailed('Cloud Runner failed');
|
||||||
if (CloudRunner.Provider !== undefined) {
|
if (CloudRunner.Provider !== undefined) {
|
||||||
await CloudRunner.Provider.cleanupWorkflow(
|
await CloudRunner.Provider.cleanupWorkflow(buildParameters, buildParameters.branch, secrets);
|
||||||
buildParameters.buildGuid,
|
|
||||||
buildParameters,
|
|
||||||
buildParameters.branch,
|
|
||||||
secrets,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,14 +103,14 @@ class CloudRunnerOptions {
|
|||||||
|
|
||||||
static get buildPlatform(): string {
|
static get buildPlatform(): string {
|
||||||
const input = CloudRunnerOptions.getInput('buildPlatform');
|
const input = CloudRunnerOptions.getInput('buildPlatform');
|
||||||
if (input) {
|
if (input && input !== '') {
|
||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
if (CloudRunnerOptions.providerStrategy !== 'local') {
|
if (CloudRunnerOptions.providerStrategy !== 'local') {
|
||||||
return 'linux';
|
return 'linux';
|
||||||
}
|
}
|
||||||
|
|
||||||
return ``;
|
return process.platform;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get cloudRunnerBranch(): string {
|
static get cloudRunnerBranch(): string {
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
|
import CloudRunner from '../../../cloud-runner';
|
||||||
|
|
||||||
export class TaskDefinitionFormation {
|
export class TaskDefinitionFormation {
|
||||||
public static readonly description: string = `Game CI Cloud Runner Task Stack`;
|
public static readonly description: string = `Game CI Cloud Runner Task Stack`;
|
||||||
public static readonly formation: string = `AWSTemplateFormatVersion: 2010-09-09
|
public static get formation(): string {
|
||||||
|
return `AWSTemplateFormatVersion: 2010-09-09
|
||||||
Description: ${TaskDefinitionFormation.description}
|
Description: ${TaskDefinitionFormation.description}
|
||||||
Parameters:
|
Parameters:
|
||||||
EnvironmentName:
|
EnvironmentName:
|
||||||
@ -26,11 +29,11 @@ Parameters:
|
|||||||
Default: 80
|
Default: 80
|
||||||
Description: What port number the application inside the docker container is binding to
|
Description: What port number the application inside the docker container is binding to
|
||||||
ContainerCpu:
|
ContainerCpu:
|
||||||
Default: 1024
|
Default: ${CloudRunner.buildParameters.containerCpu}
|
||||||
Type: Number
|
Type: Number
|
||||||
Description: How much CPU to give the container. 1024 is 1 CPU
|
Description: How much CPU to give the container. 1024 is 1 CPU
|
||||||
ContainerMemory:
|
ContainerMemory:
|
||||||
Default: 4096
|
Default: ${CloudRunner.buildParameters.containerMemory}
|
||||||
Type: Number
|
Type: Number
|
||||||
Description: How much memory in megabytes to give the container
|
Description: How much memory in megabytes to give the container
|
||||||
BUILDGUID:
|
BUILDGUID:
|
||||||
@ -92,7 +95,7 @@ Resources:
|
|||||||
EFSVolumeConfiguration:
|
EFSVolumeConfiguration:
|
||||||
FilesystemId:
|
FilesystemId:
|
||||||
'Fn::ImportValue': !Sub '${'${EnvironmentName}'}:EfsFileStorageId'
|
'Fn::ImportValue': !Sub '${'${EnvironmentName}'}:EfsFileStorageId'
|
||||||
TransitEncryption: ENABLED
|
TransitEncryption: DISABLED
|
||||||
RequiresCompatibilities:
|
RequiresCompatibilities:
|
||||||
- FARGATE
|
- FARGATE
|
||||||
ExecutionRoleArn:
|
ExecutionRoleArn:
|
||||||
@ -135,6 +138,7 @@ Resources:
|
|||||||
DependsOn:
|
DependsOn:
|
||||||
- LogGroup
|
- LogGroup
|
||||||
`;
|
`;
|
||||||
|
}
|
||||||
public static streamLogs = `
|
public static streamLogs = `
|
||||||
SubscriptionFilter:
|
SubscriptionFilter:
|
||||||
Type: 'AWS::Logs::SubscriptionFilter'
|
Type: 'AWS::Logs::SubscriptionFilter'
|
||||||
|
@ -57,8 +57,6 @@ class AWSBuildEnvironment implements ProviderInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async cleanupWorkflow(
|
async cleanupWorkflow(
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
buildGuid: string,
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
buildParameters: BuildParameters,
|
buildParameters: BuildParameters,
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
@ -41,7 +41,6 @@ class LocalDockerCloudRunner implements ProviderInterface {
|
|||||||
return new Promise((result) => result(``));
|
return new Promise((result) => result(``));
|
||||||
}
|
}
|
||||||
async cleanupWorkflow(
|
async cleanupWorkflow(
|
||||||
buildGuid: string,
|
|
||||||
buildParameters: BuildParameters,
|
buildParameters: BuildParameters,
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
branchName: string,
|
branchName: string,
|
||||||
|
@ -14,12 +14,17 @@ import { CoreV1Api } from '@kubernetes/client-node';
|
|||||||
import CloudRunner from '../../cloud-runner';
|
import CloudRunner from '../../cloud-runner';
|
||||||
import { ProviderResource } from '../provider-resource';
|
import { ProviderResource } from '../provider-resource';
|
||||||
import { ProviderWorkflow } from '../provider-workflow';
|
import { ProviderWorkflow } from '../provider-workflow';
|
||||||
|
import { RemoteClientLogger } from '../../remote-client/remote-client-logger';
|
||||||
|
import { KubernetesRole } from './kubernetes-role';
|
||||||
|
import { CloudRunnerSystem } from '../../services/core/cloud-runner-system';
|
||||||
|
|
||||||
class Kubernetes implements ProviderInterface {
|
class Kubernetes implements ProviderInterface {
|
||||||
public static Instance: Kubernetes;
|
public static Instance: Kubernetes;
|
||||||
public kubeConfig!: k8s.KubeConfig;
|
public kubeConfig!: k8s.KubeConfig;
|
||||||
public kubeClient!: k8s.CoreV1Api;
|
public kubeClient!: k8s.CoreV1Api;
|
||||||
|
public kubeClientApps!: k8s.AppsV1Api;
|
||||||
public kubeClientBatch!: k8s.BatchV1Api;
|
public kubeClientBatch!: k8s.BatchV1Api;
|
||||||
|
public rbacAuthorizationV1Api!: k8s.RbacAuthorizationV1Api;
|
||||||
public buildGuid: string = '';
|
public buildGuid: string = '';
|
||||||
public buildParameters!: BuildParameters;
|
public buildParameters!: BuildParameters;
|
||||||
public pvcName: string = '';
|
public pvcName: string = '';
|
||||||
@ -30,6 +35,7 @@ class Kubernetes implements ProviderInterface {
|
|||||||
public containerName: string = '';
|
public containerName: string = '';
|
||||||
public cleanupCronJobName: string = '';
|
public cleanupCronJobName: string = '';
|
||||||
public serviceAccountName: string = '';
|
public serviceAccountName: string = '';
|
||||||
|
public ip: string = '';
|
||||||
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
constructor(buildParameters: BuildParameters) {
|
constructor(buildParameters: BuildParameters) {
|
||||||
@ -37,11 +43,30 @@ class Kubernetes implements ProviderInterface {
|
|||||||
this.kubeConfig = new k8s.KubeConfig();
|
this.kubeConfig = new k8s.KubeConfig();
|
||||||
this.kubeConfig.loadFromDefault();
|
this.kubeConfig.loadFromDefault();
|
||||||
this.kubeClient = this.kubeConfig.makeApiClient(k8s.CoreV1Api);
|
this.kubeClient = this.kubeConfig.makeApiClient(k8s.CoreV1Api);
|
||||||
|
this.kubeClientApps = this.kubeConfig.makeApiClient(k8s.AppsV1Api);
|
||||||
this.kubeClientBatch = this.kubeConfig.makeApiClient(k8s.BatchV1Api);
|
this.kubeClientBatch = this.kubeConfig.makeApiClient(k8s.BatchV1Api);
|
||||||
|
this.rbacAuthorizationV1Api = this.kubeConfig.makeApiClient(k8s.RbacAuthorizationV1Api);
|
||||||
this.namespace = 'default';
|
this.namespace = 'default';
|
||||||
CloudRunnerLogger.log('Loaded default Kubernetes configuration for this environment');
|
CloudRunnerLogger.log('Loaded default Kubernetes configuration for this environment');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async PushLogUpdate(logs: string) {
|
||||||
|
// push logs to nginx file server via 'LOG_SERVICE_IP' env var
|
||||||
|
const ip = process.env[`LOG_SERVICE_IP`];
|
||||||
|
if (ip === undefined) {
|
||||||
|
RemoteClientLogger.logWarning(`LOG_SERVICE_IP not set, skipping log push`);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const url = `http://${ip}/api/log`;
|
||||||
|
RemoteClientLogger.log(`Pushing logs to ${url}`);
|
||||||
|
|
||||||
|
// logs to base64
|
||||||
|
logs = Buffer.from(logs).toString('base64');
|
||||||
|
const response = await CloudRunnerSystem.Run(`curl -X POST -d "${logs}" ${url}`, false, true);
|
||||||
|
RemoteClientLogger.log(`Pushed logs to ${url} ${response}`);
|
||||||
|
}
|
||||||
|
|
||||||
async listResources(): Promise<ProviderResource[]> {
|
async listResources(): Promise<ProviderResource[]> {
|
||||||
const pods = await this.kubeClient.listNamespacedPod(this.namespace);
|
const pods = await this.kubeClient.listNamespacedPod(this.namespace);
|
||||||
const serviceAccounts = await this.kubeClient.listNamespacedServiceAccount(this.namespace);
|
const serviceAccounts = await this.kubeClient.listNamespacedServiceAccount(this.namespace);
|
||||||
@ -115,7 +140,8 @@ class Kubernetes implements ProviderInterface {
|
|||||||
CloudRunnerLogger.log('Cloud Runner K8s workflow!');
|
CloudRunnerLogger.log('Cloud Runner K8s workflow!');
|
||||||
|
|
||||||
// Setup
|
// Setup
|
||||||
const id = BuildParameters.shouldUseRetainedWorkspaceMode(this.buildParameters)
|
const id =
|
||||||
|
BuildParameters && BuildParameters.shouldUseRetainedWorkspaceMode(this.buildParameters)
|
||||||
? CloudRunner.lockedWorkspace
|
? CloudRunner.lockedWorkspace
|
||||||
: this.buildParameters.buildGuid;
|
: this.buildParameters.buildGuid;
|
||||||
this.pvcName = `unity-builder-pvc-${id}`;
|
this.pvcName = `unity-builder-pvc-${id}`;
|
||||||
@ -137,10 +163,7 @@ class Kubernetes implements ProviderInterface {
|
|||||||
CloudRunnerLogger.log('Watching pod until running');
|
CloudRunnerLogger.log('Watching pod until running');
|
||||||
await KubernetesTaskRunner.watchUntilPodRunning(this.kubeClient, this.podName, this.namespace);
|
await KubernetesTaskRunner.watchUntilPodRunning(this.kubeClient, this.podName, this.namespace);
|
||||||
|
|
||||||
CloudRunnerLogger.log('Pod running, streaming logs');
|
CloudRunnerLogger.log('Pod is running');
|
||||||
CloudRunnerLogger.log(
|
|
||||||
`Starting logs follow for pod: ${this.podName} container: ${this.containerName} namespace: ${this.namespace} pvc: ${this.pvcName} ${CloudRunner.buildParameters.kubeVolumeSize}/${CloudRunner.buildParameters.containerCpu}/${CloudRunner.buildParameters.containerMemory}`,
|
|
||||||
);
|
|
||||||
output += await KubernetesTaskRunner.runTask(
|
output += await KubernetesTaskRunner.runTask(
|
||||||
this.kubeConfig,
|
this.kubeConfig,
|
||||||
this.kubeClient,
|
this.kubeClient,
|
||||||
@ -232,8 +255,12 @@ class Kubernetes implements ProviderInterface {
|
|||||||
this.jobName,
|
this.jobName,
|
||||||
k8s,
|
k8s,
|
||||||
this.containerName,
|
this.containerName,
|
||||||
|
this.ip,
|
||||||
);
|
);
|
||||||
await new Promise((promise) => setTimeout(promise, 15000));
|
await new Promise((promise) => setTimeout(promise, 15000));
|
||||||
|
|
||||||
|
// await KubernetesRole.createRole(this.serviceAccountName, this.namespace, this.rbacAuthorizationV1Api);
|
||||||
|
|
||||||
const result = await this.kubeClientBatch.createNamespacedJob(this.namespace, jobSpec);
|
const result = await this.kubeClientBatch.createNamespacedJob(this.namespace, jobSpec);
|
||||||
CloudRunnerLogger.log(`Build job created`);
|
CloudRunnerLogger.log(`Build job created`);
|
||||||
await new Promise((promise) => setTimeout(promise, 5000));
|
await new Promise((promise) => setTimeout(promise, 5000));
|
||||||
@ -257,6 +284,7 @@ class Kubernetes implements ProviderInterface {
|
|||||||
try {
|
try {
|
||||||
await this.kubeClientBatch.deleteNamespacedJob(this.jobName, this.namespace);
|
await this.kubeClientBatch.deleteNamespacedJob(this.jobName, this.namespace);
|
||||||
await this.kubeClient.deleteNamespacedPod(this.podName, this.namespace);
|
await this.kubeClient.deleteNamespacedPod(this.podName, this.namespace);
|
||||||
|
await KubernetesRole.deleteRole(this.serviceAccountName, this.namespace, this.rbacAuthorizationV1Api);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
CloudRunnerLogger.log(`Failed to cleanup`);
|
CloudRunnerLogger.log(`Failed to cleanup`);
|
||||||
if (error.response.body.reason !== `NotFound`) {
|
if (error.response.body.reason !== `NotFound`) {
|
||||||
@ -275,14 +303,13 @@ class Kubernetes implements ProviderInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async cleanupWorkflow(
|
async cleanupWorkflow(
|
||||||
buildGuid: string,
|
|
||||||
buildParameters: BuildParameters,
|
buildParameters: BuildParameters,
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
branchName: string,
|
branchName: string,
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
defaultSecretsArray: { ParameterKey: string; EnvironmentVariable: string; ParameterValue: string }[],
|
defaultSecretsArray: { ParameterKey: string; EnvironmentVariable: string; ParameterValue: string }[],
|
||||||
) {
|
) {
|
||||||
if (BuildParameters.shouldUseRetainedWorkspaceMode(buildParameters)) {
|
if (BuildParameters && BuildParameters.shouldUseRetainedWorkspaceMode(buildParameters)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
CloudRunnerLogger.log(`deleting PVC`);
|
CloudRunnerLogger.log(`deleting PVC`);
|
||||||
|
@ -20,6 +20,7 @@ class KubernetesJobSpecFactory {
|
|||||||
jobName: string,
|
jobName: string,
|
||||||
k8s: any,
|
k8s: any,
|
||||||
containerName: string,
|
containerName: string,
|
||||||
|
ip: string = '',
|
||||||
) {
|
) {
|
||||||
const job = new k8s.V1Job();
|
const job = new k8s.V1Job();
|
||||||
job.apiVersion = 'batch/v1';
|
job.apiVersion = 'batch/v1';
|
||||||
@ -81,6 +82,7 @@ class KubernetesJobSpecFactory {
|
|||||||
|
|
||||||
return environmentVariable;
|
return environmentVariable;
|
||||||
}),
|
}),
|
||||||
|
{ name: 'LOG_SERVICE_IP', value: ip },
|
||||||
],
|
],
|
||||||
volumeMounts: [
|
volumeMounts: [
|
||||||
{
|
{
|
||||||
@ -92,9 +94,8 @@ class KubernetesJobSpecFactory {
|
|||||||
preStop: {
|
preStop: {
|
||||||
exec: {
|
exec: {
|
||||||
command: [
|
command: [
|
||||||
'bin/bash',
|
`wait 60s;
|
||||||
'-c',
|
cd /data/builder/action/steps;
|
||||||
`cd /data/builder/action/steps;
|
|
||||||
chmod +x /return_license.sh;
|
chmod +x /return_license.sh;
|
||||||
/return_license.sh;`,
|
/return_license.sh;`,
|
||||||
],
|
],
|
||||||
@ -108,6 +109,16 @@ class KubernetesJobSpecFactory {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (process.env['CLOUD_RUNNER_MINIKUBE']) {
|
||||||
|
job.spec.template.spec.volumes[0] = {
|
||||||
|
name: 'build-mount',
|
||||||
|
hostPath: {
|
||||||
|
path: `/data`,
|
||||||
|
type: `Directory`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
job.spec.template.spec.containers[0].resources.requests[`ephemeral-storage`] = '10Gi';
|
job.spec.template.spec.containers[0].resources.requests[`ephemeral-storage`] = '10Gi';
|
||||||
|
|
||||||
return job;
|
return job;
|
||||||
|
53
src/model/cloud-runner/providers/k8s/kubernetes-role.ts
Normal file
53
src/model/cloud-runner/providers/k8s/kubernetes-role.ts
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import { RbacAuthorizationV1Api } from '@kubernetes/client-node';
|
||||||
|
|
||||||
|
class KubernetesRole {
|
||||||
|
static async createRole(serviceAccountName: string, namespace: string, rbac: RbacAuthorizationV1Api) {
|
||||||
|
// create admin kubernetes role and role binding
|
||||||
|
const roleBinding = {
|
||||||
|
apiVersion: 'rbac.authorization.k8s.io/v1',
|
||||||
|
kind: 'RoleBinding',
|
||||||
|
metadata: {
|
||||||
|
name: `${serviceAccountName}-admin`,
|
||||||
|
namespace,
|
||||||
|
},
|
||||||
|
subjects: [
|
||||||
|
{
|
||||||
|
kind: 'ServiceAccount',
|
||||||
|
name: serviceAccountName,
|
||||||
|
namespace,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
roleRef: {
|
||||||
|
apiGroup: 'rbac.authorization.k8s.io',
|
||||||
|
kind: 'Role',
|
||||||
|
name: `${serviceAccountName}-admin`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const role = {
|
||||||
|
apiVersion: 'rbac.authorization.k8s.io/v1',
|
||||||
|
kind: 'Role',
|
||||||
|
metadata: {
|
||||||
|
name: `${serviceAccountName}-admin`,
|
||||||
|
namespace,
|
||||||
|
},
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
apiGroups: ['*'],
|
||||||
|
resources: ['*'],
|
||||||
|
verbs: ['*'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
const roleBindingResponse = await rbac.createNamespacedRoleBinding(namespace, roleBinding);
|
||||||
|
const roleResponse = await rbac.createNamespacedRole(namespace, role);
|
||||||
|
|
||||||
|
return { roleBindingResponse, roleResponse };
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async deleteRole(serviceAccountName: string, namespace: string, rbac: RbacAuthorizationV1Api) {
|
||||||
|
await rbac.deleteNamespacedRoleBinding(`${serviceAccountName}-admin`, namespace);
|
||||||
|
await rbac.deleteNamespacedRole(`${serviceAccountName}-admin`, namespace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export { KubernetesRole };
|
@ -9,7 +9,7 @@ class KubernetesServiceAccount {
|
|||||||
serviceAccount.metadata = {
|
serviceAccount.metadata = {
|
||||||
name: serviceAccountName,
|
name: serviceAccountName,
|
||||||
};
|
};
|
||||||
serviceAccount.automountServiceAccountToken = false;
|
serviceAccount.automountServiceAccountToken = true;
|
||||||
|
|
||||||
return kubeClient.createNamespacedServiceAccount(namespace, serviceAccount);
|
return kubeClient.createNamespacedServiceAccount(namespace, serviceAccount);
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ import KubernetesPods from './kubernetes-pods';
|
|||||||
import { FollowLogStreamService } from '../../services/core/follow-log-stream-service';
|
import { FollowLogStreamService } from '../../services/core/follow-log-stream-service';
|
||||||
|
|
||||||
class KubernetesTaskRunner {
|
class KubernetesTaskRunner {
|
||||||
static lastReceivedTimestamp: number = 0;
|
|
||||||
static readonly maxRetry: number = 3;
|
static readonly maxRetry: number = 3;
|
||||||
static lastReceivedMessage: string = ``;
|
static lastReceivedMessage: string = ``;
|
||||||
|
|
||||||
@ -22,38 +21,33 @@ class KubernetesTaskRunner {
|
|||||||
let output = '';
|
let output = '';
|
||||||
let shouldReadLogs = true;
|
let shouldReadLogs = true;
|
||||||
let shouldCleanup = true;
|
let shouldCleanup = true;
|
||||||
let sinceTime = ``;
|
|
||||||
let retriesAfterFinish = 0;
|
let retriesAfterFinish = 0;
|
||||||
// eslint-disable-next-line no-constant-condition
|
// eslint-disable-next-line no-constant-condition
|
||||||
while (true) {
|
while (true) {
|
||||||
await new Promise((resolve) => setTimeout(resolve, 3000));
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
||||||
const lastReceivedMessage =
|
|
||||||
KubernetesTaskRunner.lastReceivedTimestamp > 0
|
|
||||||
? `\nLast Log Message "${this.lastReceivedMessage}" ${this.lastReceivedTimestamp}`
|
|
||||||
: ``;
|
|
||||||
CloudRunnerLogger.log(
|
CloudRunnerLogger.log(
|
||||||
`Streaming logs from pod: ${podName} container: ${containerName} namespace: ${namespace} ${CloudRunner.buildParameters.kubeVolumeSize}/${CloudRunner.buildParameters.containerCpu}/${CloudRunner.buildParameters.containerMemory}\n${lastReceivedMessage}`,
|
`Streaming logs from pod: ${podName} container: ${containerName} namespace: ${namespace} ${CloudRunner.buildParameters.kubeVolumeSize}/${CloudRunner.buildParameters.containerCpu}/${CloudRunner.buildParameters.containerMemory}`,
|
||||||
);
|
);
|
||||||
if (KubernetesTaskRunner.lastReceivedTimestamp > 0) {
|
|
||||||
const currentDate = new Date(KubernetesTaskRunner.lastReceivedTimestamp);
|
|
||||||
const dateTimeIsoString = currentDate.toISOString();
|
|
||||||
sinceTime = ` --since-time="${dateTimeIsoString}"`;
|
|
||||||
}
|
|
||||||
let extraFlags = ``;
|
let extraFlags = ``;
|
||||||
extraFlags += (await KubernetesPods.IsPodRunning(podName, namespace, kubeClient))
|
extraFlags += (await KubernetesPods.IsPodRunning(podName, namespace, kubeClient))
|
||||||
? ` -f -c ${containerName}`
|
? ` -f -c ${containerName}`
|
||||||
: ` --previous`;
|
: ` --previous`;
|
||||||
let lastMessageSeenIncludedInChunk = false;
|
|
||||||
let lastMessageSeen = false;
|
|
||||||
|
|
||||||
let logs;
|
const callback = (outputChunk: string) => {
|
||||||
|
output += outputChunk;
|
||||||
|
|
||||||
|
// split output chunk and handle per line
|
||||||
|
for (const chunk of outputChunk.split(`\n`)) {
|
||||||
|
({ shouldReadLogs, shouldCleanup, output } = FollowLogStreamService.handleIteration(
|
||||||
|
chunk,
|
||||||
|
shouldReadLogs,
|
||||||
|
shouldCleanup,
|
||||||
|
output,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
try {
|
try {
|
||||||
logs = await CloudRunnerSystem.Run(
|
await CloudRunnerSystem.Run(`kubectl logs ${podName}${extraFlags}`, false, true, callback);
|
||||||
`kubectl logs ${podName}${extraFlags} --timestamps${sinceTime}`,
|
|
||||||
false,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
await new Promise((resolve) => setTimeout(resolve, 3000));
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
||||||
const continueStreaming = await KubernetesPods.IsPodRunning(podName, namespace, kubeClient);
|
const continueStreaming = await KubernetesPods.IsPodRunning(podName, namespace, kubeClient);
|
||||||
@ -68,34 +62,6 @@ class KubernetesTaskRunner {
|
|||||||
}
|
}
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
const splitLogs = logs.split(`\n`);
|
|
||||||
for (const chunk of splitLogs) {
|
|
||||||
if (
|
|
||||||
chunk.replace(/\s/g, ``) === KubernetesTaskRunner.lastReceivedMessage.replace(/\s/g, ``) &&
|
|
||||||
KubernetesTaskRunner.lastReceivedMessage.replace(/\s/g, ``) !== ``
|
|
||||||
) {
|
|
||||||
CloudRunnerLogger.log(`Previous log message found ${chunk}`);
|
|
||||||
lastMessageSeenIncludedInChunk = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const chunk of splitLogs) {
|
|
||||||
const newDate = Date.parse(`${chunk.toString().split(`Z `)[0]}Z`);
|
|
||||||
if (chunk.replace(/\s/g, ``) === KubernetesTaskRunner.lastReceivedMessage.replace(/\s/g, ``)) {
|
|
||||||
lastMessageSeen = true;
|
|
||||||
}
|
|
||||||
if (lastMessageSeenIncludedInChunk && !lastMessageSeen) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const message = CloudRunner.buildParameters.cloudRunnerDebug ? chunk : chunk.split(`Z `)[1];
|
|
||||||
KubernetesTaskRunner.lastReceivedMessage = chunk;
|
|
||||||
KubernetesTaskRunner.lastReceivedTimestamp = newDate;
|
|
||||||
({ shouldReadLogs, shouldCleanup, output } = FollowLogStreamService.handleIteration(
|
|
||||||
message,
|
|
||||||
shouldReadLogs,
|
|
||||||
shouldCleanup,
|
|
||||||
output,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
if (FollowLogStreamService.DidReceiveEndOfTransmission) {
|
if (FollowLogStreamService.DidReceiveEndOfTransmission) {
|
||||||
CloudRunnerLogger.log('end of log stream');
|
CloudRunnerLogger.log('end of log stream');
|
||||||
break;
|
break;
|
||||||
@ -106,14 +72,14 @@ class KubernetesTaskRunner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static async watchUntilPodRunning(kubeClient: CoreV1Api, podName: string, namespace: string) {
|
static async watchUntilPodRunning(kubeClient: CoreV1Api, podName: string, namespace: string) {
|
||||||
let success: boolean = false;
|
let waitComplete: boolean = false;
|
||||||
let message = ``;
|
let message = ``;
|
||||||
CloudRunnerLogger.log(`Watching ${podName} ${namespace}`);
|
CloudRunnerLogger.log(`Watching ${podName} ${namespace}`);
|
||||||
await waitUntil(
|
await waitUntil(
|
||||||
async () => {
|
async () => {
|
||||||
const status = await kubeClient.readNamespacedPodStatus(podName, namespace);
|
const status = await kubeClient.readNamespacedPodStatus(podName, namespace);
|
||||||
const phase = status?.body.status?.phase;
|
const phase = status?.body.status?.phase;
|
||||||
success = phase === 'Running';
|
waitComplete = phase !== 'Pending';
|
||||||
message = `Phase:${status.body.status?.phase} \n Reason:${
|
message = `Phase:${status.body.status?.phase} \n Reason:${
|
||||||
status.body.status?.conditions?.[0].reason || ''
|
status.body.status?.conditions?.[0].reason || ''
|
||||||
} \n Message:${status.body.status?.conditions?.[0].message || ''}`;
|
} \n Message:${status.body.status?.conditions?.[0].message || ''}`;
|
||||||
@ -133,7 +99,7 @@ class KubernetesTaskRunner {
|
|||||||
// 4,
|
// 4,
|
||||||
// ),
|
// ),
|
||||||
// );
|
// );
|
||||||
if (success || phase !== 'Pending') return true;
|
if (waitComplete || phase !== 'Pending') return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
@ -142,11 +108,11 @@ class KubernetesTaskRunner {
|
|||||||
intervalBetweenAttempts: 15000,
|
intervalBetweenAttempts: 15000,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
if (!success) {
|
if (!waitComplete) {
|
||||||
CloudRunnerLogger.log(message);
|
CloudRunnerLogger.log(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
return success;
|
return waitComplete;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,8 +32,6 @@ class LocalCloudRunner implements ProviderInterface {
|
|||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
cleanupWorkflow(
|
cleanupWorkflow(
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
buildGuid: string,
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
buildParameters: BuildParameters,
|
buildParameters: BuildParameters,
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
@ -6,8 +6,6 @@ import { ProviderWorkflow } from './provider-workflow';
|
|||||||
|
|
||||||
export interface ProviderInterface {
|
export interface ProviderInterface {
|
||||||
cleanupWorkflow(
|
cleanupWorkflow(
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
buildGuid: string,
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
buildParameters: BuildParameters,
|
buildParameters: BuildParameters,
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
@ -25,8 +25,6 @@ class TestCloudRunner implements ProviderInterface {
|
|||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
cleanupWorkflow(
|
cleanupWorkflow(
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
buildGuid: string,
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
buildParameters: BuildParameters,
|
buildParameters: BuildParameters,
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
@ -80,7 +80,7 @@ export class Caching {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await CloudRunnerSystem.Run(
|
await CloudRunnerSystem.Run(
|
||||||
`tar -cf ${cacheArtifactName}.tar${compressionSuffix} ${path.basename(sourceFolder)}`,
|
`tar -cf ${cacheArtifactName}.tar${compressionSuffix} "${path.basename(sourceFolder)}"`,
|
||||||
);
|
);
|
||||||
await CloudRunnerSystem.Run(`du ${cacheArtifactName}.tar${compressionSuffix}`);
|
await CloudRunnerSystem.Run(`du ${cacheArtifactName}.tar${compressionSuffix}`);
|
||||||
assert(await fileExists(`${cacheArtifactName}.tar${compressionSuffix}`), 'cache archive exists');
|
assert(await fileExists(`${cacheArtifactName}.tar${compressionSuffix}`), 'cache archive exists');
|
||||||
|
@ -12,16 +12,83 @@ import { CloudRunnerSystem } from '../services/core/cloud-runner-system';
|
|||||||
import YAML from 'yaml';
|
import YAML from 'yaml';
|
||||||
import GitHub from '../../github';
|
import GitHub from '../../github';
|
||||||
import BuildParameters from '../../build-parameters';
|
import BuildParameters from '../../build-parameters';
|
||||||
|
import { Cli } from '../../cli/cli';
|
||||||
|
import CloudRunnerOptions from '../options/cloud-runner-options';
|
||||||
|
|
||||||
export class RemoteClient {
|
export class RemoteClient {
|
||||||
@CliFunction(`remote-cli-pre-build`, `sets up a repository, usually before a game-ci build`)
|
@CliFunction(`remote-cli-pre-build`, `sets up a repository, usually before a game-ci build`)
|
||||||
static async runRemoteClientJob() {
|
static async setupRemoteClient() {
|
||||||
CloudRunnerLogger.log(`bootstrap game ci cloud runner...`);
|
CloudRunnerLogger.log(`bootstrap game ci cloud runner...`);
|
||||||
if (!(await RemoteClient.handleRetainedWorkspace())) {
|
if (!(await RemoteClient.handleRetainedWorkspace())) {
|
||||||
await RemoteClient.bootstrapRepository();
|
await RemoteClient.bootstrapRepository();
|
||||||
}
|
}
|
||||||
|
await RemoteClient.replaceLargePackageReferencesWithSharedReferences();
|
||||||
await RemoteClient.runCustomHookFiles(`before-build`);
|
await RemoteClient.runCustomHookFiles(`before-build`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@CliFunction('remote-cli-log-stream', `log stream from standard input`)
|
||||||
|
public static async remoteClientLogStream() {
|
||||||
|
const logFile = Cli.options!['logFile'];
|
||||||
|
process.stdin.resume();
|
||||||
|
process.stdin.setEncoding('utf8');
|
||||||
|
|
||||||
|
let lingeringLine = '';
|
||||||
|
|
||||||
|
process.stdin.on('data', (chunk) => {
|
||||||
|
const lines = chunk.toString().split('\n');
|
||||||
|
|
||||||
|
lines[0] = lingeringLine + lines[0];
|
||||||
|
lingeringLine = lines.pop() || '';
|
||||||
|
|
||||||
|
for (const element of lines) {
|
||||||
|
if (CloudRunnerOptions.providerStrategy !== 'k8s') {
|
||||||
|
CloudRunnerLogger.log(element);
|
||||||
|
} else {
|
||||||
|
fs.appendFileSync(logFile, element);
|
||||||
|
CloudRunnerLogger.log(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
process.stdin.on('end', () => {
|
||||||
|
if (CloudRunnerOptions.providerStrategy !== 'k8s') {
|
||||||
|
CloudRunnerLogger.log(lingeringLine);
|
||||||
|
} else {
|
||||||
|
fs.appendFileSync(logFile, lingeringLine);
|
||||||
|
CloudRunnerLogger.log(lingeringLine);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@CliFunction(`remote-cli-post-build`, `runs a cloud runner build`)
|
||||||
|
public static async remoteClientPostBuild(): Promise<string> {
|
||||||
|
RemoteClientLogger.log(`Running POST build tasks`);
|
||||||
|
|
||||||
|
await Caching.PushToCache(
|
||||||
|
CloudRunnerFolders.ToLinuxFolder(`${CloudRunnerFolders.cacheFolderForCacheKeyFull}/Library`),
|
||||||
|
CloudRunnerFolders.ToLinuxFolder(CloudRunnerFolders.libraryFolderAbsolute),
|
||||||
|
`lib-${CloudRunner.buildParameters.buildGuid}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
await Caching.PushToCache(
|
||||||
|
CloudRunnerFolders.ToLinuxFolder(`${CloudRunnerFolders.cacheFolderForCacheKeyFull}/build`),
|
||||||
|
CloudRunnerFolders.ToLinuxFolder(CloudRunnerFolders.projectBuildFolderAbsolute),
|
||||||
|
`build-${CloudRunner.buildParameters.buildGuid}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!BuildParameters.shouldUseRetainedWorkspaceMode(CloudRunner.buildParameters)) {
|
||||||
|
await CloudRunnerSystem.Run(
|
||||||
|
`rm -r ${CloudRunnerFolders.ToLinuxFolder(CloudRunnerFolders.uniqueCloudRunnerJobFolderAbsolute)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await RemoteClient.runCustomHookFiles(`after-build`);
|
||||||
|
|
||||||
|
// WIP - need to give the pod permissions to create config map
|
||||||
|
await RemoteClientLogger.handleLogManagementPostJob();
|
||||||
|
|
||||||
|
return new Promise((result) => result(``));
|
||||||
|
}
|
||||||
static async runCustomHookFiles(hookLifecycle: string) {
|
static async runCustomHookFiles(hookLifecycle: string) {
|
||||||
RemoteClientLogger.log(`RunCustomHookFiles: ${hookLifecycle}`);
|
RemoteClientLogger.log(`RunCustomHookFiles: ${hookLifecycle}`);
|
||||||
const gameCiCustomHooksPath = path.join(CloudRunnerFolders.repoPathAbsolute, `game-ci`, `hooks`);
|
const gameCiCustomHooksPath = path.join(CloudRunnerFolders.repoPathAbsolute, `game-ci`, `hooks`);
|
||||||
@ -47,7 +114,6 @@ export class RemoteClient {
|
|||||||
`mkdir -p ${CloudRunnerFolders.ToLinuxFolder(CloudRunnerFolders.cacheFolderForCacheKeyFull)}`,
|
`mkdir -p ${CloudRunnerFolders.ToLinuxFolder(CloudRunnerFolders.cacheFolderForCacheKeyFull)}`,
|
||||||
);
|
);
|
||||||
await RemoteClient.cloneRepoWithoutLFSFiles();
|
await RemoteClient.cloneRepoWithoutLFSFiles();
|
||||||
await RemoteClient.replaceLargePackageReferencesWithSharedReferences();
|
|
||||||
await RemoteClient.sizeOfFolder(
|
await RemoteClient.sizeOfFolder(
|
||||||
'repo before lfs cache pull',
|
'repo before lfs cache pull',
|
||||||
CloudRunnerFolders.ToLinuxFolder(CloudRunnerFolders.repoPathAbsolute),
|
CloudRunnerFolders.ToLinuxFolder(CloudRunnerFolders.repoPathAbsolute),
|
||||||
|
@ -1,8 +1,18 @@
|
|||||||
import CloudRunnerLogger from '../services/core/cloud-runner-logger';
|
import CloudRunnerLogger from '../services/core/cloud-runner-logger';
|
||||||
|
import fs from 'node:fs';
|
||||||
|
import path from 'node:path';
|
||||||
|
import CloudRunner from '../cloud-runner';
|
||||||
|
import CloudRunnerOptions from '../options/cloud-runner-options';
|
||||||
|
|
||||||
export class RemoteClientLogger {
|
export class RemoteClientLogger {
|
||||||
|
private static get LogFilePath() {
|
||||||
|
return path.join(`/home`, `job-log.txt`);
|
||||||
|
}
|
||||||
|
|
||||||
public static log(message: string) {
|
public static log(message: string) {
|
||||||
CloudRunnerLogger.log(`[Client] ${message}`);
|
const finalMessage = `[Client] ${message}`;
|
||||||
|
this.appendToFile(finalMessage);
|
||||||
|
CloudRunnerLogger.log(finalMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static logCliError(message: string) {
|
public static logCliError(message: string) {
|
||||||
@ -16,4 +26,57 @@ export class RemoteClientLogger {
|
|||||||
public static logWarning(message: string) {
|
public static logWarning(message: string) {
|
||||||
CloudRunnerLogger.logWarning(message);
|
CloudRunnerLogger.logWarning(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static appendToFile(message: string) {
|
||||||
|
if (CloudRunner.isCloudRunnerEnvironment) {
|
||||||
|
fs.appendFileSync(RemoteClientLogger.LogFilePath, `${message}\n`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async handleLogManagementPostJob() {
|
||||||
|
if (CloudRunnerOptions.providerStrategy !== 'k8s') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CloudRunnerLogger.log(`Collected Logs`);
|
||||||
|
|
||||||
|
// check for log file not existing
|
||||||
|
if (!fs.existsSync(RemoteClientLogger.LogFilePath)) {
|
||||||
|
CloudRunnerLogger.log(`Log file does not exist`);
|
||||||
|
|
||||||
|
// check if CloudRunner.isCloudRunnerEnvironment is true, log
|
||||||
|
if (!CloudRunner.isCloudRunnerEnvironment) {
|
||||||
|
CloudRunnerLogger.log(`Cloud Runner is not running in a cloud environment, not collecting logs`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CloudRunnerLogger.log(`Log file exist`);
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 1));
|
||||||
|
|
||||||
|
// let hashedLogs = fs.readFileSync(RemoteClientLogger.LogFilePath).toString();
|
||||||
|
//
|
||||||
|
// hashedLogs = md5(hashedLogs);
|
||||||
|
//
|
||||||
|
// for (let index = 0; index < 3; index++) {
|
||||||
|
// CloudRunnerLogger.log(`LOGHASH: ${hashedLogs}`);
|
||||||
|
// const logs = fs.readFileSync(RemoteClientLogger.LogFilePath).toString();
|
||||||
|
// CloudRunnerLogger.log(`LOGS: ${Buffer.from(logs).toString('base64')}`);
|
||||||
|
// CloudRunnerLogger.log(
|
||||||
|
// `Game CI's "Cloud Runner System" will cancel the log when it has successfully received the log data to verify all logs have been received.`,
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// // wait for 15 seconds to allow the log to be sent
|
||||||
|
// await new Promise((resolve) => setTimeout(resolve, 15000));
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
public static HandleLog(message: string): boolean {
|
||||||
|
if (RemoteClientLogger.value !== '') {
|
||||||
|
RemoteClientLogger.value += `\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
RemoteClientLogger.value += message;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
static value: string = '';
|
||||||
}
|
}
|
||||||
|
24
src/model/cloud-runner/services/core/cloud-runner-result.ts
Normal file
24
src/model/cloud-runner/services/core/cloud-runner-result.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import BuildParameters from '../../../build-parameters';
|
||||||
|
|
||||||
|
class CloudRunnerResult {
|
||||||
|
public BuildParameters: BuildParameters;
|
||||||
|
public BuildResults: string;
|
||||||
|
public BuildSucceeded: boolean;
|
||||||
|
public BuildFinished: boolean;
|
||||||
|
public LibraryCacheUsed: boolean;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
buildParameters: BuildParameters,
|
||||||
|
buildResults: string,
|
||||||
|
buildSucceeded: boolean,
|
||||||
|
buildFinished: boolean,
|
||||||
|
libraryCacheUsed: boolean,
|
||||||
|
) {
|
||||||
|
this.BuildParameters = buildParameters;
|
||||||
|
this.BuildResults = buildResults;
|
||||||
|
this.BuildSucceeded = buildSucceeded;
|
||||||
|
this.BuildFinished = buildFinished;
|
||||||
|
this.LibraryCacheUsed = libraryCacheUsed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default CloudRunnerResult;
|
@ -16,7 +16,13 @@ export class CloudRunnerSystem {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Run(command: string, suppressError = false, suppressLogs = false) {
|
public static async Run(
|
||||||
|
command: string,
|
||||||
|
suppressError = false,
|
||||||
|
suppressLogs = false,
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
outputCallback?: (output: string) => void,
|
||||||
|
) {
|
||||||
for (const element of command.split(`\n`)) {
|
for (const element of command.split(`\n`)) {
|
||||||
if (!suppressLogs) {
|
if (!suppressLogs) {
|
||||||
RemoteClientLogger.log(element);
|
RemoteClientLogger.log(element);
|
||||||
@ -25,7 +31,7 @@ export class CloudRunnerSystem {
|
|||||||
|
|
||||||
return await new Promise<string>((promise, throwError) => {
|
return await new Promise<string>((promise, throwError) => {
|
||||||
let output = '';
|
let output = '';
|
||||||
const child = exec(command, (error, stdout, stderr) => {
|
const child = exec(command, { maxBuffer: 1024 * 10000 }, (error, stdout, stderr) => {
|
||||||
if (!suppressError && error) {
|
if (!suppressError && error) {
|
||||||
RemoteClientLogger.log(error.toString());
|
RemoteClientLogger.log(error.toString());
|
||||||
throwError(error);
|
throwError(error);
|
||||||
@ -38,6 +44,9 @@ export class CloudRunnerSystem {
|
|||||||
output += diagnosticOutput;
|
output += diagnosticOutput;
|
||||||
}
|
}
|
||||||
const outputChunk = `${stdout}`;
|
const outputChunk = `${stdout}`;
|
||||||
|
if (outputCallback) {
|
||||||
|
outputCallback(outputChunk);
|
||||||
|
}
|
||||||
output += outputChunk;
|
output += outputChunk;
|
||||||
});
|
});
|
||||||
child.on('close', (code) => {
|
child.on('close', (code) => {
|
||||||
|
@ -146,7 +146,8 @@ export class TaskParameterSerializer {
|
|||||||
array = TaskParameterSerializer.tryAddInput(array, 'UNITY_SERIAL');
|
array = TaskParameterSerializer.tryAddInput(array, 'UNITY_SERIAL');
|
||||||
array = TaskParameterSerializer.tryAddInput(array, 'UNITY_EMAIL');
|
array = TaskParameterSerializer.tryAddInput(array, 'UNITY_EMAIL');
|
||||||
array = TaskParameterSerializer.tryAddInput(array, 'UNITY_PASSWORD');
|
array = TaskParameterSerializer.tryAddInput(array, 'UNITY_PASSWORD');
|
||||||
array = TaskParameterSerializer.tryAddInput(array, 'UNITY_LICENSE');
|
|
||||||
|
// array = TaskParameterSerializer.tryAddInput(array, 'UNITY_LICENSE');
|
||||||
array = TaskParameterSerializer.tryAddInput(array, 'GIT_PRIVATE_TOKEN');
|
array = TaskParameterSerializer.tryAddInput(array, 'GIT_PRIVATE_TOKEN');
|
||||||
|
|
||||||
return array;
|
return array;
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import YAML from 'yaml';
|
import YAML from 'yaml';
|
||||||
import CloudRunner from '../../cloud-runner';
|
import CloudRunner from '../../cloud-runner';
|
||||||
import * as core from '@actions/core';
|
|
||||||
import { CustomWorkflow } from '../../workflows/custom-workflow';
|
import { CustomWorkflow } from '../../workflows/custom-workflow';
|
||||||
import { RemoteClientLogger } from '../../remote-client/remote-client-logger';
|
import { RemoteClientLogger } from '../../remote-client/remote-client-logger';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
@ -237,13 +236,11 @@ export class ContainerHookService {
|
|||||||
];
|
];
|
||||||
|
|
||||||
if (steps.length > 0) {
|
if (steps.length > 0) {
|
||||||
if (!CloudRunner.buildParameters.isCliMode) core.startGroup('post build steps');
|
|
||||||
output += await CustomWorkflow.runContainerJob(
|
output += await CustomWorkflow.runContainerJob(
|
||||||
steps,
|
steps,
|
||||||
cloudRunnerStepState.environment,
|
cloudRunnerStepState.environment,
|
||||||
cloudRunnerStepState.secrets,
|
cloudRunnerStepState.secrets,
|
||||||
);
|
);
|
||||||
if (!CloudRunner.buildParameters.isCliMode) core.endGroup();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
@ -256,13 +253,11 @@ export class ContainerHookService {
|
|||||||
];
|
];
|
||||||
|
|
||||||
if (steps.length > 0) {
|
if (steps.length > 0) {
|
||||||
if (!CloudRunner.buildParameters.isCliMode) core.startGroup('pre build steps');
|
|
||||||
output += await CustomWorkflow.runContainerJob(
|
output += await CustomWorkflow.runContainerJob(
|
||||||
steps,
|
steps,
|
||||||
cloudRunnerStepState.environment,
|
cloudRunnerStepState.environment,
|
||||||
cloudRunnerStepState.secrets,
|
cloudRunnerStepState.secrets,
|
||||||
);
|
);
|
||||||
if (!CloudRunner.buildParameters.isCliMode) core.endGroup();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
|
@ -24,11 +24,17 @@ describe('Cloud Runner Async Workflows', () => {
|
|||||||
unityVersion: UnityVersioning.read('test-project'),
|
unityVersion: UnityVersioning.read('test-project'),
|
||||||
asyncCloudRunner: `true`,
|
asyncCloudRunner: `true`,
|
||||||
githubChecks: `true`,
|
githubChecks: `true`,
|
||||||
|
providerStrategy: 'k8s',
|
||||||
|
buildPlatform: 'linux',
|
||||||
|
targetPlatform: 'StandaloneLinux64',
|
||||||
});
|
});
|
||||||
const baseImage = new ImageTag(buildParameter);
|
const baseImage = new ImageTag(buildParameter);
|
||||||
|
|
||||||
// Run the job
|
// Run the job
|
||||||
await CloudRunner.run(buildParameter, baseImage.toString());
|
await CloudRunner.run(buildParameter, baseImage.toString());
|
||||||
|
|
||||||
|
// wait for 15 seconds
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 1000 * 60 * 12));
|
||||||
}, 1_000_000_000);
|
}, 1_000_000_000);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -34,6 +34,7 @@ describe('Cloud Runner Sync Environments', () => {
|
|||||||
versioning: 'None',
|
versioning: 'None',
|
||||||
projectPath: 'test-project',
|
projectPath: 'test-project',
|
||||||
unityVersion: UnityVersioning.read('test-project'),
|
unityVersion: UnityVersioning.read('test-project'),
|
||||||
|
targetPlatform: 'StandaloneWindows64',
|
||||||
customJob: `
|
customJob: `
|
||||||
- name: 'step 1'
|
- name: 'step 1'
|
||||||
image: 'ubuntu'
|
image: 'ubuntu'
|
||||||
@ -44,9 +45,12 @@ describe('Cloud Runner Sync Environments', () => {
|
|||||||
`,
|
`,
|
||||||
});
|
});
|
||||||
const baseImage = new ImageTag(buildParameter);
|
const baseImage = new ImageTag(buildParameter);
|
||||||
|
if (baseImage.toString().includes('undefined')) {
|
||||||
|
throw new Error(`Base image is undefined`);
|
||||||
|
}
|
||||||
|
|
||||||
// Run the job
|
// Run the job
|
||||||
const file = await CloudRunner.run(buildParameter, baseImage.toString());
|
const file = (await CloudRunner.run(buildParameter, baseImage.toString())).BuildResults;
|
||||||
|
|
||||||
// Assert results
|
// Assert results
|
||||||
// expect(file).toContain(JSON.stringify(buildParameter));
|
// expect(file).toContain(JSON.stringify(buildParameter));
|
||||||
|
@ -0,0 +1,59 @@
|
|||||||
|
import { BuildParameters } from '../..';
|
||||||
|
import CloudRunner from '../cloud-runner';
|
||||||
|
import UnityVersioning from '../../unity-versioning';
|
||||||
|
import { Cli } from '../../cli/cli';
|
||||||
|
import CloudRunnerOptions from '../options/cloud-runner-options';
|
||||||
|
import setups from './cloud-runner-suite.test';
|
||||||
|
import { OptionValues } from 'commander';
|
||||||
|
import GitHub from '../../github';
|
||||||
|
export const TIMEOUT_INFINITE = 1e9;
|
||||||
|
async function CreateParameters(overrides: OptionValues | undefined) {
|
||||||
|
if (overrides) Cli.options = overrides;
|
||||||
|
|
||||||
|
return BuildParameters.create();
|
||||||
|
}
|
||||||
|
describe('Cloud Runner Github Checks', () => {
|
||||||
|
setups();
|
||||||
|
it('Responds', () => {});
|
||||||
|
|
||||||
|
if (CloudRunnerOptions.cloudRunnerDebug) {
|
||||||
|
it(
|
||||||
|
'Check Handling Direct',
|
||||||
|
async () => {
|
||||||
|
// Setup parameters
|
||||||
|
const buildParameter = await CreateParameters({
|
||||||
|
versioning: 'None',
|
||||||
|
projectPath: 'test-project',
|
||||||
|
unityVersion: UnityVersioning.read('test-project'),
|
||||||
|
asyncCloudRunner: `true`,
|
||||||
|
githubChecks: `true`,
|
||||||
|
});
|
||||||
|
await CloudRunner.setup(buildParameter);
|
||||||
|
CloudRunner.buildParameters.githubCheckId = await GitHub.createGitHubCheck(`direct create`);
|
||||||
|
await GitHub.updateGitHubCheck(`1 ${new Date().toISOString()}`, `direct`);
|
||||||
|
await GitHub.updateGitHubCheck(`2 ${new Date().toISOString()}`, `direct`, `success`, `completed`);
|
||||||
|
},
|
||||||
|
TIMEOUT_INFINITE,
|
||||||
|
);
|
||||||
|
it(
|
||||||
|
'Check Handling Via Async Workflow',
|
||||||
|
async () => {
|
||||||
|
// Setup parameters
|
||||||
|
const buildParameter = await CreateParameters({
|
||||||
|
versioning: 'None',
|
||||||
|
projectPath: 'test-project',
|
||||||
|
unityVersion: UnityVersioning.read('test-project'),
|
||||||
|
asyncCloudRunner: `true`,
|
||||||
|
githubChecks: `true`,
|
||||||
|
});
|
||||||
|
GitHub.forceAsyncTest = true;
|
||||||
|
await CloudRunner.setup(buildParameter);
|
||||||
|
CloudRunner.buildParameters.githubCheckId = await GitHub.createGitHubCheck(`async create`);
|
||||||
|
await GitHub.updateGitHubCheck(`1 ${new Date().toISOString()}`, `async`);
|
||||||
|
await GitHub.updateGitHubCheck(`2 ${new Date().toISOString()}`, `async`, `success`, `completed`);
|
||||||
|
GitHub.forceAsyncTest = false;
|
||||||
|
},
|
||||||
|
TIMEOUT_INFINITE,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
@ -30,6 +30,7 @@ commands: echo "test"`;
|
|||||||
projectPath: 'test-project',
|
projectPath: 'test-project',
|
||||||
unityVersion: UnityVersioning.determineUnityVersion('test-project', UnityVersioning.read('test-project')),
|
unityVersion: UnityVersioning.determineUnityVersion('test-project', UnityVersioning.read('test-project')),
|
||||||
targetPlatform: 'StandaloneLinux64',
|
targetPlatform: 'StandaloneLinux64',
|
||||||
|
image: 'ubuntu',
|
||||||
cacheKey: `test-case-${uuidv4()}`,
|
cacheKey: `test-case-${uuidv4()}`,
|
||||||
};
|
};
|
||||||
CloudRunner.setup(await CreateParameters(overrides));
|
CloudRunner.setup(await CreateParameters(overrides));
|
||||||
@ -51,6 +52,7 @@ commands: echo "test"`;
|
|||||||
it('Should be 1 before and 1 after hook', async () => {
|
it('Should be 1 before and 1 after hook', async () => {
|
||||||
const overrides = {
|
const overrides = {
|
||||||
versioning: 'None',
|
versioning: 'None',
|
||||||
|
image: 'ubuntu',
|
||||||
projectPath: 'test-project',
|
projectPath: 'test-project',
|
||||||
unityVersion: UnityVersioning.determineUnityVersion('test-project', UnityVersioning.read('test-project')),
|
unityVersion: UnityVersioning.determineUnityVersion('test-project', UnityVersioning.read('test-project')),
|
||||||
targetPlatform: 'StandaloneLinux64',
|
targetPlatform: 'StandaloneLinux64',
|
||||||
@ -72,6 +74,7 @@ commands: echo "test"`;
|
|||||||
unityVersion: UnityVersioning.determineUnityVersion('test-project', UnityVersioning.read('test-project')),
|
unityVersion: UnityVersioning.determineUnityVersion('test-project', UnityVersioning.read('test-project')),
|
||||||
targetPlatform: 'StandaloneLinux64',
|
targetPlatform: 'StandaloneLinux64',
|
||||||
cacheKey: `test-case-${uuidv4()}`,
|
cacheKey: `test-case-${uuidv4()}`,
|
||||||
|
image: 'ubuntu',
|
||||||
containerHookFiles: `my-test-step-pre-build,my-test-step-post-build`,
|
containerHookFiles: `my-test-step-pre-build,my-test-step-post-build`,
|
||||||
commandHookFiles: `my-test-hook-pre-build,my-test-hook-post-build`,
|
commandHookFiles: `my-test-hook-pre-build,my-test-hook-post-build`,
|
||||||
};
|
};
|
||||||
@ -94,7 +97,8 @@ commands: echo "test"`;
|
|||||||
};
|
};
|
||||||
const buildParameter2 = await CreateParameters(overrides);
|
const buildParameter2 = await CreateParameters(overrides);
|
||||||
const baseImage2 = new ImageTag(buildParameter2);
|
const baseImage2 = new ImageTag(buildParameter2);
|
||||||
const results2 = await CloudRunner.run(buildParameter2, baseImage2.toString());
|
const results2Object = await CloudRunner.run(buildParameter2, baseImage2.toString());
|
||||||
|
const results2 = results2Object.BuildResults;
|
||||||
CloudRunnerLogger.log(`run 2 succeeded`);
|
CloudRunnerLogger.log(`run 2 succeeded`);
|
||||||
|
|
||||||
const buildContainsBuildSucceeded = results2.includes('Build succeeded');
|
const buildContainsBuildSucceeded = results2.includes('Build succeeded');
|
||||||
|
51
src/model/cloud-runner/tests/cloud-runner-image.test.ts
Normal file
51
src/model/cloud-runner/tests/cloud-runner-image.test.ts
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import { BuildParameters, ImageTag } from '../..';
|
||||||
|
import UnityVersioning from '../../unity-versioning';
|
||||||
|
import { Cli } from '../../cli/cli';
|
||||||
|
import GitHub from '../../github';
|
||||||
|
import setups from './cloud-runner-suite.test';
|
||||||
|
|
||||||
|
async function CreateParameters(overrides: any) {
|
||||||
|
if (overrides) {
|
||||||
|
Cli.options = overrides;
|
||||||
|
}
|
||||||
|
const originalValue = GitHub.githubInputEnabled;
|
||||||
|
GitHub.githubInputEnabled = false;
|
||||||
|
const results = await BuildParameters.create();
|
||||||
|
GitHub.githubInputEnabled = originalValue;
|
||||||
|
delete Cli.options;
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('Cloud Runner Image', () => {
|
||||||
|
setups();
|
||||||
|
const testSecretName = 'testSecretName';
|
||||||
|
const testSecretValue = 'testSecretValue';
|
||||||
|
it('Can create valid image from normal config', async () => {
|
||||||
|
// Setup parameters
|
||||||
|
const buildParameter = await CreateParameters({
|
||||||
|
versioning: 'None',
|
||||||
|
projectPath: 'test-project',
|
||||||
|
unityVersion: UnityVersioning.read('test-project'),
|
||||||
|
targetPlatform: 'StandaloneWindows64',
|
||||||
|
customJob: `
|
||||||
|
- name: 'step 1'
|
||||||
|
image: 'ubuntu'
|
||||||
|
commands: 'printenv'
|
||||||
|
secrets:
|
||||||
|
- name: '${testSecretName}'
|
||||||
|
value: '${testSecretValue}'
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
const baseImage = new ImageTag(buildParameter);
|
||||||
|
if (buildParameter.targetPlatform === undefined) {
|
||||||
|
throw new Error(`target platform includes undefined`);
|
||||||
|
}
|
||||||
|
if (baseImage.toString().includes('undefined')) {
|
||||||
|
throw new Error(`Base image ${baseImage.toString()} includes undefined`);
|
||||||
|
}
|
||||||
|
if (baseImage.toString().includes('NaN')) {
|
||||||
|
throw new Error(`Base image ${baseImage.toString()} includes nan`);
|
||||||
|
}
|
||||||
|
}, 1_000_000_000);
|
||||||
|
});
|
@ -32,7 +32,8 @@ describe('Cloud Runner pre-built S3 steps', () => {
|
|||||||
};
|
};
|
||||||
const buildParameter2 = await CreateParameters(overrides);
|
const buildParameter2 = await CreateParameters(overrides);
|
||||||
const baseImage2 = new ImageTag(buildParameter2);
|
const baseImage2 = new ImageTag(buildParameter2);
|
||||||
const results2 = await CloudRunner.run(buildParameter2, baseImage2.toString());
|
const results2Object = await CloudRunner.run(buildParameter2, baseImage2.toString());
|
||||||
|
const results2 = results2Object.BuildResults;
|
||||||
CloudRunnerLogger.log(`run 2 succeeded`);
|
CloudRunnerLogger.log(`run 2 succeeded`);
|
||||||
|
|
||||||
const build2ContainsBuildSucceeded = results2.includes('Build succeeded');
|
const build2ContainsBuildSucceeded = results2.includes('Build succeeded');
|
||||||
|
@ -24,11 +24,13 @@ describe('Cloud Runner Caching', () => {
|
|||||||
it('Run one build it should not use cache, run subsequent build which should use cache', async () => {
|
it('Run one build it should not use cache, run subsequent build which should use cache', async () => {
|
||||||
const overrides = {
|
const overrides = {
|
||||||
versioning: 'None',
|
versioning: 'None',
|
||||||
|
image: 'ubuntu',
|
||||||
projectPath: 'test-project',
|
projectPath: 'test-project',
|
||||||
unityVersion: UnityVersioning.determineUnityVersion('test-project', UnityVersioning.read('test-project')),
|
unityVersion: UnityVersioning.determineUnityVersion('test-project', UnityVersioning.read('test-project')),
|
||||||
targetPlatform: 'StandaloneLinux64',
|
targetPlatform: 'StandaloneLinux64',
|
||||||
cacheKey: `test-case-${uuidv4()}`,
|
cacheKey: `test-case-${uuidv4()}`,
|
||||||
containerHookFiles: `debug-cache`,
|
containerHookFiles: `debug-cache`,
|
||||||
|
cloudRunnerBranch: `cloud-runner-develop`,
|
||||||
};
|
};
|
||||||
if (CloudRunnerOptions.providerStrategy === `k8s`) {
|
if (CloudRunnerOptions.providerStrategy === `k8s`) {
|
||||||
overrides.containerHookFiles += `,aws-s3-pull-cache,aws-s3-upload-cache`;
|
overrides.containerHookFiles += `,aws-s3-pull-cache,aws-s3-upload-cache`;
|
||||||
@ -37,7 +39,8 @@ describe('Cloud Runner Caching', () => {
|
|||||||
expect(buildParameter.projectPath).toEqual(overrides.projectPath);
|
expect(buildParameter.projectPath).toEqual(overrides.projectPath);
|
||||||
|
|
||||||
const baseImage = new ImageTag(buildParameter);
|
const baseImage = new ImageTag(buildParameter);
|
||||||
const results = await CloudRunner.run(buildParameter, baseImage.toString());
|
const resultsObject = await CloudRunner.run(buildParameter, baseImage.toString());
|
||||||
|
const results = resultsObject.BuildResults;
|
||||||
const libraryString = 'Rebuilding Library because the asset database could not be found!';
|
const libraryString = 'Rebuilding Library because the asset database could not be found!';
|
||||||
const cachePushFail = 'Did not push source folder to cache because it was empty Library';
|
const cachePushFail = 'Did not push source folder to cache because it was empty Library';
|
||||||
const buildSucceededString = 'Build succeeded';
|
const buildSucceededString = 'Build succeeded';
|
||||||
@ -63,12 +66,12 @@ describe('Cloud Runner Caching', () => {
|
|||||||
|
|
||||||
buildParameter2.cacheKey = buildParameter.cacheKey;
|
buildParameter2.cacheKey = buildParameter.cacheKey;
|
||||||
const baseImage2 = new ImageTag(buildParameter2);
|
const baseImage2 = new ImageTag(buildParameter2);
|
||||||
const results2 = await CloudRunner.run(buildParameter2, baseImage2.toString());
|
const results2Object = await CloudRunner.run(buildParameter2, baseImage2.toString());
|
||||||
|
const results2 = results2Object.BuildResults;
|
||||||
CloudRunnerLogger.log(`run 2 succeeded`);
|
CloudRunnerLogger.log(`run 2 succeeded`);
|
||||||
|
|
||||||
const build2ContainsCacheKey = results2.includes(buildParameter.cacheKey);
|
const build2ContainsCacheKey = results2.includes(buildParameter.cacheKey);
|
||||||
const build2ContainsBuildSucceeded = results2.includes(buildSucceededString);
|
const build2ContainsBuildSucceeded = results2.includes(buildSucceededString);
|
||||||
const build2NotContainsNoLibraryMessage = !results2.includes(libraryString);
|
|
||||||
const build2NotContainsZeroLibraryCacheFilesMessage = !results2.includes(
|
const build2NotContainsZeroLibraryCacheFilesMessage = !results2.includes(
|
||||||
'There is 0 files/dir in the cache pulled contents for Library',
|
'There is 0 files/dir in the cache pulled contents for Library',
|
||||||
);
|
);
|
||||||
@ -77,10 +80,13 @@ describe('Cloud Runner Caching', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
expect(build2ContainsCacheKey).toBeTruthy();
|
expect(build2ContainsCacheKey).toBeTruthy();
|
||||||
|
expect(results2).toContain('Activation successful');
|
||||||
expect(build2ContainsBuildSucceeded).toBeTruthy();
|
expect(build2ContainsBuildSucceeded).toBeTruthy();
|
||||||
|
expect(results2).toContain(buildSucceededString);
|
||||||
|
const splitResults = results2.split('Activation successful');
|
||||||
|
expect(splitResults[splitResults.length - 1]).not.toContain(libraryString);
|
||||||
expect(build2NotContainsZeroLibraryCacheFilesMessage).toBeTruthy();
|
expect(build2NotContainsZeroLibraryCacheFilesMessage).toBeTruthy();
|
||||||
expect(build2NotContainsZeroLFSCacheFilesMessage).toBeTruthy();
|
expect(build2NotContainsZeroLFSCacheFilesMessage).toBeTruthy();
|
||||||
expect(build2NotContainsNoLibraryMessage).toBeTruthy();
|
|
||||||
}, 1_000_000_000);
|
}, 1_000_000_000);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -29,7 +29,8 @@ describe('Cloud Runner Retain Workspace', () => {
|
|||||||
expect(buildParameter.projectPath).toEqual(overrides.projectPath);
|
expect(buildParameter.projectPath).toEqual(overrides.projectPath);
|
||||||
|
|
||||||
const baseImage = new ImageTag(buildParameter);
|
const baseImage = new ImageTag(buildParameter);
|
||||||
const results = await CloudRunner.run(buildParameter, baseImage.toString());
|
const resultsObject = await CloudRunner.run(buildParameter, baseImage.toString());
|
||||||
|
const results = resultsObject.BuildResults;
|
||||||
const libraryString = 'Rebuilding Library because the asset database could not be found!';
|
const libraryString = 'Rebuilding Library because the asset database could not be found!';
|
||||||
const cachePushFail = 'Did not push source folder to cache because it was empty Library';
|
const cachePushFail = 'Did not push source folder to cache because it was empty Library';
|
||||||
const buildSucceededString = 'Build succeeded';
|
const buildSucceededString = 'Build succeeded';
|
||||||
@ -51,7 +52,8 @@ describe('Cloud Runner Retain Workspace', () => {
|
|||||||
|
|
||||||
buildParameter2.cacheKey = buildParameter.cacheKey;
|
buildParameter2.cacheKey = buildParameter.cacheKey;
|
||||||
const baseImage2 = new ImageTag(buildParameter2);
|
const baseImage2 = new ImageTag(buildParameter2);
|
||||||
const results2 = await CloudRunner.run(buildParameter2, baseImage2.toString());
|
const results2Object = await CloudRunner.run(buildParameter2, baseImage2.toString());
|
||||||
|
const results2 = results2Object.BuildResults;
|
||||||
CloudRunnerLogger.log(`run 2 succeeded`);
|
CloudRunnerLogger.log(`run 2 succeeded`);
|
||||||
|
|
||||||
const build2ContainsCacheKey = results2.includes(buildParameter.cacheKey);
|
const build2ContainsCacheKey = results2.includes(buildParameter.cacheKey);
|
||||||
@ -59,7 +61,6 @@ describe('Cloud Runner Retain Workspace', () => {
|
|||||||
const build2ContainsRetainedWorkspacePhrase = results2.includes(`Retained Workspace:`);
|
const build2ContainsRetainedWorkspacePhrase = results2.includes(`Retained Workspace:`);
|
||||||
const build2ContainsWorkspaceExistsAlreadyPhrase = results2.includes(`Retained Workspace Already Exists!`);
|
const build2ContainsWorkspaceExistsAlreadyPhrase = results2.includes(`Retained Workspace Already Exists!`);
|
||||||
const build2ContainsBuildSucceeded = results2.includes(buildSucceededString);
|
const build2ContainsBuildSucceeded = results2.includes(buildSucceededString);
|
||||||
const build2NotContainsNoLibraryMessage = !results2.includes(libraryString);
|
|
||||||
const build2NotContainsZeroLibraryCacheFilesMessage = !results2.includes(
|
const build2NotContainsZeroLibraryCacheFilesMessage = !results2.includes(
|
||||||
'There is 0 files/dir in the cache pulled contents for Library',
|
'There is 0 files/dir in the cache pulled contents for Library',
|
||||||
);
|
);
|
||||||
@ -74,7 +75,8 @@ describe('Cloud Runner Retain Workspace', () => {
|
|||||||
expect(build2ContainsBuildSucceeded).toBeTruthy();
|
expect(build2ContainsBuildSucceeded).toBeTruthy();
|
||||||
expect(build2NotContainsZeroLibraryCacheFilesMessage).toBeTruthy();
|
expect(build2NotContainsZeroLibraryCacheFilesMessage).toBeTruthy();
|
||||||
expect(build2NotContainsZeroLFSCacheFilesMessage).toBeTruthy();
|
expect(build2NotContainsZeroLFSCacheFilesMessage).toBeTruthy();
|
||||||
expect(build2NotContainsNoLibraryMessage).toBeTruthy();
|
const splitResults = results2.split('Activation successful');
|
||||||
|
expect(splitResults[splitResults.length - 1]).not.toContain(libraryString);
|
||||||
}, 1_000_000_000);
|
}, 1_000_000_000);
|
||||||
afterAll(async () => {
|
afterAll(async () => {
|
||||||
await SharedWorkspaceLocking.CleanupWorkspace(CloudRunner.lockedWorkspace || ``, CloudRunner.buildParameters);
|
await SharedWorkspaceLocking.CleanupWorkspace(CloudRunner.lockedWorkspace || ``, CloudRunner.buildParameters);
|
||||||
|
@ -0,0 +1,56 @@
|
|||||||
|
import CloudRunner from '../../cloud-runner';
|
||||||
|
import UnityVersioning from '../../../unity-versioning';
|
||||||
|
import { Cli } from '../../../cli/cli';
|
||||||
|
import CloudRunnerLogger from '../../services/core/cloud-runner-logger';
|
||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
import CloudRunnerOptions from '../../options/cloud-runner-options';
|
||||||
|
import setups from '../cloud-runner-suite.test';
|
||||||
|
import BuildParameters from '../../../build-parameters';
|
||||||
|
import ImageTag from '../../../image-tag';
|
||||||
|
|
||||||
|
async function CreateParameters(overrides: any) {
|
||||||
|
if (overrides) {
|
||||||
|
Cli.options = overrides;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await BuildParameters.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('Cloud Runner Kubernetes', () => {
|
||||||
|
it('Responds', () => {});
|
||||||
|
setups();
|
||||||
|
|
||||||
|
if (CloudRunnerOptions.cloudRunnerDebug) {
|
||||||
|
it('Run one build it using K8s without error', async () => {
|
||||||
|
if (CloudRunnerOptions.providerStrategy !== `k8s`) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
process.env.USE_IL2CPP = 'false';
|
||||||
|
const overrides = {
|
||||||
|
versioning: 'None',
|
||||||
|
projectPath: 'test-project',
|
||||||
|
unityVersion: UnityVersioning.determineUnityVersion('test-project', UnityVersioning.read('test-project')),
|
||||||
|
targetPlatform: 'StandaloneLinux64',
|
||||||
|
cacheKey: `test-case-${uuidv4()}`,
|
||||||
|
providerStrategy: 'k8s',
|
||||||
|
buildPlatform: 'linux',
|
||||||
|
};
|
||||||
|
const buildParameter = await CreateParameters(overrides);
|
||||||
|
expect(buildParameter.projectPath).toEqual(overrides.projectPath);
|
||||||
|
|
||||||
|
const baseImage = new ImageTag(buildParameter);
|
||||||
|
const resultsObject = await CloudRunner.run(buildParameter, baseImage.toString());
|
||||||
|
const results = resultsObject.BuildResults;
|
||||||
|
const libraryString = 'Rebuilding Library because the asset database could not be found!';
|
||||||
|
const cachePushFail = 'Did not push source folder to cache because it was empty Library';
|
||||||
|
const buildSucceededString = 'Build succeeded';
|
||||||
|
|
||||||
|
expect(results).toContain('Collected Logs');
|
||||||
|
expect(results).toContain(libraryString);
|
||||||
|
expect(results).toContain(buildSucceededString);
|
||||||
|
expect(results).not.toContain(cachePushFail);
|
||||||
|
|
||||||
|
CloudRunnerLogger.log(`run 1 succeeded`);
|
||||||
|
}, 1_000_000_000);
|
||||||
|
}
|
||||||
|
});
|
@ -41,6 +41,11 @@ node /builder/dist/index.js -m async-workflow`,
|
|||||||
[
|
[
|
||||||
...secrets,
|
...secrets,
|
||||||
...[
|
...[
|
||||||
|
{
|
||||||
|
ParameterKey: `GITHUB_TOKEN`,
|
||||||
|
EnvironmentVariable: `GITHUB_TOKEN`,
|
||||||
|
ParameterValue: process.env.GITHUB_TOKEN || ``,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
ParameterKey: `AWS_ACCESS_KEY_ID`,
|
ParameterKey: `AWS_ACCESS_KEY_ID`,
|
||||||
EnvironmentVariable: `AWS_ACCESS_KEY_ID`,
|
EnvironmentVariable: `AWS_ACCESS_KEY_ID`,
|
||||||
|
@ -2,7 +2,6 @@ import CloudRunnerLogger from '../services/core/cloud-runner-logger';
|
|||||||
import { CloudRunnerFolders } from '../options/cloud-runner-folders';
|
import { CloudRunnerFolders } from '../options/cloud-runner-folders';
|
||||||
import { CloudRunnerStepParameters } from '../options/cloud-runner-step-parameters';
|
import { CloudRunnerStepParameters } from '../options/cloud-runner-step-parameters';
|
||||||
import { WorkflowInterface } from './workflow-interface';
|
import { WorkflowInterface } from './workflow-interface';
|
||||||
import * as core from '@actions/core';
|
|
||||||
import { CommandHookService } from '../services/hooks/command-hook-service';
|
import { CommandHookService } from '../services/hooks/command-hook-service';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import CloudRunner from '../cloud-runner';
|
import CloudRunner from '../cloud-runner';
|
||||||
@ -21,8 +20,6 @@ export class BuildAutomationWorkflow implements WorkflowInterface {
|
|||||||
|
|
||||||
output += await ContainerHookService.RunPreBuildSteps(cloudRunnerStepState);
|
output += await ContainerHookService.RunPreBuildSteps(cloudRunnerStepState);
|
||||||
CloudRunnerLogger.logWithTime('Configurable pre build step(s) time');
|
CloudRunnerLogger.logWithTime('Configurable pre build step(s) time');
|
||||||
|
|
||||||
if (!CloudRunner.buildParameters.isCliMode) core.startGroup('build');
|
|
||||||
CloudRunnerLogger.log(baseImage);
|
CloudRunnerLogger.log(baseImage);
|
||||||
CloudRunnerLogger.logLine(` `);
|
CloudRunnerLogger.logLine(` `);
|
||||||
CloudRunnerLogger.logLine('Starting build automation job');
|
CloudRunnerLogger.logLine('Starting build automation job');
|
||||||
@ -36,7 +33,6 @@ export class BuildAutomationWorkflow implements WorkflowInterface {
|
|||||||
cloudRunnerStepState.environment,
|
cloudRunnerStepState.environment,
|
||||||
cloudRunnerStepState.secrets,
|
cloudRunnerStepState.secrets,
|
||||||
);
|
);
|
||||||
if (!CloudRunner.buildParameters.isCliMode) core.endGroup();
|
|
||||||
CloudRunnerLogger.logWithTime('Build time');
|
CloudRunnerLogger.logWithTime('Build time');
|
||||||
|
|
||||||
output += await ContainerHookService.RunPostBuildSteps(cloudRunnerStepState);
|
output += await ContainerHookService.RunPostBuildSteps(cloudRunnerStepState);
|
||||||
@ -58,11 +54,14 @@ export class BuildAutomationWorkflow implements WorkflowInterface {
|
|||||||
path.join(CloudRunnerFolders.builderPathAbsolute, 'dist', `index.js`),
|
path.join(CloudRunnerFolders.builderPathAbsolute, 'dist', `index.js`),
|
||||||
);
|
);
|
||||||
|
|
||||||
return `apt-get update > /dev/null
|
return `echo "cloud runner build workflow starting"
|
||||||
|
apt-get update > /dev/null
|
||||||
apt-get install -y curl tar tree npm git-lfs jq git > /dev/null
|
apt-get install -y curl tar tree npm git-lfs jq git > /dev/null
|
||||||
npm i -g n > /dev/null
|
|
||||||
n 16.15.1 > /dev/null
|
|
||||||
npm --version
|
npm --version
|
||||||
|
npm i -g n > /dev/null
|
||||||
|
npm i -g semver > /dev/null
|
||||||
|
npm install --global yarn > /dev/null
|
||||||
|
n 20.8.0
|
||||||
node --version
|
node --version
|
||||||
${setupHooks.filter((x) => x.hook.includes(`before`)).map((x) => x.commands) || ' '}
|
${setupHooks.filter((x) => x.hook.includes(`before`)).map((x) => x.commands) || ' '}
|
||||||
export GITHUB_WORKSPACE="${CloudRunnerFolders.ToLinuxFolder(CloudRunnerFolders.repoPathAbsolute)}"
|
export GITHUB_WORKSPACE="${CloudRunnerFolders.ToLinuxFolder(CloudRunnerFolders.repoPathAbsolute)}"
|
||||||
@ -91,6 +90,7 @@ export class BuildAutomationWorkflow implements WorkflowInterface {
|
|||||||
|
|
||||||
return `export GIT_DISCOVERY_ACROSS_FILESYSTEM=1
|
return `export GIT_DISCOVERY_ACROSS_FILESYSTEM=1
|
||||||
${cloneBuilderCommands}
|
${cloneBuilderCommands}
|
||||||
|
echo "log start" >> /home/job-log.txt
|
||||||
node ${builderPath} -m remote-cli-pre-build`;
|
node ${builderPath} -m remote-cli-pre-build`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,7 +98,7 @@ node ${builderPath} -m remote-cli-pre-build`;
|
|||||||
const distFolder = path.join(CloudRunnerFolders.builderPathAbsolute, 'dist');
|
const distFolder = path.join(CloudRunnerFolders.builderPathAbsolute, 'dist');
|
||||||
const ubuntuPlatformsFolder = path.join(CloudRunnerFolders.builderPathAbsolute, 'dist', 'platforms', 'ubuntu');
|
const ubuntuPlatformsFolder = path.join(CloudRunnerFolders.builderPathAbsolute, 'dist', 'platforms', 'ubuntu');
|
||||||
|
|
||||||
return `echo "game ci cloud runner initalized"
|
return `
|
||||||
mkdir -p ${`${CloudRunnerFolders.ToLinuxFolder(CloudRunnerFolders.projectBuildFolderAbsolute)}/build`}
|
mkdir -p ${`${CloudRunnerFolders.ToLinuxFolder(CloudRunnerFolders.projectBuildFolderAbsolute)}/build`}
|
||||||
cd ${CloudRunnerFolders.ToLinuxFolder(CloudRunnerFolders.projectPathAbsolute)}
|
cd ${CloudRunnerFolders.ToLinuxFolder(CloudRunnerFolders.projectPathAbsolute)}
|
||||||
cp -r "${CloudRunnerFolders.ToLinuxFolder(path.join(distFolder, 'default-build-script'))}" "/UnityBuilderAction"
|
cp -r "${CloudRunnerFolders.ToLinuxFolder(path.join(distFolder, 'default-build-script'))}" "/UnityBuilderAction"
|
||||||
@ -107,9 +107,8 @@ node ${builderPath} -m remote-cli-pre-build`;
|
|||||||
chmod -R +x "/entrypoint.sh"
|
chmod -R +x "/entrypoint.sh"
|
||||||
chmod -R +x "/steps"
|
chmod -R +x "/steps"
|
||||||
echo "game ci start"
|
echo "game ci start"
|
||||||
/entrypoint.sh
|
echo "game ci start" >> /home/job-log.txt
|
||||||
echo "game ci caching results"
|
/entrypoint.sh | node ${builderPath} -m remote-cli-log-stream --logFile /home/job-log.txt
|
||||||
chmod +x ${builderPath}
|
|
||||||
node ${builderPath} -m remote-cli-post-build`;
|
node ${builderPath} -m remote-cli-post-build`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ class GitHub {
|
|||||||
private static startedDate: string;
|
private static startedDate: string;
|
||||||
private static endedDate: string;
|
private static endedDate: string;
|
||||||
static result: string = ``;
|
static result: string = ``;
|
||||||
|
static forceAsyncTest: boolean;
|
||||||
private static get octokitDefaultToken() {
|
private static get octokitDefaultToken() {
|
||||||
return new Octokit({
|
return new Octokit({
|
||||||
auth: process.env.GITHUB_TOKEN,
|
auth: process.env.GITHUB_TOKEN,
|
||||||
@ -51,7 +52,7 @@ class GitHub {
|
|||||||
}
|
}
|
||||||
GitHub.startedDate = new Date().toISOString();
|
GitHub.startedDate = new Date().toISOString();
|
||||||
|
|
||||||
CloudRunnerLogger.log(`Creating inital github check`);
|
CloudRunnerLogger.log(`Creating github check`);
|
||||||
const data = {
|
const data = {
|
||||||
owner: GitHub.owner,
|
owner: GitHub.owner,
|
||||||
repo: GitHub.repo,
|
repo: GitHub.repo,
|
||||||
@ -78,6 +79,8 @@ class GitHub {
|
|||||||
};
|
};
|
||||||
const result = await GitHub.createGitHubCheckRequest(data);
|
const result = await GitHub.createGitHubCheckRequest(data);
|
||||||
|
|
||||||
|
CloudRunnerLogger.log(`Creating github check ${result.status}`);
|
||||||
|
|
||||||
return result.data.id.toString();
|
return result.data.id.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,7 +130,7 @@ class GitHub {
|
|||||||
data.conclusion = result;
|
data.conclusion = result;
|
||||||
}
|
}
|
||||||
|
|
||||||
await (CloudRunner.isCloudRunnerAsyncEnvironment
|
await (CloudRunner.isCloudRunnerAsyncEnvironment || GitHub.forceAsyncTest
|
||||||
? GitHub.runUpdateAsyncChecksWorkflow(data, `update`)
|
? GitHub.runUpdateAsyncChecksWorkflow(data, `update`)
|
||||||
: GitHub.updateGitHubCheckRequest(data));
|
: GitHub.updateGitHubCheckRequest(data));
|
||||||
}
|
}
|
||||||
@ -174,9 +177,10 @@ class GitHub {
|
|||||||
|
|
||||||
static async triggerWorkflowOnComplete(triggerWorkflowOnComplete: string[]) {
|
static async triggerWorkflowOnComplete(triggerWorkflowOnComplete: string[]) {
|
||||||
const isLocalAsync = CloudRunner.buildParameters.asyncWorkflow && !CloudRunner.isCloudRunnerAsyncEnvironment;
|
const isLocalAsync = CloudRunner.buildParameters.asyncWorkflow && !CloudRunner.isCloudRunnerAsyncEnvironment;
|
||||||
if (isLocalAsync) {
|
if (isLocalAsync || triggerWorkflowOnComplete === undefined || triggerWorkflowOnComplete.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
const workflowsResult = await GitHub.octokitPAT.request(`GET /repos/{owner}/{repo}/actions/workflows`, {
|
const workflowsResult = await GitHub.octokitPAT.request(`GET /repos/{owner}/{repo}/actions/workflows`, {
|
||||||
owner: GitHub.owner,
|
owner: GitHub.owner,
|
||||||
repo: GitHub.repo,
|
repo: GitHub.repo,
|
||||||
@ -205,6 +209,13 @@ class GitHub {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} catch {
|
||||||
|
core.info(`github workflow complete hook not found`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async getCheckStatus() {
|
||||||
|
return await GitHub.octokitDefaultToken.request(`GET /repos/{owner}/{repo}/check-runs/{check_run_id}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@ import Platform from './platform';
|
|||||||
|
|
||||||
class ImageTag {
|
class ImageTag {
|
||||||
public repository: string;
|
public repository: string;
|
||||||
public cloudRunnerBuilderPlatform!: string;
|
|
||||||
public editorVersion: string;
|
public editorVersion: string;
|
||||||
public targetPlatform: string;
|
public targetPlatform: string;
|
||||||
public builderPlatform: string;
|
public builderPlatform: string;
|
||||||
@ -15,7 +14,7 @@ class ImageTag {
|
|||||||
editorVersion,
|
editorVersion,
|
||||||
targetPlatform,
|
targetPlatform,
|
||||||
customImage,
|
customImage,
|
||||||
cloudRunnerBuilderPlatform,
|
buildPlatform,
|
||||||
containerRegistryRepository,
|
containerRegistryRepository,
|
||||||
containerRegistryImageVersion,
|
containerRegistryImageVersion,
|
||||||
} = imageProperties;
|
} = imageProperties;
|
||||||
@ -32,12 +31,8 @@ class ImageTag {
|
|||||||
this.repository = containerRegistryRepository;
|
this.repository = containerRegistryRepository;
|
||||||
this.editorVersion = editorVersion;
|
this.editorVersion = editorVersion;
|
||||||
this.targetPlatform = targetPlatform;
|
this.targetPlatform = targetPlatform;
|
||||||
this.cloudRunnerBuilderPlatform = cloudRunnerBuilderPlatform;
|
|
||||||
const isCloudRunnerLocal = cloudRunnerBuilderPlatform === 'local' || cloudRunnerBuilderPlatform === undefined;
|
|
||||||
this.builderPlatform = ImageTag.getTargetPlatformToTargetPlatformSuffixMap(targetPlatform, editorVersion);
|
this.builderPlatform = ImageTag.getTargetPlatformToTargetPlatformSuffixMap(targetPlatform, editorVersion);
|
||||||
this.imagePlatformPrefix = ImageTag.getImagePlatformPrefixes(
|
this.imagePlatformPrefix = ImageTag.getImagePlatformPrefixes(buildPlatform);
|
||||||
isCloudRunnerLocal ? process.platform : cloudRunnerBuilderPlatform,
|
|
||||||
);
|
|
||||||
this.imageRollingVersion = Number(containerRegistryImageVersion); // Will automatically roll to the latest non-breaking version.
|
this.imageRollingVersion = Number(containerRegistryImageVersion); // Will automatically roll to the latest non-breaking version.
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,6 +58,10 @@ class ImageTag {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static getImagePlatformPrefixes(platform: string): string {
|
static getImagePlatformPrefixes(platform: string): string {
|
||||||
|
if (!platform || platform === '') {
|
||||||
|
platform = process.platform;
|
||||||
|
}
|
||||||
|
|
||||||
switch (platform) {
|
switch (platform) {
|
||||||
case 'win32':
|
case 'win32':
|
||||||
return 'windows';
|
return 'windows';
|
||||||
@ -101,7 +100,7 @@ class ImageTag {
|
|||||||
return windows;
|
return windows;
|
||||||
case Platform.types.StandaloneLinux64: {
|
case Platform.types.StandaloneLinux64: {
|
||||||
// Unity versions before 2019.3 do not support il2cpp
|
// Unity versions before 2019.3 do not support il2cpp
|
||||||
if (major >= 2020 || (major === 2019 && minor >= 3)) {
|
if (process.env.USE_IL2CPP === 'true' && (major >= 2020 || (major === 2019 && minor >= 3))) {
|
||||||
return linuxIl2cpp;
|
return linuxIl2cpp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,11 +261,11 @@ class Input {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static get containerRegistryRepository(): string {
|
static get containerRegistryRepository(): string {
|
||||||
return Input.getInput('containerRegistryRepository')!;
|
return Input.getInput('containerRegistryRepository') || 'unityci/editor';
|
||||||
}
|
}
|
||||||
|
|
||||||
static get containerRegistryImageVersion(): string {
|
static get containerRegistryImageVersion(): string {
|
||||||
return Input.getInput('containerRegistryImageVersion')!;
|
return Input.getInput('containerRegistryImageVersion') || '3';
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ToEnvVarFormat(input: string) {
|
public static ToEnvVarFormat(input: string) {
|
||||||
|
Loading…
Reference in New Issue
Block a user