From 4cca069ebb19078d2cdaf52f5af504bf59eba7d9 Mon Sep 17 00:00:00 2001 From: Frostebite Date: Mon, 7 Nov 2022 20:41:00 +0000 Subject: [PATCH] Cloud Runner Develop - 1.0 R.C 1 (#437) Release Candidate changeset 1 - For 1.0 Cloud Runner --- .github/workflows/build-tests.yml | 29 +- .github/workflows/cleanup.yml | 2 +- .../workflows/cloud-runner-local-pipeline.yml | 96 ++++++ .github/workflows/cloud-runner-pipeline.yml | 285 +++++++++--------- .vscode/settings.json | 1 + action.yml | 85 +++--- dist/index.js | Bin 21898504 -> 21968567 bytes dist/index.js.map | Bin 16283017 -> 16362387 bytes game-ci/hooks/my-test-hook-post-build.yaml | 3 + game-ci/hooks/my-test-hook-pre-build.yaml | 3 + game-ci/steps/my-test-step-post-build.yaml | 3 + game-ci/steps/my-test-step-pre-build.yaml | 3 + jest.config.js | 1 + package.json | 10 +- src/model/build-parameters.ts | 70 +++-- src/model/cli/cli.ts | 112 ++++++- .../cloud-runner/cloud-runner-options.ts | 265 ++++++++++++++++ src/model/cloud-runner/cloud-runner.test.ts | 144 --------- src/model/cloud-runner/cloud-runner.ts | 72 ++++- .../cloud-runner/error/cloud-runner-error.ts | 18 +- .../cloud-runner/providers/aws/aws-error.ts | 2 +- .../providers/aws/aws-job-stack.ts | 6 + .../providers/aws/aws-task-runner.ts | 109 +++---- .../task-definition-formation.ts | 10 +- .../aws/commands/aws-cli-commands.ts | 170 ----------- src/model/cloud-runner/providers/aws/index.ts | 62 +++- .../services/garbage-collection-service.ts | 58 ++++ .../providers/aws/services/task-service.ts | 131 ++++++++ .../services/tertiary-resources-service.ts | 36 +++ .../cloud-runner/providers/docker/index.ts | 148 +++++++++ src/model/cloud-runner/providers/k8s/index.ts | 236 ++++++++++----- .../k8s/kubernetes-job-spec-factory.ts | 4 +- .../providers/k8s/kubernetes-pods.ts | 14 + .../providers/k8s/kubernetes-secret.ts | 40 ++- .../providers/k8s/kubernetes-storage.ts | 16 +- .../providers/local-docker/index.ts | 49 --- .../cloud-runner/providers/local/index.ts | 31 +- .../providers/provider-interface.ts | 23 +- .../providers/provider-resource.ts | 3 + .../providers/provider-workflow.ts | 3 + .../cloud-runner/providers/test/index.ts | 25 +- .../cloud-runner/remote-client/caching.ts | 86 +++--- src/model/cloud-runner/remote-client/index.ts | 110 +++++-- .../cloud-runner-build-command-process.ts | 43 --- .../services/cloud-runner-custom-hooks.ts | 117 +++++++ .../services/cloud-runner-custom-steps.ts | 206 +++++++++++++ .../services/cloud-runner-folders.ts | 28 +- .../services/cloud-runner-options-reader.ts | 10 + .../services/cloud-runner-query-override.ts | 17 +- .../services/cloud-runner-system.ts | 14 + .../services/depdency-override-service.ts | 22 -- .../services/follow-log-stream-service.ts | 9 +- .../services/shared-workspace-locking.ts | 279 +++++++++++++++++ .../services/task-parameter-serializer.ts | 177 +++++++---- ...loud-runner-environment-serializer.test.ts | 45 +++ ...loud-runner-remote-client-caching.test.ts} | 17 +- ...cloud-runner-run-once-custom-hooks.test.ts | 70 +++++ .../cloud-runner-run-twice-caching.test.ts | 72 +++++ .../cloud-runner-run-twice-retaining.test.ts | 93 ++++++ .../cloud-runner-s3-prebuilt-steps.test.ts | 46 +++ .../tests/cloud-runner-suite.test.ts | 25 ++ .../cloud-runner-sync-environment.test.ts | 77 +++++ .../tests/shared-workspace-locking.test.ts | 99 ++++++ .../workflows/build-automation-workflow.ts | 122 ++++---- .../cloud-runner/workflows/custom-workflow.ts | 56 ++-- .../workflows/workflow-composition-root.ts | 20 +- src/model/docker.ts | 34 ++- src/model/github.ts | 5 + src/model/image-environment-factory.ts | 29 +- src/model/image-tag.ts | 5 +- .../input-readers/generic-input-reader.ts | 4 +- src/model/input-readers/git-repo.test.ts | 6 +- src/model/input-readers/git-repo.ts | 12 +- src/model/input-readers/github-cli.ts | 4 +- .../input-readers/test-license-reader.ts | 4 +- src/model/input.ts | 108 +------ 76 files changed, 3213 insertions(+), 1236 deletions(-) create mode 100644 .github/workflows/cloud-runner-local-pipeline.yml create mode 100644 game-ci/hooks/my-test-hook-post-build.yaml create mode 100644 game-ci/hooks/my-test-hook-pre-build.yaml create mode 100644 game-ci/steps/my-test-step-post-build.yaml create mode 100644 game-ci/steps/my-test-step-pre-build.yaml create mode 100644 src/model/cloud-runner/cloud-runner-options.ts delete mode 100644 src/model/cloud-runner/cloud-runner.test.ts delete mode 100644 src/model/cloud-runner/providers/aws/commands/aws-cli-commands.ts create mode 100644 src/model/cloud-runner/providers/aws/services/garbage-collection-service.ts create mode 100644 src/model/cloud-runner/providers/aws/services/task-service.ts create mode 100644 src/model/cloud-runner/providers/aws/services/tertiary-resources-service.ts create mode 100644 src/model/cloud-runner/providers/docker/index.ts create mode 100644 src/model/cloud-runner/providers/k8s/kubernetes-pods.ts delete mode 100644 src/model/cloud-runner/providers/local-docker/index.ts create mode 100644 src/model/cloud-runner/providers/provider-resource.ts create mode 100644 src/model/cloud-runner/providers/provider-workflow.ts delete mode 100644 src/model/cloud-runner/services/cloud-runner-build-command-process.ts create mode 100644 src/model/cloud-runner/services/cloud-runner-custom-hooks.ts create mode 100644 src/model/cloud-runner/services/cloud-runner-custom-steps.ts create mode 100644 src/model/cloud-runner/services/cloud-runner-options-reader.ts delete mode 100644 src/model/cloud-runner/services/depdency-override-service.ts create mode 100644 src/model/cloud-runner/services/shared-workspace-locking.ts create mode 100644 src/model/cloud-runner/tests/cloud-runner-environment-serializer.test.ts rename src/model/cloud-runner/{remote-client/caching.test.ts => tests/cloud-runner-remote-client-caching.test.ts} (82%) create mode 100644 src/model/cloud-runner/tests/cloud-runner-run-once-custom-hooks.test.ts create mode 100644 src/model/cloud-runner/tests/cloud-runner-run-twice-caching.test.ts create mode 100644 src/model/cloud-runner/tests/cloud-runner-run-twice-retaining.test.ts create mode 100644 src/model/cloud-runner/tests/cloud-runner-s3-prebuilt-steps.test.ts create mode 100644 src/model/cloud-runner/tests/cloud-runner-suite.test.ts create mode 100644 src/model/cloud-runner/tests/cloud-runner-sync-environment.test.ts create mode 100644 src/model/cloud-runner/tests/shared-workspace-locking.test.ts create mode 100644 src/model/github.ts diff --git a/.github/workflows/build-tests.yml b/.github/workflows/build-tests.yml index 239f2b0f..1d40d1ab 100644 --- a/.github/workflows/build-tests.yml +++ b/.github/workflows/build-tests.yml @@ -7,7 +7,30 @@ on: - '.github/**' env: - UNITY_LICENSE: "\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \nm0Db8UK+ktnOLJBtHybkfetpcKo=o/pUbSQAukz7+ZYAWhnA0AJbIlyyCPL7bKVEM2lVqbrXt7cyey+umkCXamuOgsWPVUKBMkXtMH8L\n5etLmD0getWIhTGhzOnDCk+gtIPfL4jMo9tkEuOCROQAXCci23VFscKcrkB+3X6h4wEOtA2APhOY\nB+wvC794o8/82ffjP79aVAi57rp3Wmzx+9pe9yMwoJuljAy2sc2tIMgdQGWVmOGBpQm3JqsidyzI\nJWG2kjnc7pDXK9pwYzXoKiqUqqrut90d+kQqRyv7MSZXR50HFqD/LI69h68b7P8Bjo3bPXOhNXGR\n9YCoemH6EkfCJxp2gIjzjWW+l2Hj2EsFQi8YXw==" + UNITY_LICENSE: + "\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \nm0Db8UK+ktnOLJBtHybkfetpcKo=o/pUbSQAukz7+ZYAWhnA0AJbIlyyCPL7bKVEM2lVqbrXt7cyey+umkCXamuOgsWPVUKBMkXtMH8L\n5etLmD0getWIhTGhzOnDCk+gtIPfL4jMo9tkEuOCROQAXCci23VFscKcrkB+3X6h4wEOtA2APhOY\nB+wvC794o8/82ffjP79aVAi57rp3Wmzx+9pe9yMwoJuljAy2sc2tIMgdQGWVmOGBpQm3JqsidyzI\nJWG2kjnc7pDXK9pwYzXoKiqUqqrut90d+kQqRyv7MSZXR50HFqD/LI69h68b7P8Bjo3bPXOhNXGR\n9YCoemH6EkfCJxp2gIjzjWW+l2Hj2EsFQi8YXw==" jobs: buildForAllPlatformsUbuntu: @@ -16,6 +39,9 @@ jobs: strategy: fail-fast: false matrix: + cloudRunnerCluster: + # - local-docker + - local projectPath: - test-project unityVersion: @@ -62,6 +88,7 @@ jobs: unityVersion: ${{ matrix.unityVersion }} targetPlatform: ${{ matrix.targetPlatform }} customParameters: -profile SomeProfile -someBoolean -someValue exampleValue + cloudRunnerCluster: ${{ matrix.cloudRunnerCluster }} ########################### # Upload # diff --git a/.github/workflows/cleanup.yml b/.github/workflows/cleanup.yml index 10a5cfea..6568e057 100644 --- a/.github/workflows/cleanup.yml +++ b/.github/workflows/cleanup.yml @@ -29,7 +29,7 @@ jobs: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_DEFAULT_REGION: eu-west-2 - - run: yarn run cli -m aws-list-all + - run: yarn run cli -m list-resources env: AWS_REGION: eu-west-2 AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} diff --git a/.github/workflows/cloud-runner-local-pipeline.yml b/.github/workflows/cloud-runner-local-pipeline.yml new file mode 100644 index 00000000..84c4ac08 --- /dev/null +++ b/.github/workflows/cloud-runner-local-pipeline.yml @@ -0,0 +1,96 @@ +name: Cloud Runner Local + +on: + push: { branches: ['!cloud-runner-develop', '!cloud-runner-preview', '!main'] } +# push: { branches: [main] } +# pull_request: +# paths-ignore: +# - '.github/**' + +jobs: + integrationTests: + name: Integration Tests + if: github.event.event_type != 'pull_request_target' + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + cloudRunnerCluster: + - local-docker + targetPlatform: + - StandaloneWindows64 # Build a Windows 64-bit standalone. + # steps + steps: + - name: Checkout (default) + uses: actions/checkout@v2 + with: + lfs: true + - run: yarn + - run: yarn run cli --help + - run: yarn run test-i --detectOpenHandles --forceExit --runInBand + env: + UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }} + PROJECT_PATH: ${{ matrix.projectPath }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TARGET_PLATFORM: ${{ matrix.targetPlatform }} + cloudRunnerTests: true + versioning: None + CLOUD_RUNNER_CLUSTER: ${{ matrix.cloudRunnerCluster }} + buildTests: + name: Build Tests + if: github.event.event_type != 'pull_request_target' + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + cloudRunnerCluster: + - local-docker + targetPlatform: + - StandaloneOSX # Build a macOS standalone (Intel 64-bit). + - StandaloneWindows64 # Build a Windows 64-bit standalone. + - StandaloneLinux64 # Build a Linux 64-bit standalone. + - WebGL # WebGL. + - iOS # Build an iOS player. + - Android # Build an Android .apk. + # - StandaloneWindows # Build a Windows standalone. + # - WSAPlayer # Build an Windows Store Apps player. + # - PS4 # Build a PS4 Standalone. + # - XboxOne # Build a Xbox One Standalone. + # - tvOS # Build to Apple's tvOS platform. + # - Switch # Build a Nintendo Switch player + # steps + steps: + - name: Checkout (default) + uses: actions/checkout@v2 + with: + lfs: true + - uses: ./ + id: unity-build + timeout-minutes: 25 + env: + CLOUD_RUNNER_BRANCH: ${{ github.ref }} + CLOUD_RUNNER_DEBUG: true + CLOUD_RUNNER_DEBUG_TREE: true + DEBUG: true + PROJECT_PATH: test-project + UNITY_VERSION: 2019.3.15f1 + USE_IL2CPP: false + with: + cloudRunnerTests: true + versioning: None + projectPath: ${{ matrix.projectPath }} + gitPrivateToken: ${{ secrets.GITHUB_TOKEN }} + targetPlatform: ${{ matrix.targetPlatform }} + cloudRunnerCluster: ${{ matrix.cloudRunnerCluster }} + - run: | + mv ./cloud-runner-cache/${{ steps.unity-build.outputs.CACHE_KEY }}/build/build-${{ steps.unity-build.outputs.BUILD_GUID }}.tar.lz4 build-${{ steps.unity-build.outputs.BUILD_GUID }}.tar.lz4 + ls + - run: yarn run cli -m list-resources + ########################### + # Upload # + ########################### + - uses: actions/upload-artifact@v2 + with: + name: AWS Build (${{ matrix.targetPlatform }}) + path: build-${{ steps.unity-build.outputs.BUILD_GUID }}.tar.lz4 + retention-days: 14 diff --git a/.github/workflows/cloud-runner-pipeline.yml b/.github/workflows/cloud-runner-pipeline.yml index 1b7c2293..70a8a8f0 100644 --- a/.github/workflows/cloud-runner-pipeline.yml +++ b/.github/workflows/cloud-runner-pipeline.yml @@ -1,11 +1,7 @@ -name: Cloud Runner +name: Cloud Runner CI Pipeline on: - push: { branches: [cloud-runner-develop, main] } -# push: { branches: [main] } -# pull_request: -# paths-ignore: -# - '.github/**' + push: { branches: [cloud-runner-develop, cloud-runner-preview] } env: GKE_ZONE: 'us-central1' @@ -21,43 +17,38 @@ env: AWS_DEFAULT_REGION: eu-west-2 AWS_BASE_STACK_NAME: game-ci-github-pipelines CLOUD_RUNNER_BRANCH: ${{ github.ref }} - CLOUD_RUNNER_TESTS: true + CLOUD_RUNNER_DEBUG: true + CLOUD_RUNNER_DEBUG_TREE: true DEBUG: true UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }} + PROJECT_PATH: test-project + UNITY_VERSION: 2019.3.15f1 + USE_IL2CPP: false jobs: - awsBuild: - name: AWS Fargate Build - if: github.event.pull_request.draft == false + integrationTests: + name: Integration Tests + if: github.event.event_type != 'pull_request_target' runs-on: ubuntu-latest strategy: fail-fast: false matrix: - projectPath: - - test-project - unityVersion: - # - 2019.2.11f1 - - 2019.3.15f1 - targetPlatform: - #- StandaloneOSX # Build a macOS standalone (Intel 64-bit). - - StandaloneWindows64 # Build a Windows 64-bit standalone. - - StandaloneLinux64 # Build a Linux 64-bit standalone. - - WebGL # WebGL. - #- iOS # Build an iOS player. - #- Android # Build an Android .apk. - # - StandaloneWindows # Build a Windows standalone. - # - WSAPlayer # Build an Windows Store Apps player. - # - PS4 # Build a PS4 Standalone. - # - XboxOne # Build a Xbox One Standalone. - # - tvOS # Build to Apple's tvOS platform. - # - Switch # Build a Nintendo Switch player - # steps + cloudRunnerCluster: + - aws + - local-docker + - k8s steps: - name: Checkout (default) uses: actions/checkout@v2 - if: github.event.event_type != 'pull_request_target' with: lfs: true + - uses: google-github-actions/setup-gcloud@v0 + with: + version: '288.0.0' + service_account_email: ${{ secrets.GOOGLE_SERVICE_ACCOUNT_EMAIL }} + service_account_key: ${{ secrets.GOOGLE_SERVICE_ACCOUNT_KEY }} + - name: Get GKE cluster credentials + run: gcloud container clusters get-credentials $GKE_CLUSTER --zone $GKE_ZONE --project $GKE_PROJECT - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v1 with: @@ -66,81 +57,117 @@ jobs: aws-region: eu-west-2 - run: yarn - run: yarn run cli --help - - run: yarn run test "caching" - - run: yarn run test-i-aws + - run: yarn run test "cloud-runner-run-twice-retaining" --detectOpenHandles --forceExit --runInBand + if: matrix.CloudRunnerCluster == 'aws' || matrix.CloudRunnerCluster == 'k8s' + timeout-minutes: 180 env: UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }} - PROJECT_PATH: ${{ matrix.projectPath }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - TARGET_PLATFORM: ${{ matrix.targetPlatform }} + PROJECT_PATH: test-project + GIT_PRIVATE_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TARGET_PLATFORM: StandaloneWindows64 cloudRunnerTests: true versioning: None - - uses: ./ - id: aws-fargate-unity-build - timeout-minutes: 25 - with: - cloudRunnerCluster: aws + CLOUD_RUNNER_CLUSTER: ${{ matrix.cloudRunnerCluster }} + - run: yarn run test-i --detectOpenHandles --forceExit --runInBand + if: matrix.CloudRunnerCluster == 'local-docker' + timeout-minutes: 180 + env: + UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }} + PROJECT_PATH: test-project + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TARGET_PLATFORM: StandaloneWindows64 + cloudRunnerTests: true versioning: None - projectPath: ${{ matrix.projectPath }} - unityVersion: ${{ matrix.unityVersion }} - targetPlatform: ${{ matrix.targetPlatform }} - githubToken: ${{ secrets.GITHUB_TOKEN }} - postBuildSteps: | - - name: upload - image: amazon/aws-cli - commands: | - aws configure set aws_access_key_id $AWS_ACCESS_KEY_ID --profile default - aws configure set aws_secret_access_key $AWS_SECRET_ACCESS_KEY --profile default - aws configure set region $AWS_DEFAULT_REGION --profile default - aws s3 ls - aws s3 ls game-ci-test-storage - ls /data/cache/$CACHE_KEY - ls /data/cache/$CACHE_KEY/build - aws s3 cp /data/cache/$CACHE_KEY/build/build-$BUILD_GUID.tar s3://game-ci-test-storage/$CACHE_KEY/build-$BUILD_GUID.tar - secrets: - - name: awsAccessKeyId - value: ${{ secrets.AWS_ACCESS_KEY_ID }} - - name: awsSecretAccessKey - value: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - - name: awsDefaultRegion - value: eu-west-2 - - run: | - aws s3 cp s3://game-ci-test-storage/${{ steps.aws-fargate-unity-build.outputs.CACHE_KEY }}/build-${{ steps.aws-fargate-unity-build.outputs.BUILD_GUID }}.tar build-${{ steps.aws-fargate-unity-build.outputs.BUILD_GUID }}.tar - ls - - run: yarn run cli -m aws-garbage-collect - ########################### - # Upload # - ########################### - # download from cloud storage - - uses: actions/upload-artifact@v2 - with: - name: AWS Build (${{ matrix.targetPlatform }}) - path: build-${{ steps.aws-fargate-unity-build.outputs.BUILD_GUID }}.tar - retention-days: 14 - k8sBuilds: - name: K8s (GKE Autopilot) build for ${{ matrix.targetPlatform }} on version ${{ matrix.unityVersion }} + CLOUD_RUNNER_CLUSTER: ${{ matrix.cloudRunnerCluster }} + + buildTargetTests: + name: Build Tests - Targets + if: github.event.event_type != 'pull_request_target' runs-on: ubuntu-latest strategy: fail-fast: false matrix: - unityVersion: - # - 2019.2.11f1 - - 2019.3.15f1 + cloudRunnerCluster: + #- aws + - local-docker + #- k8s targetPlatform: - # - StandaloneWindows64 - - StandaloneLinux64 + - StandaloneOSX # Build a macOS standalone (Intel 64-bit). + # - StandaloneWindows64 # Build a Windows 64-bit standalone. + - StandaloneLinux64 # Build a Linux 64-bit standalone. + - WebGL # WebGL. + - iOS # Build an iOS player. + - Android # Build an Android .apk. steps: - ########################### - # Checkout # - ########################### - - uses: actions/checkout@v2 - if: github.event.event_type != 'pull_request_target' + - name: Checkout (default) + uses: actions/checkout@v2 + with: + lfs: true + + - uses: google-github-actions/setup-gcloud@v0 + with: + version: '288.0.0' + service_account_email: ${{ secrets.GOOGLE_SERVICE_ACCOUNT_EMAIL }} + service_account_key: ${{ secrets.GOOGLE_SERVICE_ACCOUNT_KEY }} + - name: Get GKE cluster credentials + run: gcloud container clusters get-credentials $GKE_CLUSTER --zone $GKE_ZONE --project $GKE_PROJECT + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: eu-west-2 + - run: yarn + - uses: ./ + id: unity-build + timeout-minutes: 90 + env: + UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }} + with: + cloudRunnerTests: true + versioning: None + projectPath: test-project + gitPrivateToken: ${{ secrets.GITHUB_TOKEN }} + targetPlatform: ${{ matrix.targetPlatform }} + cloudRunnerCluster: ${{ matrix.cloudRunnerCluster }} + customStepFiles: aws-s3-upload-build,aws-s3-pull-cache,aws-s3-upload-cache + - run: | + aws s3 cp s3://game-ci-test-storage/cloud-runner-cache/${{ steps.unity-build.outputs.CACHE_KEY }}/build/build-${{ steps.unity-build.outputs.BUILD_GUID }}.tar.lz4 build-${{ steps.unity-build.outputs.BUILD_GUID }}.tar.lz4 + ls + - run: yarn run cli -m list-resources + env: + cloudRunnerTests: true + CLOUD_RUNNER_CLUSTER: ${{ matrix.cloudRunnerCluster }} + - uses: actions/upload-artifact@v2 + with: + name: ${{ matrix.cloudRunnerCluster }} Build (${{ matrix.targetPlatform }}) + path: build-${{ steps.unity-build.outputs.BUILD_GUID }}.tar.lz4 + retention-days: 14 + buildTests: + name: Build Tests - Providers + if: github.event.event_type != 'pull_request_target' + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + cloudRunnerCluster: + - aws + - local-docker + - k8s + targetPlatform: + #- StandaloneOSX # Build a macOS standalone (Intel 64-bit). + - StandaloneWindows64 # Build a Windows 64-bit standalone. + #- StandaloneLinux64 # Build a Linux 64-bit standalone. + #- WebGL # WebGL. + #- iOS # Build an iOS player. + #- Android # Build an Android .apk. + # steps + steps: + - name: Checkout (default) + uses: actions/checkout@v2 with: lfs: true - ########################### - # Setup # - ########################### - uses: google-github-actions/setup-gcloud@v0 with: version: '288.0.0' @@ -149,70 +176,36 @@ jobs: - name: Get GKE cluster credentials run: gcloud container clusters get-credentials $GKE_CLUSTER --zone $GKE_ZONE --project $GKE_PROJECT - ########################### - # Cloud Runner Test Suite # - ########################### - - uses: actions/setup-node@v2 + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v1 with: - node-version: 12.x + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: eu-west-2 - run: yarn - - run: yarn run cli --help - - run: yarn run test "caching" - - name: Cloud Runner Test Suite - run: yarn run test-i-k8s --detectOpenHandles --forceExit + - uses: ./ + id: unity-build + timeout-minutes: 90 env: UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }} - PROJECT_PATH: ${{ matrix.projectPath }} - TARGET_PLATFORM: ${{ matrix.targetPlatform }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - KUBE_CONFIG: ${{ steps.read-base64.outputs.base64 }} - unityVersion: ${{ matrix.unityVersion }} + with: cloudRunnerTests: true versioning: None - - ########################### - # Cloud Runner Build Test # - ########################### - - name: Cloud Runner Build Test - uses: ./ - id: k8s-unity-build - timeout-minutes: 30 - with: - cloudRunnerCluster: k8s - UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }} - targetPlatform: ${{ matrix.targetPlatform }} - kubeConfig: ${{ steps.read-base64.outputs.base64 }} - githubToken: ${{ secrets.GITHUB_TOKEN }} projectPath: test-project - unityVersion: ${{ matrix.unityVersion }} - versioning: None - postBuildSteps: | - - name: upload - image: amazon/aws-cli - commands: | - aws configure set aws_access_key_id $AWS_ACCESS_KEY_ID --profile default - aws configure set aws_secret_access_key $AWS_SECRET_ACCESS_KEY --profile default - aws configure set region $AWS_DEFAULT_REGION --profile default - aws s3 ls - aws s3 ls game-ci-test-storage - ls /data/cache/$CACHE_KEY - aws s3 cp /data/cache/$CACHE_KEY/build/build-$BUILD_GUID.tar s3://game-ci-test-storage/$CACHE_KEY/build-$BUILD_GUID.tar - secrets: - - name: awsAccessKeyId - value: ${{ secrets.AWS_ACCESS_KEY_ID }} - - name: awsSecretAccessKey - value: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - - name: awsDefaultRegion - value: eu-west-2 + gitPrivateToken: ${{ secrets.GITHUB_TOKEN }} + targetPlatform: ${{ matrix.targetPlatform }} + cloudRunnerCluster: ${{ matrix.cloudRunnerCluster }} + customStepFiles: aws-s3-upload-build,aws-s3-pull-cache,aws-s3-upload-cache - run: | - aws s3 cp s3://game-ci-test-storage/${{ steps.k8s-unity-build.outputs.CACHE_KEY }}/build-${{ steps.k8s-unity-build.outputs.BUILD_GUID }}.tar build-${{ steps.k8s-unity-build.outputs.BUILD_GUID }}.tar + aws s3 cp s3://game-ci-test-storage/cloud-runner-cache/${{ steps.unity-build.outputs.CACHE_KEY }}/build/build-${{ steps.unity-build.outputs.BUILD_GUID }}.tar.lz4 build-${{ steps.unity-build.outputs.BUILD_GUID }}.tar.lz4 ls - ########################### - # Upload # - ########################### - # download from cloud storage + - run: yarn run cli -m list-resources + if: always() + env: + cloudRunnerTests: true + CLOUD_RUNNER_CLUSTER: ${{ matrix.cloudRunnerCluster }} - uses: actions/upload-artifact@v2 with: - name: K8s Build (${{ matrix.targetPlatform }}) - path: build-${{ steps.k8s-unity-build.outputs.BUILD_GUID }}.tar + name: ${{ matrix.cloudRunnerCluster }} Build (${{ matrix.targetPlatform }}) + path: build-${{ steps.unity-build.outputs.BUILD_GUID }}.tar.lz4 retention-days: 14 diff --git a/.vscode/settings.json b/.vscode/settings.json index e22e08b0..5a33b7a0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,5 @@ { + "files.eol": "\n", "god.tsconfig": "./tsconfig.json", "yaml.customTags": [ "!And", diff --git a/action.yml b/action.yml index 6658a9a8..64effd18 100644 --- a/action.yml +++ b/action.yml @@ -9,7 +9,8 @@ inputs: unityVersion: required: false default: 'auto' - description: 'Version of unity to use for building the project. Use "auto" to get from your ProjectSettings/ProjectVersion.txt' + description: + 'Version of unity to use for building the project. Use "auto" to get from your ProjectSettings/ProjectVersion.txt' customImage: required: false default: '' @@ -81,91 +82,107 @@ inputs: gitPrivateToken: required: false default: '' - description: 'Github private token to pull from github' + description: '[CloudRunner] Github private token to pull from github' chownFilesTo: required: false default: '' - description: 'User and optionally group (user or user:group or uid:gid) to give ownership of the resulting build artifacts' + description: + 'User and optionally group (user or user:group or uid:gid) to give ownership of the resulting build artifacts' allowDirtyBuild: required: false default: '' - description: 'Allows the branch of the build to be dirty, and still generate the build.' + description: '[CloudRunner] Allows the branch of the build to be dirty, and still generate the build.' postBuildSteps: required: false default: '' - description: 'run a post build job in yaml format with the keys image, secrets (name, value object array), command string' + description: + '[CloudRunner] run a post build job in yaml format with the keys image, secrets (name, value object array), + command string' preBuildSteps: required: false default: '' - description: 'Run a pre build job after the repository setup but before the build job (in yaml format with the keys image, secrets (name, value object array), command line string)' + description: + '[CloudRunner] Run a pre build job after the repository setup but before the build job (in yaml format with the + keys image, secrets (name, value object array), command line string)' + customStepFiles: + required: false + default: '' + description: + '[CloudRunner] Specify the names (by file name) of custom steps to run before or after cloud runner jobs, must + match a yaml step file inside your repo in the folder .game-ci/steps/' + customHookFiles: + required: false + default: '' + description: + '[CloudRunner] Specify the names (by file name) of custom hooks to run before or after cloud runner jobs, must + match a yaml step file inside your repo in the folder .game-ci/hooks/' customJobHooks: required: false default: '' - description: 'Specify custom commands and trigger hooks (injects commands into jobs)' + description: '[CloudRunner] Specify custom commands and trigger hooks (injects commands into jobs)' customJob: required: false default: '' - description: 'Run a custom job instead of the standard build automation for cloud runner (in yaml format with the keys image, secrets (name, value object array), command line string)' + description: + '[CloudRunner] Run a custom job instead of the standard build automation for cloud runner (in yaml format with the + keys image, secrets (name, value object array), command line string)' awsBaseStackName: default: 'game-ci' required: false - description: 'The Cloud Formation stack name that must be setup before using this option.' + description: '[CloudRunner] The Cloud Formation stack name that must be setup before using this option.' cloudRunnerCluster: default: 'local' required: false - description: 'Either local, k8s or aws can be used to run builds on a remote cluster. Additional parameters must be configured.' + description: + '[CloudRunner] Either local, k8s or aws can be used to run builds on a remote cluster. Additional parameters must + be configured.' cloudRunnerCpu: default: '' required: false - description: 'Amount of CPU time to assign the remote build container' + description: '[CloudRunner] Amount of CPU time to assign the remote build container' cloudRunnerMemory: default: '' required: false - description: 'Amount of memory to assign the remote build container' - cachePushOverrideCommand: - default: '' - required: false - description: 'A command run every time a file is pushed to cache, formatted with input file path and remote cache path' - cachePullOverrideCommand: - default: '' - required: false - description: 'A command run every time before a file is being pulled from cache, formatted with request cache file and destination path' + description: '[CloudRunner] Amount of memory to assign the remote build container' readInputFromOverrideList: default: '' required: false - description: 'Comma separated list of input value names to read from "input override command"' + description: '[CloudRunner] Comma separated list of input value names to read from "input override command"' readInputOverrideCommand: default: '' required: false - description: 'Extend game ci by specifying a command to execute to pull input from external source e.g cloud provider secret managers' + description: + '[CloudRunner] Extend game ci by specifying a command to execute to pull input from external source e.g cloud + provider secret managers' kubeConfig: default: '' required: false - description: 'Supply a base64 encoded kubernetes config to run builds on kubernetes and stream logs until completion.' + description: + '[CloudRunner] Supply a base64 encoded kubernetes config to run builds on kubernetes and stream logs until + completion.' kubeVolume: default: '' required: false - description: 'Supply a Persistent Volume Claim name to use for the Unity build.' + description: '[CloudRunner] Supply a Persistent Volume Claim name to use for the Unity build.' kubeStorageClass: default: '' required: false - description: 'Kubernetes storage class to use for cloud runner jobs, leave empty to install rook cluster.' + description: + '[CloudRunner] Kubernetes storage class to use for cloud runner jobs, leave empty to install rook cluster.' kubeVolumeSize: default: '5Gi' required: false - description: 'Amount of disc space to assign the Kubernetes Persistent Volume' + description: '[CloudRunner] Amount of disc space to assign the Kubernetes Persistent Volume' cacheKey: default: '' required: false - description: 'Cache key to indicate bucket for cache' - checkDependencyHealthOverride: - default: '' + description: '[CloudRunner] Cache key to indicate bucket for cache' + watchToEnd: + default: 'true' required: false - description: 'Use to specify a way to check depdency services health to enable resilient self-starting jobs' - startDependenciesOverride: - default: '' - required: false - description: 'Use to specify a way to start depdency services health to enable resilient self-starting jobs' + description: + '[CloudRunner] Whether or not to watch the build to the end. Can be used for especially long running jobs e.g + imports or self-hosted ephemeral runners.' outputs: volume: description: 'The Persistent Volume (PV) where the build artifacts have been stored by Kubernetes' diff --git a/dist/index.js b/dist/index.js index 1ccffaab8a965a0fc393dcb25c17611df5a0864d..13bdcbc8018a4116876a623dc306a3a6d63033db 100644 GIT binary patch delta 46785 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 delta 22202 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 { const buildFile = this.parseBuildFile(Input.buildName, Input.targetPlatform, Input.androidAppBundle); @@ -78,13 +82,13 @@ class BuildParameters { // --- let unitySerial = ''; if (Input.unityLicensingServer === '') { - if (!process.env.UNITY_SERIAL && Input.githubInputEnabled) { + if (!process.env.UNITY_SERIAL && GitHub.githubInputEnabled) { // No serial was present, so it is a personal license that we need to convert if (!process.env.UNITY_LICENSE) { throw new Error(`Missing Unity License File and no Serial was found. If this - is a personal license, make sure to follow the activation - steps and set the UNITY_LICENSE GitHub secret or enter a Unity - serial number inside the UNITY_SERIAL GitHub secret.`); + is a personal license, make sure to follow the activation + steps and set the UNITY_LICENSE GitHub secret or enter a Unity + serial number inside the UNITY_SERIAL GitHub secret.`); } unitySerial = this.getSerialFromLicenseFile(process.env.UNITY_LICENSE); } else { @@ -117,36 +121,38 @@ class BuildParameters { sshAgent: Input.sshAgent, gitPrivateToken: Input.gitPrivateToken || (await GithubCliReader.GetGitHubAuthToken()), chownFilesTo: Input.chownFilesTo, - cloudRunnerCluster: Input.cloudRunnerCluster, - cloudRunnerBuilderPlatform: Input.cloudRunnerBuilderPlatform, - awsBaseStackName: Input.awsBaseStackName, - kubeConfig: Input.kubeConfig, - cloudRunnerMemory: Input.cloudRunnerMemory, - cloudRunnerCpu: Input.cloudRunnerCpu, - kubeVolumeSize: Input.kubeVolumeSize, - kubeVolume: Input.kubeVolume, - postBuildSteps: Input.postBuildSteps, - preBuildSteps: Input.preBuildSteps, - customJob: Input.customJob, + cloudRunnerCluster: CloudRunnerOptions.cloudRunnerCluster, + cloudRunnerBuilderPlatform: CloudRunnerOptions.cloudRunnerBuilderPlatform, + awsBaseStackName: CloudRunnerOptions.awsBaseStackName, + kubeConfig: CloudRunnerOptions.kubeConfig, + cloudRunnerMemory: CloudRunnerOptions.cloudRunnerMemory, + cloudRunnerCpu: CloudRunnerOptions.cloudRunnerCpu, + kubeVolumeSize: CloudRunnerOptions.kubeVolumeSize, + kubeVolume: CloudRunnerOptions.kubeVolume, + postBuildSteps: CloudRunnerOptions.postBuildSteps, + preBuildSteps: CloudRunnerOptions.preBuildSteps, + customJob: CloudRunnerOptions.customJob, runNumber: Input.runNumber, branch: Input.branch.replace('/head', '') || (await GitRepoReader.GetBranch()), - cloudRunnerBranch: Input.cloudRunnerBranch.split('/').reverse()[0], - cloudRunnerIntegrationTests: Input.cloudRunnerTests, + cloudRunnerBranch: CloudRunnerOptions.cloudRunnerBranch.split('/').reverse()[0], + cloudRunnerDebug: CloudRunnerOptions.cloudRunnerDebug, githubRepo: Input.githubRepo || (await GitRepoReader.GetRemote()) || 'game-ci/unity-builder', isCliMode: Cli.isCliMode, - awsStackName: Input.awsBaseStackName, + awsStackName: CloudRunnerOptions.awsBaseStackName, gitSha: Input.gitSha, logId: customAlphabet(CloudRunnerConstants.alphabet, 9)(), buildGuid: CloudRunnerBuildGuid.generateGuid(Input.runNumber, Input.targetPlatform), - customJobHooks: Input.customJobHooks(), - cachePullOverrideCommand: Input.cachePullOverrideCommand(), - cachePushOverrideCommand: Input.cachePushOverrideCommand(), - readInputOverrideCommand: Input.readInputOverrideCommand(), - readInputFromOverrideList: Input.readInputFromOverrideList(), - kubeStorageClass: Input.kubeStorageClass, - checkDependencyHealthOverride: Input.checkDependencyHealthOverride, - startDependenciesOverride: Input.startDependenciesOverride, - cacheKey: Input.cacheKey, + customJobHooks: CloudRunnerOptions.customJobHooks(), + readInputOverrideCommand: CloudRunnerOptions.readInputOverrideCommand(), + readInputFromOverrideList: CloudRunnerOptions.readInputFromOverrideList(), + kubeStorageClass: CloudRunnerOptions.kubeStorageClass, + cacheKey: CloudRunnerOptions.cacheKey, + retainWorkspace: CloudRunnerOptions.retainWorkspaces, + useSharedLargePackages: CloudRunnerOptions.useSharedLargePackages, + useLz4Compression: CloudRunnerOptions.useLz4Compression, + maxRetainedWorkspaces: CloudRunnerOptions.maxRetainedWorkspaces, + constantGarbageCollection: CloudRunnerOptions.constantGarbageCollection, + garbageCollectionMaxAge: CloudRunnerOptions.garbageCollectionMaxAge, }; } diff --git a/src/model/cli/cli.ts b/src/model/cli/cli.ts index de7854e7..392ed28f 100644 --- a/src/model/cli/cli.ts +++ b/src/model/cli/cli.ts @@ -5,10 +5,14 @@ import { ActionYamlReader } from '../input-readers/action-yaml'; import CloudRunnerLogger from '../cloud-runner/services/cloud-runner-logger'; import CloudRunnerQueryOverride from '../cloud-runner/services/cloud-runner-query-override'; import { CliFunction, CliFunctionsRepository } from './cli-functions-repository'; -import { AwsCliCommands } from '../cloud-runner/providers/aws/commands/aws-cli-commands'; import { Caching } from '../cloud-runner/remote-client/caching'; import { LfsHashing } from '../cloud-runner/services/lfs-hashing'; import { RemoteClient } from '../cloud-runner/remote-client'; +import CloudRunnerOptionsReader from '../cloud-runner/services/cloud-runner-options-reader'; +import GitHub from '../github'; +import { TaskParameterSerializer } from '../cloud-runner/services/task-parameter-serializer'; +import { CloudRunnerFolders } from '../cloud-runner/services/cloud-runner-folders'; +import { CloudRunnerSystem } from '../cloud-runner/services/cloud-runner-system'; export class Cli { public static options; @@ -27,13 +31,13 @@ export class Cli { } public static InitCliMode() { - CliFunctionsRepository.PushCliFunctionSource(AwsCliCommands); + CliFunctionsRepository.PushCliFunctionSource(RemoteClient); CliFunctionsRepository.PushCliFunctionSource(Caching); CliFunctionsRepository.PushCliFunctionSource(LfsHashing); - CliFunctionsRepository.PushCliFunctionSource(RemoteClient); const program = new Command(); program.version('0.0.1'); - const properties = Object.getOwnPropertyNames(Input); + + const properties = CloudRunnerOptionsReader.GetProperties(); const actionYamlReader: ActionYamlReader = new ActionYamlReader(); for (const element of properties) { program.option(`--${element} <${element}>`, actionYamlReader.GetActionYamlValue(element)); @@ -48,6 +52,7 @@ export class Cli { program.option('--cachePushFrom ', 'cache push from source folder'); program.option('--cachePushTo ', 'cache push to caching folder'); program.option('--artifactName ', 'caching artifact name'); + program.option('--select