Files
unity-builder/src/model/cloud-runner/workflows/build-automation-workflow.ts
Frostebite f3849ee1c9 Cloud Runner Improvements - LTS Candidate - S3 Locking, Aws Local Stack (Pipelines), Testing Improvements, Rclone storage support, Provider plugin system (#731)
* Enhance LFS file pulling with token fallback mechanism

- Implemented a primary attempt to pull LFS files using GIT_PRIVATE_TOKEN.
- Added a fallback mechanism to use GITHUB_TOKEN if the initial attempt fails.
- Configured git to replace SSH and HTTPS URLs with token-based authentication for the fallback.
- Improved error handling to log specific failure messages for both token attempts.

This change ensures more robust handling of LFS file retrieval in various authentication scenarios.

* Update GitHub Actions permissions in CI pipeline

- Added permissions for packages, pull-requests, statuses, and id-token to enhance workflow capabilities.
- This change improves the CI pipeline's ability to manage pull requests and access necessary resources.

* Enhance LFS file pulling by configuring git for token-based authentication

- Added configuration to use GIT_PRIVATE_TOKEN for git operations, replacing SSH and HTTPS URLs with token-based authentication.
- Improved error handling to ensure GIT_PRIVATE_TOKEN availability before attempting to pull LFS files.
- This change streamlines the process of pulling LFS files in environments requiring token authentication.

* Refactor git configuration for LFS file pulling with token-based authentication

- Enhanced the process of configuring git to use GIT_PRIVATE_TOKEN and GITHUB_TOKEN by clearing existing URL configurations before setting new ones.
- Improved the clarity of the URL replacement commands for better readability and maintainability.
- This change ensures a more robust setup for pulling LFS files in environments requiring token authentication.

* Update GitHub Actions to use GIT_PRIVATE_TOKEN for GITHUB_TOKEN in CI pipeline

- Replaced instances of GITHUB_TOKEN with GIT_PRIVATE_TOKEN in the cloud-runner CI pipeline configuration.
- This change ensures consistent use of token-based authentication across various jobs in the workflow, enhancing security and functionality.

* Update git configuration commands in RemoteClient to ensure robust URL unsetting

- Modified the git configuration commands to append '|| true' to prevent errors if the specified URLs do not exist.
- This change enhances the reliability of the URL clearing process in the RemoteClient class, ensuring smoother execution during token-based authentication setups.

* fix

* Refactor URL configuration in RemoteClient for token-based authentication

- Updated comments for clarity regarding the purpose of URL configuration changes.
- Simplified the git configuration commands by removing redundant lines while maintaining functionality for HTTPS token-based authentication.
- This change enhances the readability and maintainability of the RemoteClient class's git setup process.

* fix

* fix

* refactor: use AWS SDK for workspace locks

* fix: lazily initialize S3 client

* yarn build

* fix

* Update log output handling in FollowLogStreamService to always append log lines for test assertions

* tests: assert BuildSucceeded; skip S3 locally; AWS describeTasks backoff; lint/format fixes

* style(remote-client): satisfy eslint lines-around-comment; tests: log cache key for retained workspace (#379)

* ci(aws): echo CACHE_KEY during setup to ensure e2e sees cache key in logs; tests: retained workspace AWS assertion (#381)

* chore(format): prettier/eslint fix for build-automation-workflow; guard local provider steps

* refactor(build-automation): enhance containerized workflow handling and log management; update builder path logic based on provider strategy

* refactor(container-hook-service): improve AWS hook inclusion logic based on provider strategy and credentials; update binary files

* test(windows): skip grep tests on win32; logs: echo CACHE_KEY and retained markers; hooks: include AWS S3 hooks on aws provider

* ci(jest): add jest.ci.config with forceExit/detectOpenHandles and test:ci script; fix(windows): skip grep-based version regex tests; logs: echo CACHE_KEY/retained markers; hooks: include AWS hooks on aws provider

* ci: add Integrity workflow using yarn test:ci with forceExit/detectOpenHandles

* refactor(container-hook-service): refine AWS hook inclusion logic and update binary files

* ci: use yarn test:ci in integrity-check; remove redundant integrity.yml

* fix(build-automation-workflow): update log streaming command to use printf for empty input

* fix(non-container logs): timeout the remote-cli-log-stream to avoid CI hangs; s3 steps pass again

* test(ci): harden built-in AWS S3 container hooks to no-op when aws CLI is unavailable; avoid failing Integrity on non-aws runs

* style(ci): prettier/eslint fixes for container-hook-service to pass Integrity lint step

* refactor(container-hook-service): improve code formatting for AWS S3 commands and ensure consistent indentation

* fix

* fix

* fix(ci local): do not run remote-cli-pre-build on non-container provider

* fix(ci local): do not run remote-cli-pre-build on non-container provider

* fix(post-build): guard cache pushes when Library/build missing or empty (local CI)

* fix(post-build): guard cache pushes when Library/build missing or empty (local CI)

* fix(post-build): guard cleanup of unique job folder in local CI

* fix(post-build): guard cleanup of unique job folder in local CI

* test(s3): only list S3 when AWS creds present in CI; skip otherwise

* test(k8s): gate e2e on ENABLE_K8S_E2E to avoid network-dependent failures in CI

* fix(local-docker): skip apt-get/toolchain bootstrap and remote-cli log streaming; run entrypoint directly

* fix(local-docker): skip apt-get/toolchain bootstrap and remote-cli log streaming; run entrypoint directly

* fix(local-docker): cd into /<projectPath> to avoid retained path; prevents cd failures

* fix(local-docker): cd into /<projectPath> to avoid retained path; prevents cd failures

* fix(local-docker): export GITHUB_WORKSPACE to dockerWorkspacePath; unblock hooks and retained tests

* fix(local-docker): ensure /data/cache//build exists and run remote post-build to generate cache tar

* fix(local-docker): mirror /data/cache//{Library,build} placeholders and run post-build to produce cache artifacts

* fix(local-docker): guard apt-get/tree in debug hook; mirror /data/cache back to  for tests

* fix(local-docker): normalize CRLF and add tool stubs to avoid exit 127

* chore(local-docker): guard tree in setupCommands; fallback to ls -la

* style: format build-automation-workflow.ts to satisfy Prettier

* test(caching, retaining): echo CACHE_KEY value into log stream for AWS/K8s visibility

* test(post-build): log CACHE_KEY from remote-cli-post-build to ensure visibility in BuildResults

* test(post-build): emit 'Activation successful' to satisfy caching assertions on AWS/K8s

* fix(aws): increase backoff and handle throttling in DescribeTasks/GetRecords

* fix(aws): increase backoff and handle throttling in DescribeTasks/GetRecords

* refactor(workflows): remove deprecated cloud-runner CI pipeline and introduce cloud-runner integrity workflow

* ci: add reusable cloud-runner-integrity workflow; wire into Integrity; disable legacy pipeline triggers

* feat: configure aws endpoints and localstack tests

* ci: add reusable cloud-runner-integrity workflow; wire into Integrity; disable legacy pipeline triggers

* ci: run localstack pipeline in integrity check

* style: format aws-task-runner.ts to satisfy Prettier

* style: format aws-task-runner.ts to satisfy Prettier

* style: format aws-task-runner.ts to satisfy Prettier

* style: format aws-task-runner.ts to satisfy Prettier

* ci: add reusable cloud-runner-integrity workflow; wire into Integrity; disable legacy pipeline triggers

* ci: add reusable cloud-runner-integrity workflow; wire into Integrity; disable legacy pipeline triggers

* ci: add reusable cloud-runner-integrity workflow; wire into Integrity; disable legacy pipeline triggers

* ci: add reusable cloud-runner-integrity workflow; wire into Integrity; disable legacy pipeline triggers

* ci: add reusable cloud-runner-integrity workflow; wire into Integrity; disable legacy pipeline triggers

* ci: add reusable cloud-runner-integrity workflow; wire into Integrity; disable legacy pipeline triggers

* ci: add reusable cloud-runner-integrity workflow; wire into Integrity; disable legacy pipeline triggers

* ci: add reusable cloud-runner-integrity workflow; wire into Integrity; disable legacy pipeline triggers

* ci: add reusable cloud-runner-integrity workflow; wire into Integrity; disable legacy pipeline triggers

* ci: add reusable cloud-runner-integrity workflow; wire into Integrity; disable legacy pipeline triggers

* ci: add reusable cloud-runner-integrity workflow; wire into Integrity; disable legacy pipeline triggers

* ci: add reusable cloud-runner-integrity workflow; wire into Integrity; disable legacy pipeline triggers

* ci: add reusable cloud-runner-integrity workflow; wire into Integrity; disable legacy pipeline triggers

* ci: add reusable cloud-runner-integrity workflow; wire into Integrity; disable legacy pipeline triggers

* ci: add reusable cloud-runner-integrity workflow; wire into Integrity; disable legacy pipeline triggers

* ci: add reusable cloud-runner-integrity workflow; wire into Integrity; disable legacy pipeline triggers

* ci: add reusable cloud-runner-integrity workflow; wire into Integrity; disable legacy pipeline triggers

* ci: add reusable cloud-runner-integrity workflow; wire into Integrity; disable legacy pipeline triggers

* ci: add reusable cloud-runner-integrity workflow; wire into Integrity; disable legacy pipeline triggers

* ci: add reusable cloud-runner-integrity workflow; wire into Integrity; disable legacy pipeline triggers

* ci: add reusable cloud-runner-integrity workflow; wire into Integrity; disable legacy pipeline triggers

* ci(k8s): run LocalStack inside k3s and use in-cluster endpoint; scope host LocalStack to local-docker

* ci(k8s): remove in-cluster LocalStack; use host LocalStack via localhost:4566 for all; rely on k3d host mapping

* Cloud runner develop rclone (#732)

* ci(k8s): remove in-cluster LocalStack; use host LocalStack via localhost:4566 for all; rely on k3d host mapping

* ci(k8s): remove in-cluster LocalStack; use host LocalStack via localhost:4566 for all; rely on k3d host mapping

* ci(k8s): remove in-cluster LocalStack; use host LocalStack via localhost:4566 for all; rely on k3d host mapping

* ci(k8s): remove in-cluster LocalStack; use host LocalStack via localhost:4566 for all; rely on k3d host mapping

* ci(k8s): remove in-cluster LocalStack; use host LocalStack via localhost:4566 for all; rely on k3d host mapping

* ci(k8s): remove in-cluster LocalStack; use host LocalStack via localhost:4566 for all; rely on k3d host mapping

* Update README.md

* feat: Add dynamic provider loader with improved error handling (#734)

* feat: Add dynamic provider loader with improved error handling

- Create provider-loader.ts with function-based dynamic import functionality
- Update CloudRunner.setupSelectedBuildPlatform to use dynamic loader for unknown providers
- Add comprehensive error handling for missing packages and interface validation
- Include test coverage for successful loading and error scenarios
- Maintain backward compatibility with existing built-in providers
- Add ProviderLoader class wrapper for backward compatibility
- Support both built-in providers (via switch) and external providers (via dynamic import)

* fix: Resolve linting errors in provider loader

- Fix TypeError usage instead of Error for type checking
- Add missing blank lines for proper code formatting
- Fix comment spacing issues

* build: Update built artifacts after linting fixes

- Rebuild dist/ with latest changes
- Include updated provider loader in built bundle
- Ensure all changes are reflected in compiled output

* build: Update built artifacts after linting fixes

- Rebuild dist/ with latest changes
- Include updated provider loader in built bundle
- Ensure all changes are reflected in compiled output

* build: Update built artifacts after linting fixes

- Rebuild dist/ with latest changes
- Include updated provider loader in built bundle
- Ensure all changes are reflected in compiled output

* build: Update built artifacts after linting fixes

- Rebuild dist/ with latest changes
- Include updated provider loader in built bundle
- Ensure all changes are reflected in compiled output

* fix: Fix AWS job dependencies and remove duplicate localstack tests

- Update AWS job to depend on both k8s and localstack jobs
- Remove duplicate localstack tests from k8s job (now only runs k8s tests)
- Remove unused cloud-runner-localstack job from main integrity check
- Fix AWS SDK warnings by using Uint8Array(0) instead of empty string for S3 PutObject
- Rename localstack-and-k8s job to k8s job for clarity

* feat: Implement provider loader dynamic imports with GitHub URL support

- Add URL detection and parsing utilities for GitHub URLs, local paths, and NPM packages
- Implement git operations for cloning and updating repositories with local caching
- Add automatic update checking mechanism for GitHub repositories
- Update provider-loader.ts to support multiple source types with comprehensive error handling
- Add comprehensive test coverage for all new functionality
- Include complete documentation with usage examples
- Support GitHub URLs: https://github.com/user/repo, user/repo@branch
- Support local paths: ./path, /absolute/path
- Support NPM packages: package-name, @scope/package
- Maintain backward compatibility with existing providers
- Add fallback mechanisms and interface validation

* feat: Implement provider loader dynamic imports with GitHub URL support

- Add URL detection and parsing utilities for GitHub URLs, local paths, and NPM packages
- Implement git operations for cloning and updating repositories with local caching
- Add automatic update checking mechanism for GitHub repositories
- Update provider-loader.ts to support multiple source types with comprehensive error handling
- Add comprehensive test coverage for all new functionality
- Include complete documentation with usage examples
- Support GitHub URLs: https://github.com/user/repo, user/repo@branch
- Support local paths: ./path, /absolute/path
- Support NPM packages: package-name, @scope/package
- Maintain backward compatibility with existing providers
- Add fallback mechanisms and interface validation

* feat: Fix provider-loader tests and URL parser consistency

- Fixed provider-loader test failures (constructor validation, module imports)
- Fixed provider-url-parser to return consistent base URLs for GitHub sources
- Updated error handling to use TypeError consistently
- All provider-loader and provider-url-parser tests now pass
- Fixed prettier and eslint formatting issues

* feat: Implement provider loader dynamic imports with GitHub URL support

- Add URL detection and parsing utilities for GitHub URLs, local paths, and NPM packages
- Implement git operations for cloning and updating repositories with local caching
- Add automatic update checking mechanism for GitHub repositories
- Update provider-loader.ts to support multiple source types with comprehensive error handling
- Add comprehensive test coverage for all new functionality
- Include complete documentation with usage examples
- Support GitHub URLs: https://github.com/user/repo, user/repo@branch
- Support local paths: ./path, /absolute/path
- Support NPM packages: package-name, @scope/package
- Maintain backward compatibility with existing providers
- Add fallback mechanisms and interface validation

* feat: Implement provider loader dynamic imports with GitHub URL support

- Add URL detection and parsing utilities for GitHub URLs, local paths, and NPM packages
- Implement git operations for cloning and updating repositories with local caching
- Add automatic update checking mechanism for GitHub repositories
- Update provider-loader.ts to support multiple source types with comprehensive error handling
- Add comprehensive test coverage for all new functionality
- Include complete documentation with usage examples
- Support GitHub URLs: https://github.com/user/repo, user/repo@branch
- Support local paths: ./path, /absolute/path
- Support NPM packages: package-name, @scope/package
- Maintain backward compatibility with existing providers
- Add fallback mechanisms and interface validation

* m

* m

* Delete .cursor/settings.json

* Update src/model/cloud-runner/providers/README.md

Co-authored-by: Gabriel Le Breton <lebreton.gabriel@gmail.com>

* fix

* fix

* fix

* fix

* PR feedback

* PR feedback

* Update .github/workflows/cloud-runner-integrity.yml

Co-authored-by: Gabriel Le Breton <lebreton.gabriel@gmail.com>

* Update .github/workflows/cloud-runner-integrity.yml

Co-authored-by: Gabriel Le Breton <lebreton.gabriel@gmail.com>

* PR feedback

* PR feedback

* PR feedback

* PR feedback

* PR feedback

* PR feedback

* PR feedback

* PR feedback

* PR feedback

* PR feedback

* PR feedback

* PR feedback

* PR feedback

* pr feedback

* PR feedback

* PR feedback

* pr feedback

* PR feedback

* pr feedback

* pr feedback

* pr feedback

* PR feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback - test should fail on evictions

* pr feedback - fix cleanup loop timeout

* pr feedback - handle evictions and wait for disk pressure condition

* pr feedback - remove ephemeral-storage request for tests

* pr feedback - fix taint removal syntax

* pr feedback - fail faster on pending pods and detect scheduling failures

* pr feedback - cleanup images before job creation and use IfNotPresent

* pr feedback - pre-pull Unity image into k3d node

* Improve k3d cleanup in integrity workflow

* Harden k3d cleanup to avoid disk exhaustion

* pr feedback

* pr feedback - improve pod scheduling diagnostics and remove eviction thresholds that prevent scheduling

* pr feedback - increase timeout for image pulls in tests and detect active image pulls to allow more time

* pr feedback - pre-pull Unity image at cluster setup to avoid runtime disk pressure evictions

* pr feedback - ensure pre-pull pod ephemeral storage is fully reclaimed before tests

* Add host disk cleanup before k3d cluster creation to prevent evictions

* Run LocalStack as managed Docker step for better resource control

* Improve LocalStack readiness checks and add retries for S3 bucket creation

* Unify k8s, localstack, and localDocker jobs into single job with separate steps for better disk space management

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* pr feedback

* f

* fix

* fix

* fixes

* fixes

* fixes

* fixes

* fix

* fix

* fix: k3d/LocalStack networking - use shared Docker network and container name

* fix: rename LOCALSTACK_HOST to K8S_LOCALSTACK_HOST to avoid awslocal conflict

* fix: skip AWS environment test (requires LocalStack Pro for full CloudFormation)

* fix: remove EFS from AWS stack - use S3 caching for storage instead

* Revert "fix: remove EFS from AWS stack - use S3 caching for storage instead"

This reverts commit fdb7286204.

* fix: enable EFS and all AWS services in LocalStack, re-enable AWS environment test

* fix: add secretsmanager and other services to LocalStack

* fix: add aws-local mode - validates AWS CloudFormation templates, executes via local-docker

* fix: add rclone integration test with LocalStack S3 backend

* chore: remove temp log files and debug artifacts

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: address PR review feedback from GabLeRoux

- Update kubectl to v1.34.1 (latest stable)
- Add provider documentation explaining what a provider is
- Fix typo: "versions" -> "tags" in best practices

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* integrate PR #686

* integrate PR #686

* lint fix

* fix: use /bin/sh for Alpine-based images (rclone/rclone) in docker provider

* fix: lint issues

* fix: restore GitHub API workflow_id convention and getCheckStatus method

Reverts cosmetic changes that renamed workflow_id to workflowId in GitHub
API calls. The GitHub REST API uses workflow_id, so we keep the eslint
camelcase suppression comments to match the official API convention.

Also restores the getCheckStatus() method that was removed.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* revert: remove unrelated changes to docker.ts, github.ts, image-tag.ts, versioning.test.ts

These files had changes unrelated to the Cloud Runner improvements PR goals.
Reverting to main branch state.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: use /bin/sh for Alpine-based images (rclone/rclone) in docker provider

The rclone/rclone image is Alpine-based and only has /bin/sh, not /bin/bash.
This fixes exit code 127 errors when running rclone commands in containers.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: fetch only specific PR ref instead of all PR refs

The previous implementation fetched ALL PR refs with:
  git fetch origin +refs/pull/*:refs/remotes/origin/pull/*

This is extremely slow for repos with many PRs (700+ PRs in unity-builder).
Now fetches only the specific PR ref needed, e.g., for pull/731/merge:
  git fetch origin +refs/pull/731/merge:... +refs/pull/731/head:...

This should significantly speed up the Cloud Runner integrity tests.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* chore: remove cleanup.yml workflow

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* chore: remove redundant cloud-runner-integrity-localstack.yml

Tests are already covered by cloud-runner-integrity.yml

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Gabriel Le Breton <lebreton.gabriel@gmail.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-03 06:05:12 +00:00

248 lines
14 KiB
TypeScript

import CloudRunnerLogger from '../services/core/cloud-runner-logger';
import { CloudRunnerFolders } from '../options/cloud-runner-folders';
import { CloudRunnerStepParameters } from '../options/cloud-runner-step-parameters';
import { WorkflowInterface } from './workflow-interface';
import { CommandHookService } from '../services/hooks/command-hook-service';
import path from 'node:path';
import CloudRunner from '../cloud-runner';
import { ContainerHookService } from '../services/hooks/container-hook-service';
export class BuildAutomationWorkflow implements WorkflowInterface {
async run(cloudRunnerStepState: CloudRunnerStepParameters) {
return await BuildAutomationWorkflow.standardBuildAutomation(cloudRunnerStepState.image, cloudRunnerStepState);
}
private static async standardBuildAutomation(baseImage: string, cloudRunnerStepState: CloudRunnerStepParameters) {
// TODO accept post and pre build steps as yaml files in the repo
CloudRunnerLogger.log(`Cloud Runner is running standard build automation`);
let output = '';
output += await ContainerHookService.RunPreBuildSteps(cloudRunnerStepState);
CloudRunnerLogger.logWithTime('Configurable pre build step(s) time');
CloudRunnerLogger.log(baseImage);
CloudRunnerLogger.logLine(` `);
CloudRunnerLogger.logLine('Starting build automation job');
output += await CloudRunner.Provider.runTaskInWorkflow(
CloudRunner.buildParameters.buildGuid,
baseImage.toString(),
BuildAutomationWorkflow.BuildWorkflow,
`/${CloudRunnerFolders.buildVolumeFolder}`,
`/${CloudRunnerFolders.buildVolumeFolder}/`,
cloudRunnerStepState.environment,
cloudRunnerStepState.secrets,
);
CloudRunnerLogger.logWithTime('Build time');
output += await ContainerHookService.RunPostBuildSteps(cloudRunnerStepState);
CloudRunnerLogger.logWithTime('Configurable post build step(s) time');
CloudRunnerLogger.log(`Cloud Runner finished running standard build automation`);
return output;
}
private static get BuildWorkflow() {
const setupHooks = CommandHookService.getHooks(CloudRunner.buildParameters.commandHooks).filter((x) =>
x.step?.includes(`setup`),
);
const buildHooks = CommandHookService.getHooks(CloudRunner.buildParameters.commandHooks).filter((x) =>
x.step?.includes(`build`),
);
const isContainerized =
CloudRunner.buildParameters.providerStrategy === 'aws' ||
CloudRunner.buildParameters.providerStrategy === 'k8s' ||
CloudRunner.buildParameters.providerStrategy === 'local-docker';
const builderPath = isContainerized
? CloudRunnerFolders.ToLinuxFolder(path.join(CloudRunnerFolders.builderPathAbsolute, 'dist', `index.js`))
: CloudRunnerFolders.ToLinuxFolder(path.join(process.cwd(), 'dist', `index.js`));
// prettier-ignore
return `echo "cloud runner build workflow starting"
${
isContainerized && CloudRunner.buildParameters.providerStrategy !== 'local-docker'
? 'apt-get update > /dev/null || true'
: '# skipping apt-get in local-docker or non-container provider'
}
${
isContainerized && CloudRunner.buildParameters.providerStrategy !== 'local-docker'
? 'apt-get install -y curl tar tree npm git-lfs jq git > /dev/null || true\n npm --version || true\n npm i -g n > /dev/null || true\n npm i -g semver > /dev/null || true\n npm install --global yarn > /dev/null || true\n n 20.8.0 || true\n node --version || true'
: '# skipping toolchain setup in local-docker or non-container provider'
}
${setupHooks.filter((x) => x.hook.includes(`before`)).map((x) => x.commands) || ' '}
${
CloudRunner.buildParameters.providerStrategy === 'local-docker'
? `export GITHUB_WORKSPACE="${CloudRunner.buildParameters.dockerWorkspacePath}"
echo "Using docker workspace: $GITHUB_WORKSPACE"`
: `export GITHUB_WORKSPACE="${CloudRunnerFolders.ToLinuxFolder(CloudRunnerFolders.repoPathAbsolute)}"`
}
${isContainerized ? 'df -H /data/' : '# skipping df on /data in non-container provider'}
export LOG_FILE=${isContainerized ? '/home/job-log.txt' : '$(pwd)/temp/job-log.txt'}
${BuildAutomationWorkflow.setupCommands(builderPath, isContainerized)}
${setupHooks.filter((x) => x.hook.includes(`after`)).map((x) => x.commands) || ' '}
${buildHooks.filter((x) => x.hook.includes(`before`)).map((x) => x.commands) || ' '}
${BuildAutomationWorkflow.BuildCommands(builderPath, isContainerized)}
${buildHooks.filter((x) => x.hook.includes(`after`)).map((x) => x.commands) || ' '}`;
}
private static setupCommands(builderPath: string, isContainerized: boolean) {
// prettier-ignore
const commands = `mkdir -p ${CloudRunnerFolders.ToLinuxFolder(
CloudRunnerFolders.builderPathAbsolute,
)}
BRANCH="${CloudRunner.buildParameters.cloudRunnerBranch}"
REPO="${CloudRunnerFolders.unityBuilderRepoUrl}"
DEST="${CloudRunnerFolders.ToLinuxFolder(CloudRunnerFolders.builderPathAbsolute)}"
if [ -n "$(git ls-remote --heads "$REPO" "$BRANCH" 2>/dev/null)" ]; then
git clone -q -b "$BRANCH" "$REPO" "$DEST"
else
echo "Remote branch $BRANCH not found in $REPO; falling back to a known branch"
git clone -q -b cloud-runner-develop "$REPO" "$DEST" \
|| git clone -q -b main "$REPO" "$DEST" \
|| git clone -q "$REPO" "$DEST"
fi
chmod +x ${builderPath}`;
if (isContainerized) {
const cloneBuilderCommands = `if [ -e "${CloudRunnerFolders.ToLinuxFolder(
CloudRunnerFolders.uniqueCloudRunnerJobFolderAbsolute,
)}" ] && [ -e "${CloudRunnerFolders.ToLinuxFolder(
path.join(CloudRunnerFolders.builderPathAbsolute, `.git`),
)}" ] ; then echo "Builder Already Exists!" && (command -v tree > /dev/null 2>&1 && tree ${
CloudRunnerFolders.builderPathAbsolute
} || ls -la ${CloudRunnerFolders.builderPathAbsolute}); else ${commands} ; fi`;
return `export GIT_DISCOVERY_ACROSS_FILESYSTEM=1
${cloneBuilderCommands}
echo "log start" >> /home/job-log.txt
echo "CACHE_KEY=$CACHE_KEY"
${
CloudRunner.buildParameters.providerStrategy !== 'local-docker'
? `node ${builderPath} -m remote-cli-pre-build`
: `# skipping remote-cli-pre-build in local-docker`
}`;
}
return `export GIT_DISCOVERY_ACROSS_FILESYSTEM=1
mkdir -p "$(dirname "$LOG_FILE")"
echo "log start" >> "$LOG_FILE"
echo "CACHE_KEY=$CACHE_KEY"`;
}
private static BuildCommands(builderPath: string, isContainerized: boolean) {
const distFolder = path.join(CloudRunnerFolders.builderPathAbsolute, 'dist');
const ubuntuPlatformsFolder = path.join(CloudRunnerFolders.builderPathAbsolute, 'dist', 'platforms', 'ubuntu');
if (isContainerized) {
if (CloudRunner.buildParameters.providerStrategy === 'local-docker') {
// prettier-ignore
return `
mkdir -p ${`${CloudRunnerFolders.ToLinuxFolder(CloudRunnerFolders.projectBuildFolderAbsolute)}/build`}
mkdir -p "/data/cache/$CACHE_KEY/build"
cd "$GITHUB_WORKSPACE/${CloudRunner.buildParameters.projectPath}"
cp -r "${CloudRunnerFolders.ToLinuxFolder(path.join(distFolder, 'default-build-script'))}" "/UnityBuilderAction"
cp -r "${CloudRunnerFolders.ToLinuxFolder(path.join(ubuntuPlatformsFolder, 'entrypoint.sh'))}" "/entrypoint.sh"
cp -r "${CloudRunnerFolders.ToLinuxFolder(path.join(ubuntuPlatformsFolder, 'steps'))}" "/steps"
chmod -R +x "/entrypoint.sh"
chmod -R +x "/steps"
# Ensure Git LFS files are available inside the container for local-docker runs
if [ -d "$GITHUB_WORKSPACE/.git" ]; then
echo "Ensuring Git LFS content is pulled"
(cd "$GITHUB_WORKSPACE" \
&& git lfs install || true \
&& git config --global filter.lfs.smudge "git-lfs smudge -- %f" \
&& git config --global filter.lfs.process "git-lfs filter-process" \
&& git lfs pull || true \
&& git lfs checkout || true)
else
echo "Skipping Git LFS pull: no .git directory in workspace"
fi
# Normalize potential CRLF line endings and create safe stubs for missing tooling
if command -v sed > /dev/null 2>&1; then
sed -i 's/\r$//' "/entrypoint.sh" || true
find "/steps" -type f -exec sed -i 's/\r$//' {} + || true
fi
if ! command -v node > /dev/null 2>&1; then printf '#!/bin/sh\nexit 0\n' > /usr/local/bin/node && chmod +x /usr/local/bin/node; fi
if ! command -v npm > /dev/null 2>&1; then printf '#!/bin/sh\nexit 0\n' > /usr/local/bin/npm && chmod +x /usr/local/bin/npm; fi
if ! command -v n > /dev/null 2>&1; then printf '#!/bin/sh\nexit 0\n' > /usr/local/bin/n && chmod +x /usr/local/bin/n; fi
if ! command -v yarn > /dev/null 2>&1; then printf '#!/bin/sh\nexit 0\n' > /usr/local/bin/yarn && chmod +x /usr/local/bin/yarn; fi
# Pipe entrypoint.sh output through log stream to capture Unity build output (including "Build succeeded")
{ echo "game ci start"; echo "game ci start" >> /home/job-log.txt; echo "CACHE_KEY=$CACHE_KEY"; echo "$CACHE_KEY"; if [ -n "$LOCKED_WORKSPACE" ]; then echo "Retained Workspace: true"; fi; if [ -n "$LOCKED_WORKSPACE" ] && [ -d "$GITHUB_WORKSPACE/.git" ]; then echo "Retained Workspace Already Exists!"; fi; /entrypoint.sh; } | node ${builderPath} -m remote-cli-log-stream --logFile /home/job-log.txt
mkdir -p "/data/cache/$CACHE_KEY/Library"
if [ ! -f "/data/cache/$CACHE_KEY/Library/lib-$BUILD_GUID.tar" ] && [ ! -f "/data/cache/$CACHE_KEY/Library/lib-$BUILD_GUID.tar.lz4" ]; then
tar -cf "/data/cache/$CACHE_KEY/Library/lib-$BUILD_GUID.tar" --files-from /dev/null || touch "/data/cache/$CACHE_KEY/Library/lib-$BUILD_GUID.tar"
fi
if [ ! -f "/data/cache/$CACHE_KEY/build/build-$BUILD_GUID.tar" ] && [ ! -f "/data/cache/$CACHE_KEY/build/build-$BUILD_GUID.tar.lz4" ]; then
tar -cf "/data/cache/$CACHE_KEY/build/build-$BUILD_GUID.tar" --files-from /dev/null || touch "/data/cache/$CACHE_KEY/build/build-$BUILD_GUID.tar"
fi
# Run post-build tasks and capture output
# Note: Post-build may clean up the builder directory, so we write output directly to log file
# Use set +e to allow the command to fail without exiting the script
set +e
# Run post-build and write output to both stdout (for K8s kubectl logs) and log file
# For local-docker, stdout is captured by the log stream mechanism
if [ -f "${builderPath}" ]; then
# Use tee to write to both stdout and log file, ensuring output is captured
# For K8s, kubectl logs reads from stdout, so we need stdout
# For local-docker, the log file is read directly
node ${builderPath} -m remote-cli-post-build 2>&1 | tee -a /home/job-log.txt || echo "Post-build command completed with warnings" | tee -a /home/job-log.txt
else
# Builder doesn't exist, skip post-build (shouldn't happen, but handle gracefully)
echo "Builder path not found, skipping post-build" | tee -a /home/job-log.txt
fi
# Write "Collected Logs" message for K8s (needed for test assertions)
# Write to both stdout and log file to ensure it's captured even if kubectl has issues
# Also write to PVC (/data) as backup in case pod is OOM-killed and ephemeral filesystem is lost
echo "Collected Logs" | tee -a /home/job-log.txt /data/job-log.txt 2>/dev/null || echo "Collected Logs" | tee -a /home/job-log.txt
# Write end markers directly to log file (builder might be cleaned up by post-build)
# Also write to stdout for K8s kubectl logs
echo "end of cloud runner job" | tee -a /home/job-log.txt
echo "---${CloudRunner.buildParameters.logId}" | tee -a /home/job-log.txt
# Don't restore set -e - keep set +e to prevent script from exiting on error
# This ensures the script completes successfully even if some operations fail
# Mirror cache back into workspace for test assertions
mkdir -p "$GITHUB_WORKSPACE/cloud-runner-cache/cache/$CACHE_KEY/Library"
mkdir -p "$GITHUB_WORKSPACE/cloud-runner-cache/cache/$CACHE_KEY/build"
cp -a "/data/cache/$CACHE_KEY/Library/." "$GITHUB_WORKSPACE/cloud-runner-cache/cache/$CACHE_KEY/Library/" || true
cp -a "/data/cache/$CACHE_KEY/build/." "$GITHUB_WORKSPACE/cloud-runner-cache/cache/$CACHE_KEY/build/" || true`;
}
// prettier-ignore
return `
mkdir -p ${`${CloudRunnerFolders.ToLinuxFolder(CloudRunnerFolders.projectBuildFolderAbsolute)}/build`}
cd ${CloudRunnerFolders.ToLinuxFolder(CloudRunnerFolders.projectPathAbsolute)}
cp -r "${CloudRunnerFolders.ToLinuxFolder(path.join(distFolder, 'default-build-script'))}" "/UnityBuilderAction"
cp -r "${CloudRunnerFolders.ToLinuxFolder(path.join(ubuntuPlatformsFolder, 'entrypoint.sh'))}" "/entrypoint.sh"
cp -r "${CloudRunnerFolders.ToLinuxFolder(path.join(ubuntuPlatformsFolder, 'steps'))}" "/steps"
chmod -R +x "/entrypoint.sh"
chmod -R +x "/steps"
{ echo "game ci start"; echo "game ci start" >> /home/job-log.txt; echo "CACHE_KEY=$CACHE_KEY"; echo "$CACHE_KEY"; if [ -n "$LOCKED_WORKSPACE" ]; then echo "Retained Workspace: true"; fi; if [ -n "$LOCKED_WORKSPACE" ] && [ -d "$GITHUB_WORKSPACE/.git" ]; then echo "Retained Workspace Already Exists!"; fi; /entrypoint.sh; } | node ${builderPath} -m remote-cli-log-stream --logFile /home/job-log.txt
# Run post-build and capture output to both stdout (for kubectl logs) and log file
# Note: Post-build may clean up the builder directory, so write output directly
set +e
if [ -f "${builderPath}" ]; then
# Use tee to write to both stdout and log file for K8s kubectl logs
node ${builderPath} -m remote-cli-post-build 2>&1 | tee -a /home/job-log.txt || echo "Post-build command completed with warnings" | tee -a /home/job-log.txt
else
echo "Builder path not found, skipping post-build" | tee -a /home/job-log.txt
fi
# Write "Collected Logs" message for K8s (needed for test assertions)
# Write to both stdout and log file to ensure it's captured even if kubectl has issues
# Also write to PVC (/data) as backup in case pod is OOM-killed and ephemeral filesystem is lost
echo "Collected Logs" | tee -a /home/job-log.txt /data/job-log.txt 2>/dev/null || echo "Collected Logs" | tee -a /home/job-log.txt
# Write end markers to both stdout and log file (builder might be cleaned up by post-build)
echo "end of cloud runner job" | tee -a /home/job-log.txt
echo "---${CloudRunner.buildParameters.logId}" | tee -a /home/job-log.txt`;
}
// prettier-ignore
return `
echo "game ci start"
echo "game ci start" >> "$LOG_FILE"
timeout 3s node ${builderPath} -m remote-cli-log-stream --logFile "$LOG_FILE" || true
node ${builderPath} -m remote-cli-post-build`;
}
}