Compare commits
18
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a946a3acf5 | ||
|
|
6ecda1d8ea | ||
|
|
d21e5b430e | ||
|
|
d8d2e74810 | ||
|
|
5f5708a2b8 | ||
|
|
f8e1cae677 | ||
|
|
996cc75daf | ||
|
|
adf5e34937 | ||
|
|
4041f8648c | ||
|
|
1f60eaf940 | ||
|
|
c3d8e2ab20 | ||
|
|
3f829eef9e | ||
|
|
011ffb284e | ||
|
|
0951cc73e4 | ||
|
|
15e808935c | ||
|
|
ad9cb43c31 | ||
|
|
2934de33f8 | ||
|
|
ea25fd1b3e |
@@ -0,0 +1,28 @@
|
|||||||
|
version: 2
|
||||||
|
|
||||||
|
updates:
|
||||||
|
# Group updates into a single PR per workspace package
|
||||||
|
- package-ecosystem: npm
|
||||||
|
directory: "/packages/docker"
|
||||||
|
schedule:
|
||||||
|
interval: weekly
|
||||||
|
groups:
|
||||||
|
all-dependencies:
|
||||||
|
patterns:
|
||||||
|
- "*"
|
||||||
|
- package-ecosystem: npm
|
||||||
|
directory: "/packages/hooklib"
|
||||||
|
schedule:
|
||||||
|
interval: weekly
|
||||||
|
groups:
|
||||||
|
all-dependencies:
|
||||||
|
patterns:
|
||||||
|
- "*"
|
||||||
|
- package-ecosystem: npm
|
||||||
|
directory: "/packages/k8s"
|
||||||
|
schedule:
|
||||||
|
interval: weekly
|
||||||
|
groups:
|
||||||
|
all-dependencies:
|
||||||
|
patterns:
|
||||||
|
- "*"
|
||||||
@@ -13,7 +13,13 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v5
|
||||||
- run: npm install
|
- uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version: 24
|
||||||
|
cache: npm
|
||||||
|
- run: node --version && npm --version
|
||||||
|
name: Log Node and npm versions
|
||||||
|
- run: npm ci
|
||||||
name: Install dependencies
|
name: Install dependencies
|
||||||
- run: npm run bootstrap
|
- run: npm run bootstrap
|
||||||
name: Bootstrap the packages
|
name: Bootstrap the packages
|
||||||
@@ -32,7 +38,13 @@ jobs:
|
|||||||
needs: format-and-lint
|
needs: format-and-lint
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v5
|
||||||
- run: npm install
|
- uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version: 24
|
||||||
|
cache: npm
|
||||||
|
- run: node --version && npm --version
|
||||||
|
name: Log Node and npm versions
|
||||||
|
- run: npm ci
|
||||||
name: Install dependencies
|
name: Install dependencies
|
||||||
- run: npm run bootstrap
|
- run: npm run bootstrap
|
||||||
name: Bootstrap the packages
|
name: Bootstrap the packages
|
||||||
@@ -47,12 +59,18 @@ jobs:
|
|||||||
needs: format-and-lint
|
needs: format-and-lint
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v5
|
||||||
|
- uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version: 24
|
||||||
|
cache: npm
|
||||||
- run: sed -i "s|{{PATHTOREPO}}|$(pwd)|" packages/k8s/tests/test-kind.yaml
|
- run: sed -i "s|{{PATHTOREPO}}|$(pwd)|" packages/k8s/tests/test-kind.yaml
|
||||||
name: Setup kind cluster yaml config
|
name: Setup kind cluster yaml config
|
||||||
- uses: helm/[email protected]
|
- uses: helm/[email protected]
|
||||||
with:
|
with:
|
||||||
config: packages/k8s/tests/test-kind.yaml
|
config: packages/k8s/tests/test-kind.yaml
|
||||||
- run: npm install
|
- run: node --version && npm --version
|
||||||
|
name: Log Node and npm versions
|
||||||
|
- run: npm ci
|
||||||
name: Install dependencies
|
name: Install dependencies
|
||||||
- run: npm run bootstrap
|
- run: npm run bootstrap
|
||||||
name: Bootstrap the packages
|
name: Bootstrap the packages
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v5
|
||||||
|
|
||||||
|
- uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version: 24
|
||||||
|
cache: npm
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm install
|
run: npm install
|
||||||
|
|
||||||
|
|||||||
Generated
+13
-5
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "hooks",
|
"name": "hooks",
|
||||||
"version": "0.8.0",
|
"version": "0.8.1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "hooks",
|
"name": "hooks",
|
||||||
"version": "0.8.0",
|
"version": "0.8.1",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"eslint-plugin-jest": "^29.0.1"
|
"eslint-plugin-jest": "^29.0.1"
|
||||||
@@ -560,6 +560,7 @@
|
|||||||
"integrity": "sha512-HC3y9CVuevvWCl/oyZuI47dOeDF9ztdMEfMH8/DW/Mhwa9cCLnK1oD7JoTVGW/u7kFzNZUKUoyJEqkaJh5y3Wg==",
|
"integrity": "sha512-HC3y9CVuevvWCl/oyZuI47dOeDF9ztdMEfMH8/DW/Mhwa9cCLnK1oD7JoTVGW/u7kFzNZUKUoyJEqkaJh5y3Wg==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/regexpp": "^4.10.0",
|
"@eslint-community/regexpp": "^4.10.0",
|
||||||
"@typescript-eslint/scope-manager": "8.45.0",
|
"@typescript-eslint/scope-manager": "8.45.0",
|
||||||
@@ -590,6 +591,7 @@
|
|||||||
"integrity": "sha512-TGf22kon8KW+DeKaUmOibKWktRY8b2NSAZNdtWh798COm1NWx8+xJ6iFBtk3IvLdv6+LGLJLRlyhrhEDZWargQ==",
|
"integrity": "sha512-TGf22kon8KW+DeKaUmOibKWktRY8b2NSAZNdtWh798COm1NWx8+xJ6iFBtk3IvLdv6+LGLJLRlyhrhEDZWargQ==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/scope-manager": "8.45.0",
|
"@typescript-eslint/scope-manager": "8.45.0",
|
||||||
"@typescript-eslint/types": "8.45.0",
|
"@typescript-eslint/types": "8.45.0",
|
||||||
@@ -786,6 +788,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"acorn": "bin/acorn"
|
"acorn": "bin/acorn"
|
||||||
},
|
},
|
||||||
@@ -1081,6 +1084,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"baseline-browser-mapping": "^2.8.9",
|
"baseline-browser-mapping": "^2.8.9",
|
||||||
"caniuse-lite": "^1.0.30001746",
|
"caniuse-lite": "^1.0.30001746",
|
||||||
@@ -1583,6 +1587,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.36.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.36.0.tgz",
|
||||||
"integrity": "sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ==",
|
"integrity": "sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.8.0",
|
"@eslint-community/eslint-utils": "^4.8.0",
|
||||||
"@eslint-community/regexpp": "^4.12.1",
|
"@eslint-community/regexpp": "^4.12.1",
|
||||||
@@ -1644,6 +1649,7 @@
|
|||||||
"integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==",
|
"integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"eslint-config-prettier": "bin/cli.js"
|
"eslint-config-prettier": "bin/cli.js"
|
||||||
},
|
},
|
||||||
@@ -3165,9 +3171,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/js-yaml": {
|
"node_modules/js-yaml": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
|
||||||
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
|
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"argparse": "^2.0.1"
|
"argparse": "^2.0.1"
|
||||||
@@ -3646,6 +3652,7 @@
|
|||||||
"integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
|
"integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"prettier": "bin/prettier.cjs"
|
"prettier": "bin/prettier.cjs"
|
||||||
},
|
},
|
||||||
@@ -4380,6 +4387,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
|
|||||||
+3
-2
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "hooks",
|
"name": "hooks",
|
||||||
"version": "0.8.0",
|
"version": "0.8.1",
|
||||||
"description": "Three projects are included - k8s: a kubernetes hook implementation that spins up pods dynamically to run a job - docker: A hook implementation of the runner's docker implementation - A hook lib, which contains shared typescript definitions and utilities that the other packages consume",
|
"description": "Three projects are included - k8s: a kubernetes hook implementation that spins up pods dynamically to run a job - docker: A hook implementation of the runner's docker implementation - A hook lib, which contains shared typescript definitions and utilities that the other packages consume",
|
||||||
"main": "",
|
"main": "",
|
||||||
"directories": {
|
"directories": {
|
||||||
@@ -8,10 +8,11 @@
|
|||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "npm run test --prefix packages/docker && npm run test --prefix packages/k8s",
|
"test": "npm run test --prefix packages/docker && npm run test --prefix packages/k8s",
|
||||||
"bootstrap": "npm install --prefix packages/hooklib && npm install --prefix packages/k8s && npm install --prefix packages/docker",
|
"bootstrap": "npm ci --prefix packages/hooklib && npm ci --prefix packages/k8s && npm ci --prefix packages/docker",
|
||||||
"format": "prettier --write '**/*.ts'",
|
"format": "prettier --write '**/*.ts'",
|
||||||
"format-check": "prettier --check '**/*.ts'",
|
"format-check": "prettier --check '**/*.ts'",
|
||||||
"lint": "eslint packages/**/*.ts",
|
"lint": "eslint packages/**/*.ts",
|
||||||
|
"lint:fix": "eslint packages/**/*.ts --fix",
|
||||||
"build-all": "npm run build --prefix packages/hooklib && npm run build --prefix packages/k8s && npm run build --prefix packages/docker"
|
"build-all": "npm run build --prefix packages/hooklib && npm run build --prefix packages/k8s && npm run build --prefix packages/docker"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
Generated
+570
-716
File diff suppressed because it is too large
Load Diff
@@ -13,8 +13,8 @@
|
|||||||
"author": "",
|
"author": "",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/core": "^1.11.1",
|
"@actions/core": "^2.0.2",
|
||||||
"@actions/exec": "^1.1.1",
|
"@actions/exec": "^2.0.0",
|
||||||
"hooklib": "file:../hooklib",
|
"hooklib": "file:../hooklib",
|
||||||
"shlex": "^3.0.0",
|
"shlex": "^3.0.0",
|
||||||
"uuid": "^11.1.0"
|
"uuid": "^11.1.0"
|
||||||
|
|||||||
Generated
+245
-429
File diff suppressed because it is too large
Load Diff
@@ -22,6 +22,6 @@
|
|||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/core": "^1.11.1"
|
"@actions/core": "^2.0.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,3 +41,4 @@ rules:
|
|||||||
- Container actions will not have access to the services network or job container network
|
- Container actions will not have access to the services network or job container network
|
||||||
- Docker [create options](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idcontaineroptions) are not supported
|
- Docker [create options](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idcontaineroptions) are not supported
|
||||||
- Container actions will have to specify the entrypoint, since the default entrypoint will be overridden to run the commands from the workflow.
|
- Container actions will have to specify the entrypoint, since the default entrypoint will be overridden to run the commands from the workflow.
|
||||||
|
- Container actions need to have the following binaries in their container image: `sh`, `env`, `tail`.
|
||||||
|
|||||||
Generated
+459
-460
File diff suppressed because it is too large
Load Diff
@@ -13,9 +13,9 @@
|
|||||||
"author": "",
|
"author": "",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/core": "^1.11.1",
|
"@actions/core": "^2.0.2",
|
||||||
"@actions/exec": "^1.1.1",
|
"@actions/exec": "^2.0.0",
|
||||||
"@actions/io": "^1.1.3",
|
"@actions/io": "^2.0.0",
|
||||||
"@kubernetes/client-node": "^1.3.0",
|
"@kubernetes/client-node": "^1.3.0",
|
||||||
"hooklib": "file:../hooklib",
|
"hooklib": "file:../hooklib",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ export async function runContainerStep(
|
|||||||
try {
|
try {
|
||||||
core.debug(`Executing container step script in pod ${podName}`)
|
core.debug(`Executing container step script in pod ${podName}`)
|
||||||
return await execPodStep(
|
return await execPodStep(
|
||||||
['/__e/sh', '-e', containerPath],
|
['sh', '-e', containerPath],
|
||||||
pod.metadata.name,
|
pod.metadata.name,
|
||||||
JOB_CONTAINER_NAME
|
JOB_CONTAINER_NAME
|
||||||
)
|
)
|
||||||
@@ -133,7 +133,7 @@ function createContainerSpec(
|
|||||||
podContainer.name = JOB_CONTAINER_NAME
|
podContainer.name = JOB_CONTAINER_NAME
|
||||||
podContainer.image = container.image
|
podContainer.image = container.image
|
||||||
podContainer.workingDir = '/__w'
|
podContainer.workingDir = '/__w'
|
||||||
podContainer.command = ['/__e/tail']
|
podContainer.command = ['tail']
|
||||||
podContainer.args = DEFAULT_CONTAINER_ENTRY_POINT_ARGS
|
podContainer.args = DEFAULT_CONTAINER_ENTRY_POINT_ARGS
|
||||||
|
|
||||||
podContainer.volumeMounts = CONTAINER_VOLUMES
|
podContainer.volumeMounts = CONTAINER_VOLUMES
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { execCpFromPod, execCpToPod, execPodStep } from '../k8s'
|
|||||||
import { writeRunScript, sleep, listDirAllCommand } from '../k8s/utils'
|
import { writeRunScript, sleep, listDirAllCommand } from '../k8s/utils'
|
||||||
import { JOB_CONTAINER_NAME } from './constants'
|
import { JOB_CONTAINER_NAME } from './constants'
|
||||||
import { dirname } from 'path'
|
import { dirname } from 'path'
|
||||||
|
import * as shlex from 'shlex'
|
||||||
|
|
||||||
export async function runScriptStep(
|
export async function runScriptStep(
|
||||||
args: RunScriptStepArgs,
|
args: RunScriptStepArgs,
|
||||||
@@ -22,9 +23,53 @@ export async function runScriptStep(
|
|||||||
)
|
)
|
||||||
|
|
||||||
const workdir = dirname(process.env.RUNNER_WORKSPACE as string)
|
const workdir = dirname(process.env.RUNNER_WORKSPACE as string)
|
||||||
const containerTemp = '/__w/_temp'
|
|
||||||
const runnerTemp = `${workdir}/_temp`
|
const runnerTemp = `${workdir}/_temp`
|
||||||
await execCpToPod(state.jobPod, runnerTemp, containerTemp)
|
const containerTemp = '/__w/_temp'
|
||||||
|
const containerTempSrc = '/__w/_temp_pre'
|
||||||
|
// Ensure base and staging dirs exist before copying
|
||||||
|
await execPodStep(
|
||||||
|
[
|
||||||
|
'sh',
|
||||||
|
'-c',
|
||||||
|
'mkdir -p /__w && mkdir -p /__w/_temp && mkdir -p /__w/_temp_pre'
|
||||||
|
],
|
||||||
|
state.jobPod,
|
||||||
|
JOB_CONTAINER_NAME
|
||||||
|
)
|
||||||
|
await execCpToPod(state.jobPod, runnerTemp, containerTempSrc)
|
||||||
|
|
||||||
|
// Copy GitHub directories from temp to /github
|
||||||
|
// Merge strategy:
|
||||||
|
// - Overwrite files in _runner_file_commands
|
||||||
|
// - Append files not already present elsewhere
|
||||||
|
const mergeCommands = [
|
||||||
|
'set -e',
|
||||||
|
'mkdir -p /__w/_temp /__w/_temp_pre',
|
||||||
|
'SRC=/__w/_temp_pre',
|
||||||
|
'DST=/__w/_temp',
|
||||||
|
// Overwrite _runner_file_commands
|
||||||
|
'cp -a "$SRC/_runner_file_commands/." "$DST/_runner_file_commands"',
|
||||||
|
`find "$SRC" -type f ! -path "*/_runner_file_commands/*" -exec sh -c '
|
||||||
|
rel="\${1#$2/}"
|
||||||
|
target="$3/$rel"
|
||||||
|
mkdir -p "$(dirname "$target")"
|
||||||
|
cp -a "$1" "$target"
|
||||||
|
' _ {} "$SRC" "$DST" \\;`,
|
||||||
|
// Remove _temp_pre after merging
|
||||||
|
'rm -rf /__w/_temp_pre'
|
||||||
|
]
|
||||||
|
|
||||||
|
try {
|
||||||
|
await execPodStep(
|
||||||
|
['sh', '-c', mergeCommands.join(' && ')],
|
||||||
|
state.jobPod,
|
||||||
|
JOB_CONTAINER_NAME
|
||||||
|
)
|
||||||
|
} catch (err) {
|
||||||
|
core.debug(`Failed to merge temp directories: ${JSON.stringify(err)}`)
|
||||||
|
const message = (err as any)?.response?.body?.message || err
|
||||||
|
throw new Error(`failed to merge temp dirs: ${message}`)
|
||||||
|
}
|
||||||
|
|
||||||
// Execute the entrypoint script
|
// Execute the entrypoint script
|
||||||
args.entryPoint = 'sh'
|
args.entryPoint = 'sh'
|
||||||
@@ -51,7 +96,11 @@ export async function runScriptStep(
|
|||||||
core.debug(
|
core.debug(
|
||||||
`Copying from job pod '${state.jobPod}' ${containerTemp} to ${runnerTemp}`
|
`Copying from job pod '${state.jobPod}' ${containerTemp} to ${runnerTemp}`
|
||||||
)
|
)
|
||||||
await execCpFromPod(state.jobPod, containerTemp, workdir)
|
await execCpFromPod(
|
||||||
|
state.jobPod,
|
||||||
|
`${containerTemp}/_runner_file_commands`,
|
||||||
|
`${workdir}/_temp`
|
||||||
|
)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
core.warning('Failed to copy _temp from pod')
|
core.warning('Failed to copy _temp from pod')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,8 +20,10 @@ import {
|
|||||||
listDirAllCommand,
|
listDirAllCommand,
|
||||||
sleep,
|
sleep,
|
||||||
EXTERNALS_VOLUME_NAME,
|
EXTERNALS_VOLUME_NAME,
|
||||||
GITHUB_VOLUME_NAME
|
GITHUB_VOLUME_NAME,
|
||||||
|
WORK_VOLUME
|
||||||
} from './utils'
|
} from './utils'
|
||||||
|
import * as shlex from 'shlex'
|
||||||
|
|
||||||
const kc = new k8s.KubeConfig()
|
const kc = new k8s.KubeConfig()
|
||||||
|
|
||||||
@@ -91,13 +93,33 @@ export async function createJobPod(
|
|||||||
|
|
||||||
appPod.spec = new k8s.V1PodSpec()
|
appPod.spec = new k8s.V1PodSpec()
|
||||||
appPod.spec.containers = containers
|
appPod.spec.containers = containers
|
||||||
|
appPod.spec.securityContext = {
|
||||||
|
fsGroup: 1001
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract working directory from GITHUB_WORKSPACE
|
||||||
|
// GITHUB_WORKSPACE is like /__w/repo-name/repo-name
|
||||||
|
const githubWorkspace = process.env.GITHUB_WORKSPACE
|
||||||
|
const workingDirPath = githubWorkspace?.split('/').slice(-2).join('/') ?? ''
|
||||||
|
|
||||||
|
const initCommands = [
|
||||||
|
'mkdir -p /mnt/externals',
|
||||||
|
'mkdir -p /mnt/work',
|
||||||
|
'mkdir -p /mnt/github',
|
||||||
|
'mv /home/runner/externals/* /mnt/externals/'
|
||||||
|
]
|
||||||
|
|
||||||
|
if (workingDirPath) {
|
||||||
|
initCommands.push(`mkdir -p /mnt/work/${workingDirPath}`)
|
||||||
|
}
|
||||||
|
|
||||||
appPod.spec.initContainers = [
|
appPod.spec.initContainers = [
|
||||||
{
|
{
|
||||||
name: 'fs-init',
|
name: 'fs-init',
|
||||||
image:
|
image:
|
||||||
process.env.ACTIONS_RUNNER_IMAGE ||
|
process.env.ACTIONS_RUNNER_IMAGE ||
|
||||||
'ghcr.io/actions/actions-runner:latest',
|
'ghcr.io/actions/actions-runner:latest',
|
||||||
command: ['sh', '-c', 'sudo mv /home/runner/externals/* /mnt/externals'],
|
command: ['sh', '-c', initCommands.join(' && ')],
|
||||||
securityContext: {
|
securityContext: {
|
||||||
runAsGroup: 1001,
|
runAsGroup: 1001,
|
||||||
runAsUser: 1001
|
runAsUser: 1001
|
||||||
@@ -106,6 +128,14 @@ export async function createJobPod(
|
|||||||
{
|
{
|
||||||
name: EXTERNALS_VOLUME_NAME,
|
name: EXTERNALS_VOLUME_NAME,
|
||||||
mountPath: '/mnt/externals'
|
mountPath: '/mnt/externals'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: WORK_VOLUME,
|
||||||
|
mountPath: '/mnt/work'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: GITHUB_VOLUME_NAME,
|
||||||
|
mountPath: '/mnt/github'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -121,6 +151,10 @@ export async function createJobPod(
|
|||||||
{
|
{
|
||||||
name: GITHUB_VOLUME_NAME,
|
name: GITHUB_VOLUME_NAME,
|
||||||
emptyDir: {}
|
emptyDir: {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: WORK_VOLUME,
|
||||||
|
emptyDir: {}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -169,33 +203,6 @@ export async function createContainerStepPod(
|
|||||||
|
|
||||||
appPod.spec = new k8s.V1PodSpec()
|
appPod.spec = new k8s.V1PodSpec()
|
||||||
appPod.spec.containers = [container]
|
appPod.spec.containers = [container]
|
||||||
appPod.spec.initContainers = [
|
|
||||||
{
|
|
||||||
name: 'fs-init',
|
|
||||||
image:
|
|
||||||
process.env.ACTIONS_RUNNER_IMAGE ||
|
|
||||||
'ghcr.io/actions/actions-runner:latest',
|
|
||||||
command: [
|
|
||||||
'bash',
|
|
||||||
'-c',
|
|
||||||
`sudo cp $(which sh) /mnt/externals/sh \
|
|
||||||
&& sudo cp $(which tail) /mnt/externals/tail \
|
|
||||||
&& sudo cp $(which env) /mnt/externals/env \
|
|
||||||
&& sudo chmod -R 777 /mnt/externals`
|
|
||||||
],
|
|
||||||
securityContext: {
|
|
||||||
runAsGroup: 1001,
|
|
||||||
runAsUser: 1001,
|
|
||||||
privileged: true
|
|
||||||
},
|
|
||||||
volumeMounts: [
|
|
||||||
{
|
|
||||||
name: EXTERNALS_VOLUME_NAME,
|
|
||||||
mountPath: '/mnt/externals'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
appPod.spec.restartPolicy = 'Never'
|
appPod.spec.restartPolicy = 'Never'
|
||||||
|
|
||||||
@@ -207,6 +214,10 @@ export async function createContainerStepPod(
|
|||||||
{
|
{
|
||||||
name: GITHUB_VOLUME_NAME,
|
name: GITHUB_VOLUME_NAME,
|
||||||
emptyDir: {}
|
emptyDir: {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: WORK_VOLUME,
|
||||||
|
emptyDir: {}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -271,19 +282,18 @@ export async function execPodStep(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function execCalculateOutputHash(
|
export async function execCalculateOutputHashSorted(
|
||||||
podName: string,
|
podName: string,
|
||||||
containerName: string,
|
containerName: string,
|
||||||
command: string[]
|
command: string[]
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
const exec = new k8s.Exec(kc)
|
const exec = new k8s.Exec(kc)
|
||||||
|
|
||||||
// Create a writable stream that updates a SHA-256 hash with stdout data
|
let output = ''
|
||||||
const hash = createHash('sha256')
|
const outputWriter = new stream.Writable({
|
||||||
const hashWriter = new stream.Writable({
|
|
||||||
write(chunk, _enc, cb) {
|
write(chunk, _enc, cb) {
|
||||||
try {
|
try {
|
||||||
hash.update(chunk.toString('utf8') as Buffer)
|
output += chunk.toString('utf8')
|
||||||
cb()
|
cb()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
cb(e as Error)
|
cb(e as Error)
|
||||||
@@ -298,7 +308,7 @@ export async function execCalculateOutputHash(
|
|||||||
podName,
|
podName,
|
||||||
containerName,
|
containerName,
|
||||||
command,
|
command,
|
||||||
hashWriter, // capture stdout for hashing
|
outputWriter, // capture stdout
|
||||||
process.stderr,
|
process.stderr,
|
||||||
null,
|
null,
|
||||||
false /* tty */,
|
false /* tty */,
|
||||||
@@ -320,27 +330,46 @@ export async function execCalculateOutputHash(
|
|||||||
.catch(e => reject(e))
|
.catch(e => reject(e))
|
||||||
})
|
})
|
||||||
|
|
||||||
// finalize hash and return digest
|
outputWriter.end()
|
||||||
hashWriter.end()
|
|
||||||
|
|
||||||
|
// Sort lines for consistent ordering across platforms
|
||||||
|
const sortedOutput =
|
||||||
|
output
|
||||||
|
.split('\n')
|
||||||
|
.filter(line => line.length > 0)
|
||||||
|
.sort()
|
||||||
|
.join('\n') + '\n'
|
||||||
|
|
||||||
|
const hash = createHash('sha256')
|
||||||
|
hash.update(sortedOutput)
|
||||||
return hash.digest('hex')
|
return hash.digest('hex')
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function localCalculateOutputHash(
|
export async function localCalculateOutputHashSorted(
|
||||||
commands: string[]
|
commands: string[]
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
return await new Promise<string>((resolve, reject) => {
|
return await new Promise<string>((resolve, reject) => {
|
||||||
const hash = createHash('sha256')
|
|
||||||
const child = spawn(commands[0], commands.slice(1), {
|
const child = spawn(commands[0], commands.slice(1), {
|
||||||
stdio: ['ignore', 'pipe', 'ignore']
|
stdio: ['ignore', 'pipe', 'ignore']
|
||||||
})
|
})
|
||||||
|
|
||||||
|
let output = ''
|
||||||
child.stdout.on('data', chunk => {
|
child.stdout.on('data', chunk => {
|
||||||
hash.update(chunk)
|
output += chunk.toString('utf8')
|
||||||
})
|
})
|
||||||
child.on('error', reject)
|
child.on('error', reject)
|
||||||
child.on('close', (code: number) => {
|
child.on('close', (code: number) => {
|
||||||
if (code === 0) {
|
if (code === 0) {
|
||||||
|
// Sort lines for consistent ordering across distributions/platforms
|
||||||
|
const sortedOutput =
|
||||||
|
output
|
||||||
|
.split('\n')
|
||||||
|
.filter(line => line.length > 0)
|
||||||
|
.sort()
|
||||||
|
.join('\n') + '\n'
|
||||||
|
|
||||||
|
const hash = createHash('sha256')
|
||||||
|
hash.update(sortedOutput)
|
||||||
resolve(hash.digest('hex'))
|
resolve(hash.digest('hex'))
|
||||||
} else {
|
} else {
|
||||||
reject(new Error(`child process exited with code ${code}`))
|
reject(new Error(`child process exited with code ${code}`))
|
||||||
@@ -360,7 +389,15 @@ export async function execCpToPod(
|
|||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
const exec = new k8s.Exec(kc)
|
const exec = new k8s.Exec(kc)
|
||||||
const command = ['tar', 'xf', '-', '-C', containerPath]
|
// Use tar to extract with --no-same-owner to avoid ownership issues.
|
||||||
|
// Then use find to fix permissions. The -m flag helps but we also need to fix permissions after.
|
||||||
|
const command = [
|
||||||
|
'sh',
|
||||||
|
'-c',
|
||||||
|
`tar xf - --no-same-owner -C ${shlex.quote(containerPath)} 2>/dev/null; ` +
|
||||||
|
`find ${shlex.quote(containerPath)} -type f -exec chmod u+rw {} \\; 2>/dev/null; ` +
|
||||||
|
`find ${shlex.quote(containerPath)} -type d -exec chmod u+rwx {} \\; 2>/dev/null`
|
||||||
|
]
|
||||||
const readStream = tar.pack(runnerPath)
|
const readStream = tar.pack(runnerPath)
|
||||||
const errStream = new WritableStreamBuffer()
|
const errStream = new WritableStreamBuffer()
|
||||||
await new Promise((resolve, reject) => {
|
await new Promise((resolve, reject) => {
|
||||||
@@ -378,7 +415,7 @@ export async function execCpToPod(
|
|||||||
if (errStream.size()) {
|
if (errStream.size()) {
|
||||||
reject(
|
reject(
|
||||||
new Error(
|
new Error(
|
||||||
`Error from cpFromPod - details: \n ${errStream.getContentsAsString()}`
|
`Error from execCpToPod - status: ${status.status}, details: \n ${errStream.getContentsAsString()}`
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -400,22 +437,22 @@ export async function execCpToPod(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const want = await localCalculateOutputHash([
|
|
||||||
'sh',
|
|
||||||
'-c',
|
|
||||||
listDirAllCommand(runnerPath)
|
|
||||||
])
|
|
||||||
|
|
||||||
let attempts = 15
|
let attempts = 15
|
||||||
const delay = 1000
|
const delay = 1000
|
||||||
for (let i = 0; i < attempts; i++) {
|
for (let i = 0; i < attempts; i++) {
|
||||||
try {
|
try {
|
||||||
const got = await execCalculateOutputHash(podName, JOB_CONTAINER_NAME, [
|
const want = await localCalculateOutputHashSorted([
|
||||||
'sh',
|
'sh',
|
||||||
'-c',
|
'-c',
|
||||||
listDirAllCommand(containerPath)
|
listDirAllCommand(runnerPath)
|
||||||
])
|
])
|
||||||
|
|
||||||
|
const got = await execCalculateOutputHashSorted(
|
||||||
|
podName,
|
||||||
|
JOB_CONTAINER_NAME,
|
||||||
|
['sh', '-c', listDirAllCommand(containerPath)]
|
||||||
|
)
|
||||||
|
|
||||||
if (got !== want) {
|
if (got !== want) {
|
||||||
core.debug(
|
core.debug(
|
||||||
`The hash of the directory does not match the expected value; want='${want}' got='${got}'`
|
`The hash of the directory does not match the expected value; want='${want}' got='${got}'`
|
||||||
@@ -441,11 +478,6 @@ export async function execCpFromPod(
|
|||||||
core.debug(
|
core.debug(
|
||||||
`Copying from pod ${podName} ${containerPath} to ${targetRunnerPath}`
|
`Copying from pod ${podName} ${containerPath} to ${targetRunnerPath}`
|
||||||
)
|
)
|
||||||
const want = await execCalculateOutputHash(podName, JOB_CONTAINER_NAME, [
|
|
||||||
'sh',
|
|
||||||
'-c',
|
|
||||||
listDirAllCommand(containerPath)
|
|
||||||
])
|
|
||||||
|
|
||||||
let attempt = 0
|
let attempt = 0
|
||||||
while (true) {
|
while (true) {
|
||||||
@@ -506,7 +538,13 @@ export async function execCpFromPod(
|
|||||||
const delay = 1000
|
const delay = 1000
|
||||||
for (let i = 0; i < attempts; i++) {
|
for (let i = 0; i < attempts; i++) {
|
||||||
try {
|
try {
|
||||||
const got = await localCalculateOutputHash([
|
const want = await execCalculateOutputHashSorted(
|
||||||
|
podName,
|
||||||
|
JOB_CONTAINER_NAME,
|
||||||
|
['sh', '-c', listDirAllCommand(containerPath)]
|
||||||
|
)
|
||||||
|
|
||||||
|
const got = await localCalculateOutputHashSorted([
|
||||||
'sh',
|
'sh',
|
||||||
'-c',
|
'-c',
|
||||||
listDirAllCommand(targetRunnerPath)
|
listDirAllCommand(targetRunnerPath)
|
||||||
@@ -793,7 +831,7 @@ export async function isPodContainerAlpine(
|
|||||||
[
|
[
|
||||||
'sh',
|
'sh',
|
||||||
'-c',
|
'-c',
|
||||||
`'[ $(cat /etc/*release* | grep -i -e "^ID=*alpine*" -c) != 0 ] || exit 1'`
|
`[ $(cat /etc/*release* | grep -i -e "^ID=*alpine*" -c) != 0 ] || exit 1`
|
||||||
],
|
],
|
||||||
podName,
|
podName,
|
||||||
containerName
|
containerName
|
||||||
|
|||||||
@@ -15,12 +15,17 @@ export const ENV_USE_KUBE_SCHEDULER = 'ACTIONS_RUNNER_USE_KUBE_SCHEDULER'
|
|||||||
|
|
||||||
export const EXTERNALS_VOLUME_NAME = 'externals'
|
export const EXTERNALS_VOLUME_NAME = 'externals'
|
||||||
export const GITHUB_VOLUME_NAME = 'github'
|
export const GITHUB_VOLUME_NAME = 'github'
|
||||||
|
export const WORK_VOLUME = 'work'
|
||||||
|
|
||||||
export const CONTAINER_VOLUMES: k8s.V1VolumeMount[] = [
|
export const CONTAINER_VOLUMES: k8s.V1VolumeMount[] = [
|
||||||
{
|
{
|
||||||
name: EXTERNALS_VOLUME_NAME,
|
name: EXTERNALS_VOLUME_NAME,
|
||||||
mountPath: '/__e'
|
mountPath: '/__e'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: WORK_VOLUME,
|
||||||
|
mountPath: '/__w'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: GITHUB_VOLUME_NAME,
|
name: GITHUB_VOLUME_NAME,
|
||||||
mountPath: '/github'
|
mountPath: '/github'
|
||||||
@@ -102,7 +107,7 @@ export function writeContainerStepScript(
|
|||||||
rm "$0" # remove script after running
|
rm "$0" # remove script after running
|
||||||
mv /__w/_temp/_github_home /github/home && \
|
mv /__w/_temp/_github_home /github/home && \
|
||||||
mv /__w/_temp/_github_workflow /github/workflow && \
|
mv /__w/_temp/_github_workflow /github/workflow && \
|
||||||
mv /__w/_temp/_runner_file_commands /github/file_commands && \
|
mv /__w/_temp/_runner_file_commands /github/file_commands || true && \
|
||||||
mv /__w/${parts.join('/')}/ /github/workspace && \
|
mv /__w/${parts.join('/')}/ /github/workspace && \
|
||||||
cd /github/workspace && \
|
cd /github/workspace && \
|
||||||
exec ${environmentPrefix} ${entryPoint} ${
|
exec ${environmentPrefix} ${entryPoint} ${
|
||||||
@@ -283,6 +288,11 @@ function mergeLists<T>(base?: T[], from?: T[]): T[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function fixArgs(args: string[]): string[] {
|
export function fixArgs(args: string[]): string[] {
|
||||||
|
// Preserve shell command strings passed via `sh -c` without re-tokenizing.
|
||||||
|
// Retokenizing would split the script into multiple args, breaking `sh -c`.
|
||||||
|
if (args.length >= 2 && args[0] === 'sh' && args[1] === '-c') {
|
||||||
|
return args
|
||||||
|
}
|
||||||
return shlex.split(args.join(' '))
|
return shlex.split(args.join(' '))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -291,5 +301,5 @@ export async function sleep(ms: number): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function listDirAllCommand(dir: string): string {
|
export function listDirAllCommand(dir: string): string {
|
||||||
return `cd ${shlex.quote(dir)} && find . -not -path '*/_runner_hook_responses*' -exec stat -c '%b %n' {} \\;`
|
return `cd ${shlex.quote(dir)} && find . -type f -not -path '*/_runner_hook_responses*' -exec stat -c '%s %n' {} \\;`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ describe('e2e', () => {
|
|||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
await testHelper.cleanup()
|
await testHelper.cleanup()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should prepare job, run script step, run container step then cleanup without errors', async () => {
|
it('should prepare job, run script step, run container step then cleanup without errors', async () => {
|
||||||
await expect(
|
await expect(
|
||||||
prepareJob(prepareJobData.args, prepareJobOutputFilePath)
|
prepareJob(prepareJobData.args, prepareJobOutputFilePath)
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ describe('Prepare job', () => {
|
|||||||
process.env.GITHUB_WORKSPACE as string,
|
process.env.GITHUB_WORKSPACE as string,
|
||||||
'myvolume'
|
'myvolume'
|
||||||
)
|
)
|
||||||
fs.mkdirSync(userVolumeMount)
|
fs.mkdirSync(userVolumeMount, { recursive: true })
|
||||||
fs.writeFileSync(path.join(userVolumeMount, 'file.txt'), 'hello')
|
fs.writeFileSync(path.join(userVolumeMount, 'file.txt'), 'hello')
|
||||||
prepareJobData.args.container.userMountVolumes = [
|
prepareJobData.args.container.userMountVolumes = [
|
||||||
{
|
{
|
||||||
@@ -63,11 +63,7 @@ describe('Prepare job', () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
await execPodStep(
|
await execPodStep(
|
||||||
[
|
['sh', '-c', '[ "$(cat /__w/myvolume/file.txt)" = "hello" ] || exit 5'],
|
||||||
'sh',
|
|
||||||
'-c',
|
|
||||||
'\'[ "$(cat /__w/myvolume/file.txt)" = "hello" ] || exit 5\''
|
|
||||||
],
|
|
||||||
content!.state!.jobPod,
|
content!.state!.jobPod,
|
||||||
JOB_CONTAINER_NAME
|
JOB_CONTAINER_NAME
|
||||||
).then(output => {
|
).then(output => {
|
||||||
@@ -231,4 +227,20 @@ describe('Prepare job', () => {
|
|||||||
expect(() => content.context.services[0].image).not.toThrow()
|
expect(() => content.context.services[0].image).not.toThrow()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
it('should prepare job with container with non-root user', async () => {
|
||||||
|
prepareJobData.args!.container!.image =
|
||||||
|
'ghcr.io/actions/actions-runner:latest' // known to use user 1001
|
||||||
|
await expect(
|
||||||
|
prepareJob(prepareJobData.args, prepareJobOutputFilePath)
|
||||||
|
).resolves.not.toThrow()
|
||||||
|
|
||||||
|
const content = JSON.parse(
|
||||||
|
fs.readFileSync(prepareJobOutputFilePath).toString()
|
||||||
|
)
|
||||||
|
expect(content.state.jobPod).toBeTruthy()
|
||||||
|
expect(content.context.container.image).toBe(
|
||||||
|
'ghcr.io/actions/actions-runner:latest'
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
+12
-6
@@ -1,15 +1,21 @@
|
|||||||
## Features
|
<!-- ## Features -->
|
||||||
|
|
||||||
- k8s: remove dependency on the runner's volume [#244]
|
|
||||||
|
|
||||||
## Bugs
|
## Bugs
|
||||||
|
|
||||||
- docker: fix readOnly volumes in createContainer [#236]
|
- Change command to remove sudo to fix fs-init initial container [#263]
|
||||||
|
- Sort 'find' output before hashing for consistency [#267]
|
||||||
|
- feat: check if required binaries are present [#272]
|
||||||
|
- Allow non-root container [#264]
|
||||||
|
- Improve validation checks after copying [#285]
|
||||||
|
- Fix workingDir permissions issue by creating it within init container [#283]
|
||||||
|
- Fix event.json not being copied to /github/workflow in kubernetes-novolume mode [#287]
|
||||||
|
- Reduce the amount of data copied to the workflow pod [#293]
|
||||||
|
- Overwrite runner file commands [#298]
|
||||||
|
|
||||||
## Misc
|
## Misc
|
||||||
|
|
||||||
- bump all dependencies [#234] [#240] [#239] [#238]
|
- Dependency updates [#276] [#277] [#278] [#279] [#304]
|
||||||
- bump actions [#254]
|
- Group dependabot updates [#289]
|
||||||
|
|
||||||
## SHA-256 Checksums
|
## SHA-256 Checksums
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user