Compare commits

..

2 Commits

Author SHA1 Message Date
frostebite
3d8edb0711 fix: replace orchestrator-develop branch references with main
The orchestrator-develop branch no longer exists. Update all fallback
clone commands and test fixtures to use main instead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 19:54:16 +00:00
frostebite
9cf65cf5fc feat: add CLAUDE.md for AI-assisted development
Adds a minimal CLAUDE.md covering project architecture, build system,
code conventions, testing, security considerations, and CI workflows.
Provides Claude Code with the context needed to assist with builds,
debugging, orchestrator work, and contributions.

Closes #801

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 18:18:28 +00:00
12 changed files with 101 additions and 327 deletions

View File

@@ -12,7 +12,6 @@ jobs:
buildForAllPlatformsMacOS:
name: ${{ matrix.targetPlatform }} on ${{ matrix.unityVersion }}
runs-on: macos-latest
continue-on-error: true
strategy:
fail-fast: false
matrix:

80
CLAUDE.md Normal file
View File

@@ -0,0 +1,80 @@
# Unity-Builder
GitHub Action and CLI that builds Unity projects for multiple platforms. Part of the [GameCI](https://game.ci) project.
## Quick Reference
```bash
yarn # install dependencies
yarn build # full build: tsc → ncc bundle (src/ → lib/ → dist/index.js)
yarn test # run all tests (jest)
yarn test:ci # run tests in CI mode (single-threaded, 2min timeout)
yarn lint # prettier + eslint check
yarn format # auto-format with prettier
```
## Architecture
**Entry point:** `src/index.ts` → decides between CLI mode and GitHub Action mode.
**Two execution paths:**
1. **Local builds** — Docker container or native macOS (`src/model/docker.ts`, `src/model/mac-builder.ts`)
2. **Orchestrator builds** — Remote execution on AWS ECS, Kubernetes, or other providers (`src/model/orchestrator/`)
**Key modules:**
| Path | Purpose |
|---|---|
| `src/model/build-parameters.ts` | Central config object — all build settings flow through here |
| `src/model/input.ts` | Input resolution with priority: Action inputs → CLI flags → env override → env vars |
| `src/model/orchestrator/orchestrator.ts` | Remote build orchestration — provider selection, workflow execution |
| `src/model/orchestrator/providers/` | Provider plugin system (AWS, K8s, Docker, Local, Test) |
| `src/model/orchestrator/remote-client/` | Code that runs inside remote containers (caching, hooks, artifacts) |
| `src/model/orchestrator/workflows/` | Build workflow types (standard, custom, async) |
| `src/model/orchestrator/services/` | Logging, locking, resource tracking |
| `src/model/cli/` | CLI mode using commander — dispatches to `@CliFunction`-decorated methods |
| `action.yml` | GitHub Action manifest — all inputs/outputs defined here |
| `dist/index.js` | Bundled output (committed to repo, used by action.yml at runtime) |
**Provider interface:** All providers implement `ProviderInterface` (`providers/provider-interface.ts`) with methods: `setupWorkflow`, `runTaskInWorkflow`, `cleanupWorkflow`, `garbageCollect`, `listResources`, `listWorkflow`, `watchWorkflow`.
**Provider loading:** Providers can be built-in, loaded from npm, cloned from GitHub repos, or loaded from local paths (`provider-loader.ts`).
## Build System
The build pipeline is: `yarn``tsc` (src/ → lib/) → `ncc build lib` (lib/ → dist/index.js).
- **dist/ is committed** — GitHub Actions loads `dist/index.js` directly, no install step on runners
- **Pre-commit hooks** (lefthook) auto-run formatting, linting, related tests, and `yarn build` to keep dist/ in sync
- Runtime: Node 20 (configured via Volta and action.yml `runs.using: node20`)
## Code Conventions
- **Files:** kebab-case (enforced by eslint `unicorn/filename-case`)
- **Code:** camelCase variables/functions, PascalCase classes/types
- **Formatting:** Prettier — 120 char width, single quotes, trailing commas, semicolons
- **Linting:** ESLint with unicorn, github, prettier, jest plugins
- **TypeScript:** strict mode, ES2020 target, CommonJS modules, experimental decorators enabled
- **Blank line before return statements** (enforced)
- **Blank line before block/line comments** (enforced)
- **No `for...in` loops** — use `for...of`
## Testing
- **Framework:** Jest 27 with ts-jest
- **Pattern:** `**/*.test.ts` files colocated with source
- **Orchestrator tests:** Concentrated in `src/model/orchestrator/tests/`
- **Run specific tests:** `yarn test -t "pattern"` or `yarn jest path/to/file.test.ts`
- **Orchestrator integration tests** require `orchestratorTests=true` env var: `cross-env orchestratorTests=true yarn test -i -t "orchestrator"`
## Security
- **Never log, output, or hardcode credentials** — cloud provider secrets (AWS, GCP, K8s), Unity serial keys, keystores, and private tokens must stay in secret inputs
- **Input validation matters** — user-supplied hook commands and custom parameters can be injection vectors; use `shell-quote` for shell escaping
- **Keystore/license data** is base64-encoded in inputs and written to temp files at build time
## CI Workflows
- `integrity-check.yml` — lint, test, build on every push/PR
- `build-tests-{ubuntu,windows,mac}.yml` — matrix builds across Unity versions and platforms
- `orchestrator-integrity.yml` / `orchestrator-async-checks.yml` — orchestrator-specific validation

View File

@@ -105,12 +105,6 @@ inputs:
required: false
default: ''
description: '[Orchestrator] Github private token to pull from github'
gitAuthMode:
required: false
default: 'header'
description:
'[Orchestrator] How git authentication is configured. "header" (default) uses http.extraHeader so the token
never appears in clone URLs or git config. "url" embeds the token in clone URLs (legacy behavior).'
githubOwner:
required: false
default: ''

102
dist/index.js generated vendored
View File

@@ -327,7 +327,6 @@ class BuildParameters {
containerRegistryRepository: input_1.default.containerRegistryRepository,
containerRegistryImageVersion: input_1.default.containerRegistryImageVersion,
providerStrategy: orchestrator_options_1.default.providerStrategy,
gitAuthMode: orchestrator_options_1.default.gitAuthMode,
buildPlatform: orchestrator_options_1.default.buildPlatform,
kubeConfig: orchestrator_options_1.default.kubeConfig,
containerMemory: orchestrator_options_1.default.containerMemory,
@@ -1945,29 +1944,6 @@ exports["default"] = OrchestratorEnvironmentVariable;
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
@@ -2022,57 +1998,12 @@ class OrchestratorFolders {
static get libraryCacheFolderFull() {
return node_path_1.default.join(OrchestratorFolders.cacheFolderForCacheKeyFull, `Library`);
}
/**
* Whether to use http.extraHeader for git authentication (secure, default)
* instead of embedding the token in clone URLs (legacy).
*/
static get useHeaderAuth() {
return orchestrator_1.default.buildParameters.gitAuthMode !== 'url';
}
static get unityBuilderRepoUrl() {
if (OrchestratorFolders.useHeaderAuth) {
return `https://github.com/${orchestrator_1.default.buildParameters.orchestratorRepoName}.git`;
}
return `https://${orchestrator_1.default.buildParameters.gitPrivateToken}@github.com/${orchestrator_1.default.buildParameters.orchestratorRepoName}.git`;
}
static get targetBuildRepoUrl() {
if (OrchestratorFolders.useHeaderAuth) {
return `https://github.com/${orchestrator_1.default.buildParameters.githubRepo}.git`;
}
return `https://${orchestrator_1.default.buildParameters.gitPrivateToken}@github.com/${orchestrator_1.default.buildParameters.githubRepo}.git`;
}
/**
* Shell commands to configure git authentication via http.extraHeader.
* Uses GIT_PRIVATE_TOKEN env var so the token never appears in clone URLs or git config output.
* This is the same mechanism used by actions/checkout.
*
* Only emits commands when gitAuthMode is 'header' (default). In 'url' mode,
* returns a no-op comment since the token is already in the URL.
*/
static get gitAuthConfigScript() {
if (!OrchestratorFolders.useHeaderAuth) {
return `# git auth: using token-in-URL mode (legacy)`;
}
return `# git auth: configuring http.extraHeader (secure mode)
if [ -n "$GIT_PRIVATE_TOKEN" ]; then
git config --global http.https://github.com/.extraHeader "Authorization: Basic $(printf '%s' "x-access-token:$GIT_PRIVATE_TOKEN" | base64 -w 0)"
fi`;
}
/**
* Configure git authentication via http.extraHeader in the current Node process.
* For use in the remote-client where shell scripts aren't used.
* Only configures when gitAuthMode is 'header' (default).
*/
static async configureGitAuth() {
if (!OrchestratorFolders.useHeaderAuth)
return;
const token = orchestrator_1.default.buildParameters.gitPrivateToken || process.env.GIT_PRIVATE_TOKEN || '';
if (!token)
return;
const encoded = Buffer.from(`x-access-token:${token}`).toString('base64');
const { OrchestratorSystem } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(9744)));
await OrchestratorSystem.Run(`git config --global http.https://github.com/.extraHeader "Authorization: Basic ${encoded}"`);
}
static get buildVolumeFolder() {
return 'data';
}
@@ -2273,9 +2204,6 @@ class OrchestratorOptions {
}
return provider || 'local';
}
static get gitAuthMode() {
return OrchestratorOptions.getInput('gitAuthMode') || 'header';
}
static get containerCpu() {
return OrchestratorOptions.getInput('containerCpu') || `1024`;
}
@@ -8057,7 +7985,6 @@ class RemoteClient {
}
remote_client_logger_1.RemoteClientLogger.log(`Initializing source repository for cloning with caching of LFS files`);
await orchestrator_system_1.OrchestratorSystem.Run(`git config --global advice.detachedHead false`);
await orchestrator_folders_1.OrchestratorFolders.configureGitAuth();
remote_client_logger_1.RemoteClientLogger.log(`Cloning the repository being built:`);
await orchestrator_system_1.OrchestratorSystem.Run(`git config --global filter.lfs.smudge "git-lfs smudge --skip -- %f"`);
await orchestrator_system_1.OrchestratorSystem.Run(`git config --global filter.lfs.process "git-lfs filter-process --skip"`);
@@ -8162,7 +8089,10 @@ class RemoteClient {
const gitPrivateToken = process.env.GIT_PRIVATE_TOKEN;
if (gitPrivateToken) {
remote_client_logger_1.RemoteClientLogger.log(`Attempting to pull LFS files with GIT_PRIVATE_TOKEN...`);
await RemoteClient.configureTokenAuth(gitPrivateToken);
await orchestrator_system_1.OrchestratorSystem.Run(`git config --global --unset-all url."https://github.com/".insteadOf || true`);
await orchestrator_system_1.OrchestratorSystem.Run(`git config --global --unset-all url."ssh://git@github.com/".insteadOf || true`);
await orchestrator_system_1.OrchestratorSystem.Run(`git config --global --unset-all url."git@github.com".insteadOf || true`);
await orchestrator_system_1.OrchestratorSystem.Run(`git config --global url."https://${gitPrivateToken}@github.com/".insteadOf "https://github.com/"`);
await orchestrator_system_1.OrchestratorSystem.Run(`git lfs pull`, true);
await orchestrator_system_1.OrchestratorSystem.Run(`git lfs checkout || true`, true);
remote_client_logger_1.RemoteClientLogger.log(`Successfully pulled LFS files with GIT_PRIVATE_TOKEN`);
@@ -8177,7 +8107,10 @@ class RemoteClient {
const githubToken = process.env.GITHUB_TOKEN;
if (githubToken) {
remote_client_logger_1.RemoteClientLogger.log(`Attempting to pull LFS files with GITHUB_TOKEN fallback...`);
await RemoteClient.configureTokenAuth(githubToken);
await orchestrator_system_1.OrchestratorSystem.Run(`git config --global --unset-all url."https://github.com/".insteadOf || true`);
await orchestrator_system_1.OrchestratorSystem.Run(`git config --global --unset-all url."ssh://git@github.com/".insteadOf || true`);
await orchestrator_system_1.OrchestratorSystem.Run(`git config --global --unset-all url."git@github.com".insteadOf || true`);
await orchestrator_system_1.OrchestratorSystem.Run(`git config --global url."https://${githubToken}@github.com/".insteadOf "https://github.com/"`);
await orchestrator_system_1.OrchestratorSystem.Run(`git lfs pull`, true);
await orchestrator_system_1.OrchestratorSystem.Run(`git lfs checkout || true`, true);
remote_client_logger_1.RemoteClientLogger.log(`Successfully pulled LFS files with GITHUB_TOKEN`);
@@ -8234,23 +8167,6 @@ class RemoteClient {
}
return false;
}
/**
* Configure git authentication for a token. In header mode (default), uses
* http.extraHeader so the token never appears in URLs or git config output.
* In url mode (legacy), uses url.insteadOf to embed the token in URLs.
*/
static async configureTokenAuth(token) {
if (orchestrator_folders_1.OrchestratorFolders.useHeaderAuth) {
const encoded = Buffer.from(`x-access-token:${token}`).toString('base64');
await orchestrator_system_1.OrchestratorSystem.Run(`git config --global http.https://github.com/.extraHeader "Authorization: Basic ${encoded}"`);
}
else {
await orchestrator_system_1.OrchestratorSystem.Run(`git config --global --unset-all url."https://github.com/".insteadOf || true`);
await orchestrator_system_1.OrchestratorSystem.Run(`git config --global --unset-all url."ssh://git@github.com/".insteadOf || true`);
await orchestrator_system_1.OrchestratorSystem.Run(`git config --global --unset-all url."git@github.com".insteadOf || true`);
await orchestrator_system_1.OrchestratorSystem.Run(`git config --global url."https://${token}@github.com/".insteadOf "https://github.com/"`);
}
}
}
__decorate([
(0, cli_functions_repository_1.CliFunction)(`remote-cli-pre-build`, `sets up a repository, usually before a game-ci build`)
@@ -9809,7 +9725,6 @@ printenv
git config --global advice.detachedHead false
git config --global filter.lfs.smudge "git-lfs smudge --skip -- %f"
git config --global filter.lfs.process "git-lfs filter-process --skip"
${orchestrator_folders_1.OrchestratorFolders.gitAuthConfigScript}
BRANCH="${orchestrator_1.default.buildParameters.orchestratorBranch}"
REPO="${orchestrator_folders_1.OrchestratorFolders.unityBuilderRepoUrl}"
if [ -n "$(git ls-remote --heads "$REPO" "$BRANCH" 2>/dev/null)" ]; then
@@ -9926,7 +9841,6 @@ class BuildAutomationWorkflow {
static setupCommands(builderPath, isContainerized) {
// prettier-ignore
const commands = `mkdir -p ${orchestrator_folders_1.OrchestratorFolders.ToLinuxFolder(orchestrator_folders_1.OrchestratorFolders.builderPathAbsolute)}
${orchestrator_folders_1.OrchestratorFolders.gitAuthConfigScript}
BRANCH="${orchestrator_1.default.buildParameters.orchestratorBranch}"
REPO="${orchestrator_folders_1.OrchestratorFolders.unityBuilderRepoUrl}"
DEST="${orchestrator_folders_1.OrchestratorFolders.ToLinuxFolder(orchestrator_folders_1.OrchestratorFolders.builderPathAbsolute)}"

2
dist/index.js.map generated vendored

File diff suppressed because one or more lines are too long

View File

@@ -54,7 +54,6 @@ class BuildParameters {
public sshAgent!: string;
public sshPublicKeysDirectoryPath!: string;
public providerStrategy!: string;
public gitAuthMode!: string;
public gitPrivateToken!: string;
public awsStackName!: string;
public awsEndpoint?: string;
@@ -195,7 +194,6 @@ class BuildParameters {
containerRegistryRepository: Input.containerRegistryRepository,
containerRegistryImageVersion: Input.containerRegistryImageVersion,
providerStrategy: OrchestratorOptions.providerStrategy,
gitAuthMode: OrchestratorOptions.gitAuthMode,
buildPlatform: OrchestratorOptions.buildPlatform,
kubeConfig: OrchestratorOptions.kubeConfig,
containerMemory: OrchestratorOptions.containerMemory,

View File

@@ -1,140 +0,0 @@
import { OrchestratorFolders } from './orchestrator-folders';
jest.mock('../orchestrator', () => ({
__esModule: true,
default: {
buildParameters: {
orchestratorRepoName: 'game-ci/unity-builder',
githubRepo: 'myorg/myrepo',
gitPrivateToken: 'ghp_test123',
gitAuthMode: 'header',
buildGuid: 'test-guid',
projectPath: '',
buildPath: 'Builds',
cacheKey: 'test-cache',
},
lockedWorkspace: '',
},
}));
jest.mock('./orchestrator-options', () => ({
__esModule: true,
default: {
useSharedBuilder: false,
},
}));
jest.mock('../services/core/orchestrator-system', () => ({
OrchestratorSystem: {
Run: jest.fn().mockResolvedValue(''),
},
}));
const mockOrchestrator = require('../orchestrator').default;
describe('OrchestratorFolders git auth', () => {
beforeEach(() => {
jest.clearAllMocks();
});
describe('useHeaderAuth', () => {
it('should return true when gitAuthMode is header', () => {
mockOrchestrator.buildParameters.gitAuthMode = 'header';
expect(OrchestratorFolders.useHeaderAuth).toBe(true);
});
it('should return true when gitAuthMode is undefined (default)', () => {
mockOrchestrator.buildParameters.gitAuthMode = undefined;
expect(OrchestratorFolders.useHeaderAuth).toBe(true);
});
it('should return false when gitAuthMode is url', () => {
mockOrchestrator.buildParameters.gitAuthMode = 'url';
expect(OrchestratorFolders.useHeaderAuth).toBe(false);
});
});
describe('unityBuilderRepoUrl', () => {
it('should not include token in URL when using header auth', () => {
mockOrchestrator.buildParameters.gitAuthMode = 'header';
const url = OrchestratorFolders.unityBuilderRepoUrl;
expect(url).toBe('https://github.com/game-ci/unity-builder.git');
expect(url).not.toContain('ghp_test123');
});
it('should include token in URL when using url auth (legacy)', () => {
mockOrchestrator.buildParameters.gitAuthMode = 'url';
const url = OrchestratorFolders.unityBuilderRepoUrl;
expect(url).toBe('https://ghp_test123@github.com/game-ci/unity-builder.git');
});
});
describe('targetBuildRepoUrl', () => {
it('should not include token in URL when using header auth', () => {
mockOrchestrator.buildParameters.gitAuthMode = 'header';
const url = OrchestratorFolders.targetBuildRepoUrl;
expect(url).toBe('https://github.com/myorg/myrepo.git');
expect(url).not.toContain('ghp_test123');
});
it('should include token in URL when using url auth (legacy)', () => {
mockOrchestrator.buildParameters.gitAuthMode = 'url';
const url = OrchestratorFolders.targetBuildRepoUrl;
expect(url).toBe('https://ghp_test123@github.com/myorg/myrepo.git');
});
});
describe('gitAuthConfigScript', () => {
it('should emit http.extraHeader commands in header mode', () => {
mockOrchestrator.buildParameters.gitAuthMode = 'header';
const script = OrchestratorFolders.gitAuthConfigScript;
expect(script).toContain('http.extraHeader');
expect(script).toContain('GIT_PRIVATE_TOKEN');
expect(script).toContain('Authorization: Basic');
});
it('should emit no-op comment in url mode', () => {
mockOrchestrator.buildParameters.gitAuthMode = 'url';
const script = OrchestratorFolders.gitAuthConfigScript;
expect(script).toContain('legacy');
expect(script).not.toContain('http.extraHeader');
});
});
describe('configureGitAuth', () => {
it('should run git config with http.extraHeader in header mode', async () => {
mockOrchestrator.buildParameters.gitAuthMode = 'header';
mockOrchestrator.buildParameters.gitPrivateToken = 'ghp_test123';
const { OrchestratorSystem } = require('../services/core/orchestrator-system');
await OrchestratorFolders.configureGitAuth();
// Verify the base64 encoding and extraHeader config are correct
const expectedEncoded = Buffer.from('x-access-token:ghp_test123').toString('base64');
expect(OrchestratorSystem.Run).toHaveBeenCalledWith(expect.stringContaining(expectedEncoded));
expect(OrchestratorSystem.Run).toHaveBeenCalledWith(expect.stringContaining('.extraHeader'));
});
it('should not run git config in url mode', async () => {
mockOrchestrator.buildParameters.gitAuthMode = 'url';
const { OrchestratorSystem } = require('../services/core/orchestrator-system');
await OrchestratorFolders.configureGitAuth();
expect(OrchestratorSystem.Run).not.toHaveBeenCalled();
});
it('should not run git config when no token is available', async () => {
mockOrchestrator.buildParameters.gitAuthMode = 'header';
mockOrchestrator.buildParameters.gitPrivateToken = '';
const originalEnv = process.env.GIT_PRIVATE_TOKEN;
delete process.env.GIT_PRIVATE_TOKEN;
const { OrchestratorSystem } = require('../services/core/orchestrator-system');
await OrchestratorFolders.configureGitAuth();
expect(OrchestratorSystem.Run).not.toHaveBeenCalled();
if (originalEnv !== undefined) process.env.GIT_PRIVATE_TOKEN = originalEnv;
});
});
});

View File

@@ -72,67 +72,14 @@ export class OrchestratorFolders {
return path.join(OrchestratorFolders.cacheFolderForCacheKeyFull, `Library`);
}
/**
* Whether to use http.extraHeader for git authentication (secure, default)
* instead of embedding the token in clone URLs (legacy).
*/
public static get useHeaderAuth(): boolean {
return Orchestrator.buildParameters.gitAuthMode !== 'url';
}
public static get unityBuilderRepoUrl(): string {
if (OrchestratorFolders.useHeaderAuth) {
return `https://github.com/${Orchestrator.buildParameters.orchestratorRepoName}.git`;
}
return `https://${Orchestrator.buildParameters.gitPrivateToken}@github.com/${Orchestrator.buildParameters.orchestratorRepoName}.git`;
}
public static get targetBuildRepoUrl(): string {
if (OrchestratorFolders.useHeaderAuth) {
return `https://github.com/${Orchestrator.buildParameters.githubRepo}.git`;
}
return `https://${Orchestrator.buildParameters.gitPrivateToken}@github.com/${Orchestrator.buildParameters.githubRepo}.git`;
}
/**
* Shell commands to configure git authentication via http.extraHeader.
* Uses GIT_PRIVATE_TOKEN env var so the token never appears in clone URLs or git config output.
* This is the same mechanism used by actions/checkout.
*
* Only emits commands when gitAuthMode is 'header' (default). In 'url' mode,
* returns a no-op comment since the token is already in the URL.
*/
public static get gitAuthConfigScript(): string {
if (!OrchestratorFolders.useHeaderAuth) {
return `# git auth: using token-in-URL mode (legacy)`;
}
return `# git auth: configuring http.extraHeader (secure mode)
if [ -n "$GIT_PRIVATE_TOKEN" ]; then
git config --global http.https://github.com/.extraHeader "Authorization: Basic $(printf '%s' "x-access-token:$GIT_PRIVATE_TOKEN" | base64 -w 0)"
fi`;
}
/**
* Configure git authentication via http.extraHeader in the current Node process.
* For use in the remote-client where shell scripts aren't used.
* Only configures when gitAuthMode is 'header' (default).
*/
public static async configureGitAuth(): Promise<void> {
if (!OrchestratorFolders.useHeaderAuth) return;
const token = Orchestrator.buildParameters.gitPrivateToken || process.env.GIT_PRIVATE_TOKEN || '';
if (!token) return;
const encoded = Buffer.from(`x-access-token:${token}`).toString('base64');
const { OrchestratorSystem } = await import('../services/core/orchestrator-system');
await OrchestratorSystem.Run(
`git config --global http.https://github.com/.extraHeader "Authorization: Basic ${encoded}"`,
);
}
public static get buildVolumeFolder() {
return 'data';
}

View File

@@ -138,10 +138,6 @@ class OrchestratorOptions {
return provider || 'local';
}
static get gitAuthMode(): string {
return OrchestratorOptions.getInput('gitAuthMode') || 'header';
}
static get containerCpu(): string {
return OrchestratorOptions.getInput('containerCpu') || `1024`;
}

View File

@@ -302,7 +302,6 @@ export class RemoteClient {
RemoteClientLogger.log(`Initializing source repository for cloning with caching of LFS files`);
await OrchestratorSystem.Run(`git config --global advice.detachedHead false`);
await OrchestratorFolders.configureGitAuth();
RemoteClientLogger.log(`Cloning the repository being built:`);
await OrchestratorSystem.Run(`git config --global filter.lfs.smudge "git-lfs smudge --skip -- %f"`);
await OrchestratorSystem.Run(`git config --global filter.lfs.process "git-lfs filter-process --skip"`);
@@ -412,7 +411,12 @@ export class RemoteClient {
const gitPrivateToken = process.env.GIT_PRIVATE_TOKEN;
if (gitPrivateToken) {
RemoteClientLogger.log(`Attempting to pull LFS files with GIT_PRIVATE_TOKEN...`);
await RemoteClient.configureTokenAuth(gitPrivateToken);
await OrchestratorSystem.Run(`git config --global --unset-all url."https://github.com/".insteadOf || true`);
await OrchestratorSystem.Run(`git config --global --unset-all url."ssh://git@github.com/".insteadOf || true`);
await OrchestratorSystem.Run(`git config --global --unset-all url."git@github.com".insteadOf || true`);
await OrchestratorSystem.Run(
`git config --global url."https://${gitPrivateToken}@github.com/".insteadOf "https://github.com/"`,
);
await OrchestratorSystem.Run(`git lfs pull`, true);
await OrchestratorSystem.Run(`git lfs checkout || true`, true);
RemoteClientLogger.log(`Successfully pulled LFS files with GIT_PRIVATE_TOKEN`);
@@ -428,7 +432,12 @@ export class RemoteClient {
const githubToken = process.env.GITHUB_TOKEN;
if (githubToken) {
RemoteClientLogger.log(`Attempting to pull LFS files with GITHUB_TOKEN fallback...`);
await RemoteClient.configureTokenAuth(githubToken);
await OrchestratorSystem.Run(`git config --global --unset-all url."https://github.com/".insteadOf || true`);
await OrchestratorSystem.Run(`git config --global --unset-all url."ssh://git@github.com/".insteadOf || true`);
await OrchestratorSystem.Run(`git config --global --unset-all url."git@github.com".insteadOf || true`);
await OrchestratorSystem.Run(
`git config --global url."https://${githubToken}@github.com/".insteadOf "https://github.com/"`,
);
await OrchestratorSystem.Run(`git lfs pull`, true);
await OrchestratorSystem.Run(`git lfs checkout || true`, true);
RemoteClientLogger.log(`Successfully pulled LFS files with GITHUB_TOKEN`);
@@ -492,25 +501,4 @@ export class RemoteClient {
return false;
}
/**
* Configure git authentication for a token. In header mode (default), uses
* http.extraHeader so the token never appears in URLs or git config output.
* In url mode (legacy), uses url.insteadOf to embed the token in URLs.
*/
private static async configureTokenAuth(token: string): Promise<void> {
if (OrchestratorFolders.useHeaderAuth) {
const encoded = Buffer.from(`x-access-token:${token}`).toString('base64');
await OrchestratorSystem.Run(
`git config --global http.https://github.com/.extraHeader "Authorization: Basic ${encoded}"`,
);
} else {
await OrchestratorSystem.Run(`git config --global --unset-all url."https://github.com/".insteadOf || true`);
await OrchestratorSystem.Run(`git config --global --unset-all url."ssh://git@github.com/".insteadOf || true`);
await OrchestratorSystem.Run(`git config --global --unset-all url."git@github.com".insteadOf || true`);
await OrchestratorSystem.Run(
`git config --global url."https://${token}@github.com/".insteadOf "https://github.com/"`,
);
}
}
}

View File

@@ -27,7 +27,6 @@ printenv
git config --global advice.detachedHead false
git config --global filter.lfs.smudge "git-lfs smudge --skip -- %f"
git config --global filter.lfs.process "git-lfs filter-process --skip"
${OrchestratorFolders.gitAuthConfigScript}
BRANCH="${Orchestrator.buildParameters.orchestratorBranch}"
REPO="${OrchestratorFolders.unityBuilderRepoUrl}"
if [ -n "$(git ls-remote --heads "$REPO" "$BRANCH" 2>/dev/null)" ]; then

View File

@@ -92,7 +92,6 @@ export class BuildAutomationWorkflow implements WorkflowInterface {
const commands = `mkdir -p ${OrchestratorFolders.ToLinuxFolder(
OrchestratorFolders.builderPathAbsolute,
)}
${OrchestratorFolders.gitAuthConfigScript}
BRANCH="${Orchestrator.buildParameters.orchestratorBranch}"
REPO="${OrchestratorFolders.unityBuilderRepoUrl}"
DEST="${OrchestratorFolders.ToLinuxFolder(OrchestratorFolders.builderPathAbsolute)}"