cloud-runner-develop
Frostebite 2025-12-04 22:32:47 +00:00
parent 1eca7bb6b9
commit 945dec774c
8 changed files with 388 additions and 65 deletions

View File

@ -23,15 +23,16 @@ jobs:
with: with:
node-version: '18' node-version: '18'
- run: yarn - run: yarn
- run: yarn run cli --help # Commented out: Using LocalStack tests instead of real AWS
env: # - run: yarn run cli --help
AWS_REGION: eu-west-2 # env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} # AWS_REGION: eu-west-2
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} # AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_DEFAULT_REGION: eu-west-2 # AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- run: yarn run cli -m list-resources # AWS_DEFAULT_REGION: eu-west-2
env: # - run: yarn run cli -m list-resources
AWS_REGION: eu-west-2 # env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} # AWS_REGION: eu-west-2
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} # AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_DEFAULT_REGION: eu-west-2 # AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
# AWS_DEFAULT_REGION: eu-west-2

View File

@ -19,11 +19,12 @@ env:
GCP_LOGGING: true GCP_LOGGING: true
GCP_PROJECT: unitykubernetesbuilder GCP_PROJECT: unitykubernetesbuilder
GCP_LOG_FILE: ${{ github.workspace }}/cloud-runner-logs.txt GCP_LOG_FILE: ${{ github.workspace }}/cloud-runner-logs.txt
AWS_REGION: eu-west-2 # Commented out: Using LocalStack tests instead of real AWS
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} # AWS_REGION: eu-west-2
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} # AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_DEFAULT_REGION: eu-west-2 # AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_STACK_NAME: game-ci-github-pipelines # AWS_DEFAULT_REGION: eu-west-2
# AWS_STACK_NAME: game-ci-github-pipelines
CLOUD_RUNNER_BRANCH: ${{ github.ref }} CLOUD_RUNNER_BRANCH: ${{ github.ref }}
CLOUD_RUNNER_DEBUG: true CLOUD_RUNNER_DEBUG: true
CLOUD_RUNNER_DEBUG_TREE: true CLOUD_RUNNER_DEBUG_TREE: true
@ -49,7 +50,8 @@ jobs:
cloudRunnerTests: true cloudRunnerTests: true
versioning: None versioning: None
CLOUD_RUNNER_CLUSTER: local-docker CLOUD_RUNNER_CLUSTER: local-docker
AWS_STACK_NAME: game-ci-github-pipelines # Commented out: Using LocalStack tests instead of real AWS
# AWS_STACK_NAME: game-ci-github-pipelines
CHECKS_UPDATE: ${{ github.event.inputs.checksObject }} CHECKS_UPDATE: ${{ github.event.inputs.checksObject }}
run: | run: |
git clone -b cloud-runner-develop https://github.com/game-ci/unity-builder git clone -b cloud-runner-develop https://github.com/game-ci/unity-builder

View File

@ -15,9 +15,10 @@ permissions:
statuses: write statuses: write
env: env:
AWS_REGION: eu-west-2 # Commented out: Using LocalStack tests instead of real AWS
AWS_DEFAULT_REGION: eu-west-2 # AWS_REGION: eu-west-2
AWS_STACK_NAME: game-ci-team-pipelines # AWS_DEFAULT_REGION: eu-west-2
AWS_STACK_NAME: game-ci-team-pipelines # Still needed for LocalStack S3 bucket creation
CLOUD_RUNNER_BRANCH: ${{ github.ref }} CLOUD_RUNNER_BRANCH: ${{ github.ref }}
DEBUG: true DEBUG: true
PROJECT_PATH: test-project PROJECT_PATH: test-project
@ -149,44 +150,45 @@ jobs:
AWS_ENDPOINT_URL: http://localhost:4566 AWS_ENDPOINT_URL: http://localhost:4566
GIT_PRIVATE_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }} GIT_PRIVATE_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }} GITHUB_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }}
aws: # Commented out: Using LocalStack tests instead of real AWS
name: Cloud Runner Tests (AWS) # aws:
runs-on: ubuntu-latest # name: Cloud Runner Tests (AWS)
needs: [k8s, localstack] # runs-on: ubuntu-latest
strategy: # needs: [k8s, localstack]
fail-fast: false # strategy:
matrix: # fail-fast: false
test: # matrix:
- 'cloud-runner-end2end-caching' # test:
- 'cloud-runner-end2end-retaining' # - 'cloud-runner-end2end-caching'
- 'cloud-runner-hooks' # - 'cloud-runner-end2end-retaining'
steps: # - 'cloud-runner-hooks'
- uses: actions/checkout@v4 # steps:
with: # - uses: actions/checkout@v4
lfs: false # with:
- name: Configure AWS Credentials # lfs: false
uses: aws-actions/configure-aws-credentials@v1 # - name: Configure AWS Credentials
with: # uses: aws-actions/configure-aws-credentials@v1
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} # with:
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} # aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-region: ${{ env.AWS_REGION }} # aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- uses: actions/setup-node@v4 # aws-region: ${{ env.AWS_REGION }}
with: # - uses: actions/setup-node@v4
node-version: 20 # with:
cache: 'yarn' # node-version: 20
- run: yarn install --frozen-lockfile # cache: 'yarn'
- run: yarn run test "${{ matrix.test }}" --detectOpenHandles --forceExit --runInBand # - run: yarn install --frozen-lockfile
timeout-minutes: 60 # - run: yarn run test "${{ matrix.test }}" --detectOpenHandles --forceExit --runInBand
env: # timeout-minutes: 60
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} # env:
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} # UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }} # UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
PROJECT_PATH: test-project # UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
TARGET_PLATFORM: StandaloneWindows64 # PROJECT_PATH: test-project
cloudRunnerTests: true # TARGET_PLATFORM: StandaloneWindows64
versioning: None # cloudRunnerTests: true
PROVIDER_STRATEGY: aws # versioning: None
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} # PROVIDER_STRATEGY: aws
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} # AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
GIT_PRIVATE_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }} # AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
GITHUB_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }} # GIT_PRIVATE_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }}
# GITHUB_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }}

278
dist/index.js vendored
View File

@ -4368,6 +4368,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
Object.defineProperty(exports, "__esModule", ({ value: true })); Object.defineProperty(exports, "__esModule", ({ value: true }));
const cloud_runner_system_1 = __nccwpck_require__(4197); const cloud_runner_system_1 = __nccwpck_require__(4197);
const cloud_runner_logger_1 = __importDefault(__nccwpck_require__(42864)); const cloud_runner_logger_1 = __importDefault(__nccwpck_require__(42864));
const shell_quote_1 = __nccwpck_require__(87029);
class LocalCloudRunner { class LocalCloudRunner {
listResources() { listResources() {
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
@ -4422,12 +4423,12 @@ class LocalCloudRunner {
// On Windows, many built-in hooks use POSIX shell syntax. Execute via bash if available. // On Windows, many built-in hooks use POSIX shell syntax. Execute via bash if available.
if (process.platform === 'win32') { if (process.platform === 'win32') {
const inline = commands const inline = commands
.replace(/"/g, '\\"')
.replace(/\r/g, '') .replace(/\r/g, '')
.split('\n') .split('\n')
.filter((x) => x.trim().length > 0) .filter((x) => x.trim().length > 0)
.join(' ; '); .join(' ; ');
const bashWrapped = `bash -lc "${inline}"`; // Use shell-quote to properly escape the command string, preventing command injection
const bashWrapped = `bash -lc ${(0, shell_quote_1.quote)([inline])}`;
return await cloud_runner_system_1.CloudRunnerSystem.Run(bashWrapped); return await cloud_runner_system_1.CloudRunnerSystem.Run(bashWrapped);
} }
return await cloud_runner_system_1.CloudRunnerSystem.Run(commands); return await cloud_runner_system_1.CloudRunnerSystem.Run(commands);
@ -298783,6 +298784,279 @@ module.exports = (string = '') => {
module.exports = /^#!(.*)/; module.exports = /^#!(.*)/;
/***/ }),
/***/ 87029:
/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
"use strict";
exports.quote = __nccwpck_require__(43730);
exports.parse = __nccwpck_require__(40277);
/***/ }),
/***/ 40277:
/***/ ((module) => {
"use strict";
// '<(' is process substitution operator and
// can be parsed the same as control operator
var CONTROL = '(?:' + [
'\\|\\|',
'\\&\\&',
';;',
'\\|\\&',
'\\<\\(',
'\\<\\<\\<',
'>>',
'>\\&',
'<\\&',
'[&;()|<>]'
].join('|') + ')';
var controlRE = new RegExp('^' + CONTROL + '$');
var META = '|&;()<> \\t';
var SINGLE_QUOTE = '"((\\\\"|[^"])*?)"';
var DOUBLE_QUOTE = '\'((\\\\\'|[^\'])*?)\'';
var hash = /^#$/;
var SQ = "'";
var DQ = '"';
var DS = '$';
var TOKEN = '';
var mult = 0x100000000; // Math.pow(16, 8);
for (var i = 0; i < 4; i++) {
TOKEN += (mult * Math.random()).toString(16);
}
var startsWithToken = new RegExp('^' + TOKEN);
function matchAll(s, r) {
var origIndex = r.lastIndex;
var matches = [];
var matchObj;
while ((matchObj = r.exec(s))) {
matches.push(matchObj);
if (r.lastIndex === matchObj.index) {
r.lastIndex += 1;
}
}
r.lastIndex = origIndex;
return matches;
}
function getVar(env, pre, key) {
var r = typeof env === 'function' ? env(key) : env[key];
if (typeof r === 'undefined' && key != '') {
r = '';
} else if (typeof r === 'undefined') {
r = '$';
}
if (typeof r === 'object') {
return pre + TOKEN + JSON.stringify(r) + TOKEN;
}
return pre + r;
}
function parseInternal(string, env, opts) {
if (!opts) {
opts = {};
}
var BS = opts.escape || '\\';
var BAREWORD = '(\\' + BS + '[\'"' + META + ']|[^\\s\'"' + META + '])+';
var chunker = new RegExp([
'(' + CONTROL + ')', // control chars
'(' + BAREWORD + '|' + SINGLE_QUOTE + '|' + DOUBLE_QUOTE + ')+'
].join('|'), 'g');
var matches = matchAll(string, chunker);
if (matches.length === 0) {
return [];
}
if (!env) {
env = {};
}
var commented = false;
return matches.map(function (match) {
var s = match[0];
if (!s || commented) {
return void undefined;
}
if (controlRE.test(s)) {
return { op: s };
}
// Hand-written scanner/parser for Bash quoting rules:
//
// 1. inside single quotes, all characters are printed literally.
// 2. inside double quotes, all characters are printed literally
// except variables prefixed by '$' and backslashes followed by
// either a double quote or another backslash.
// 3. outside of any quotes, backslashes are treated as escape
// characters and not printed (unless they are themselves escaped)
// 4. quote context can switch mid-token if there is no whitespace
// between the two quote contexts (e.g. all'one'"token" parses as
// "allonetoken")
var quote = false;
var esc = false;
var out = '';
var isGlob = false;
var i;
function parseEnvVar() {
i += 1;
var varend;
var varname;
var char = s.charAt(i);
if (char === '{') {
i += 1;
if (s.charAt(i) === '}') {
throw new Error('Bad substitution: ' + s.slice(i - 2, i + 1));
}
varend = s.indexOf('}', i);
if (varend < 0) {
throw new Error('Bad substitution: ' + s.slice(i));
}
varname = s.slice(i, varend);
i = varend;
} else if ((/[*@#?$!_-]/).test(char)) {
varname = char;
i += 1;
} else {
var slicedFromI = s.slice(i);
varend = slicedFromI.match(/[^\w\d_]/);
if (!varend) {
varname = slicedFromI;
i = s.length;
} else {
varname = slicedFromI.slice(0, varend.index);
i += varend.index - 1;
}
}
return getVar(env, '', varname);
}
for (i = 0; i < s.length; i++) {
var c = s.charAt(i);
isGlob = isGlob || (!quote && (c === '*' || c === '?'));
if (esc) {
out += c;
esc = false;
} else if (quote) {
if (c === quote) {
quote = false;
} else if (quote == SQ) {
out += c;
} else { // Double quote
if (c === BS) {
i += 1;
c = s.charAt(i);
if (c === DQ || c === BS || c === DS) {
out += c;
} else {
out += BS + c;
}
} else if (c === DS) {
out += parseEnvVar();
} else {
out += c;
}
}
} else if (c === DQ || c === SQ) {
quote = c;
} else if (controlRE.test(c)) {
return { op: s };
} else if (hash.test(c)) {
commented = true;
var commentObj = { comment: string.slice(match.index + i + 1) };
if (out.length) {
return [out, commentObj];
}
return [commentObj];
} else if (c === BS) {
esc = true;
} else if (c === DS) {
out += parseEnvVar();
} else {
out += c;
}
}
if (isGlob) {
return { op: 'glob', pattern: out };
}
return out;
}).reduce(function (prev, arg) { // finalize parsed arguments
// TODO: replace this whole reduce with a concat
return typeof arg === 'undefined' ? prev : prev.concat(arg);
}, []);
}
module.exports = function parse(s, env, opts) {
var mapped = parseInternal(s, env, opts);
if (typeof env !== 'function') {
return mapped;
}
return mapped.reduce(function (acc, s) {
if (typeof s === 'object') {
return acc.concat(s);
}
var xs = s.split(RegExp('(' + TOKEN + '.*?' + TOKEN + ')', 'g'));
if (xs.length === 1) {
return acc.concat(xs[0]);
}
return acc.concat(xs.filter(Boolean).map(function (x) {
if (startsWithToken.test(x)) {
return JSON.parse(x.split(TOKEN)[1]);
}
return x;
}));
}, []);
};
/***/ }),
/***/ 43730:
/***/ ((module) => {
"use strict";
module.exports = function quote(xs) {
return xs.map(function (s) {
if (s === '') {
return '\'\'';
}
if (s && typeof s === 'object') {
return s.op.replace(/(.)/g, '\\$1');
}
if ((/["\s\\]/).test(s) && !(/'/).test(s)) {
return "'" + s.replace(/(['])/g, '\\$1') + "'";
}
if ((/["'\s]/).test(s)) {
return '"' + s.replace(/(["\\$`!])/g, '\\$1') + '"';
}
return String(s).replace(/([A-Za-z]:)?([#!"$&'()*,:;<=>?@[\\\]^`{|}])/g, '$1\\$2');
}).join(' ');
};
/***/ }), /***/ }),
/***/ 45123: /***/ 45123:

2
dist/index.js.map vendored

File diff suppressed because one or more lines are too long

27
dist/licenses.txt vendored
View File

@ -19407,6 +19407,33 @@ The above copyright notice and this permission notice shall be included in all c
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
shell-quote
MIT
The MIT License
Copyright (c) 2013 James Halliday (mail@substack.net)
Permission is hereby granted, free of charge,
to any person obtaining a copy of this software and
associated documentation files (the "Software"), to
deal in the Software without restriction, including
without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom
the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
shelljs shelljs
BSD-3-Clause BSD-3-Clause
Copyright (c) 2012, Artur Adib <arturadib@gmail.com> Copyright (c) 2012, Artur Adib <arturadib@gmail.com>

View File

@ -9,5 +9,6 @@
"noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */, "noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */,
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
}, },
"include": ["src/**/*", "types/**/*"],
"exclude": ["node_modules", "dist"] "exclude": ["node_modules", "dist"]
} }

16
types/shell-quote.d.ts vendored 100644
View File

@ -0,0 +1,16 @@
declare module 'shell-quote' {
/**
* Quote an array of strings to be safe to use as shell arguments.
* @param args - Array of strings to quote
* @returns A properly escaped string for shell usage
*/
export function quote(args: string[]): string;
/**
* Parse a shell command string into an array of arguments.
* @param cmd - The command string to parse
* @returns Array of parsed arguments
*/
export function parse(cmd: string): string[];
}