Compare commits

..

22 Commits

Author SHA1 Message Date
Nikola Jokic
70841b6972 bump docker dep as well 2025-04-16 14:43:46 +02:00
Nikola Jokic
1c2ae5d20a rework 2025-04-16 14:25:04 +02:00
Nikola Jokic
928f63d88a exclude eslint.config.js 2025-04-16 13:42:50 +02:00
Nikola Jokic
3bda7ef21e wip 2025-04-16 10:26:22 +02:00
Nikola Jokic
3b0e87c9a7 fmt 2025-04-15 14:53:53 +02:00
Nikola Jokic
a7349e7d70 fix errors and bump client node to stable version 2025-04-15 14:53:21 +02:00
Nikola Jokic
ff583c8917 bump all dependencies 2025-04-15 14:29:29 +02:00
dependabot[bot]
e47f9b8af4 Bump jsonpath-plus from 10.1.0 to 10.3.0 in /packages/k8s (#213)
Bumps [jsonpath-plus](https://github.com/s3u/JSONPath) from 10.1.0 to 10.3.0.
- [Release notes](https://github.com/s3u/JSONPath/releases)
- [Changelog](https://github.com/JSONPath-Plus/JSONPath/blob/main/CHANGES.md)
- [Commits](https://github.com/s3u/JSONPath/compare/v10.1.0...v10.3.0)

---
updated-dependencies:
- dependency-name: jsonpath-plus
  dependency-version: 10.3.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-15 14:25:32 +02:00
dependabot[bot]
54e14cb7f3 Bump braces from 3.0.2 to 3.0.3 in /packages/hooklib (#194)
Bumps [braces](https://github.com/micromatch/braces) from 3.0.2 to 3.0.3.
- [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/braces/compare/3.0.2...3.0.3)

---
updated-dependencies:
- dependency-name: braces
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-14 14:37:19 +02:00
Grant Buskey
ef2229fc0b feat(k8s): add /github/home to containerAction mounts and surface createSecretForEnvs errors #181 (#198)
* feat: add /github/home to containerAction mounts #181

* fix: add debug logging for failed secret creations #181
2025-04-14 14:12:51 +02:00
Andre Klärner
88dc98f8ef k8s: start logging from the beginning (#184) 2025-04-14 14:03:05 +02:00
Joan Miquel Luque
b388518d40 feat(k8s): Use pod affinity when KubeScheduler is enabled #201 (#212)
Signed-off-by: Joan Miquel Luque Oliver <joan.luque@dynatrace.com>
2025-04-14 13:36:21 +02:00
dependabot[bot]
7afb8f9323 Bump cross-spawn from 7.0.3 to 7.0.6 in /packages/k8s (#196)
Bumps [cross-spawn](https://github.com/moxystudio/node-cross-spawn) from 7.0.3 to 7.0.6.
- [Changelog](https://github.com/moxystudio/node-cross-spawn/blob/master/CHANGELOG.md)
- [Commits](https://github.com/moxystudio/node-cross-spawn/compare/v7.0.3...v7.0.6)

---
updated-dependencies:
- dependency-name: cross-spawn
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-24 16:51:12 +01:00
Robin Bobbitt
d4c5425b22 support alternative network modes (#209) 2025-03-24 16:33:43 +01:00
dependabot[bot]
120636d3d7 Bump ws from 7.5.8 to 7.5.10 in /packages/k8s (#192)
Bumps [ws](https://github.com/websockets/ws) from 7.5.8 to 7.5.10.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/7.5.8...7.5.10)

---
updated-dependencies:
- dependency-name: ws
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-06 11:41:28 -05:00
Josh Gross
5e805a0546 Remove dependency on deprecated release actions (#193)
* Update to the latest available actions

* Remove dependency on deprecated release actions

* Add release workflow fixes from testing
2024-11-06 11:41:09 -05:00
Josh Gross
27bae0b2b7 Update to the latest available actions (#191) 2024-11-06 11:18:49 -05:00
dependabot[bot]
8eed1ad1b6 Bump jsonpath-plus and @kubernetes/client-node in /packages/k8s (#187)
Bumps [jsonpath-plus](https://github.com/s3u/JSONPath) to 10.1.0 and updates ancestor dependency [@kubernetes/client-node](https://github.com/kubernetes-client/javascript). These dependencies need to be updated together.


Updates `jsonpath-plus` from 9.0.0 to 10.1.0
- [Release notes](https://github.com/s3u/JSONPath/releases)
- [Changelog](https://github.com/JSONPath-Plus/JSONPath/blob/main/CHANGES.md)
- [Commits](https://github.com/s3u/JSONPath/compare/v9.0.0...v10.1.0)

Updates `@kubernetes/client-node` from 0.22.0 to 0.22.2
- [Release notes](https://github.com/kubernetes-client/javascript/releases)
- [Changelog](https://github.com/kubernetes-client/javascript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/kubernetes-client/javascript/compare/0.22.0...0.22.2)

---
updated-dependencies:
- dependency-name: jsonpath-plus
  dependency-type: indirect
- dependency-name: "@kubernetes/client-node"
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-06 10:34:45 -05:00
dependabot[bot]
7b404841b2 Bump braces from 3.0.2 to 3.0.3 in /packages/k8s (#188)
Bumps [braces](https://github.com/micromatch/braces) from 3.0.2 to 3.0.3.
- [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/braces/compare/3.0.2...3.0.3)

---
updated-dependencies:
- dependency-name: braces
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-06 10:34:16 -05:00
Josh Gross
977d53963d Remove @actions/runner-akvelon from CODEOWNERS (#190) 2024-11-05 18:13:42 -05:00
Josh Gross
77b40ac6df Prepare 0.6.2 Release (#189) 2024-11-05 14:36:03 -05:00
Oliver Radwell
ee10d95fd4 Bump kubernetes/client-node from 0.18.1 to 0.22.0 (#182) 2024-11-05 13:22:04 -05:00
28 changed files with 9821 additions and 18341 deletions

View File

@@ -1,4 +0,0 @@
dist/
lib/
node_modules/
**/tests/**

View File

@@ -1,56 +0,0 @@
{
"plugins": ["@typescript-eslint"],
"extends": ["plugin:github/recommended"],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 9,
"sourceType": "module",
"project": "./tsconfig.json"
},
"rules": {
"eslint-comments/no-use": "off",
"import/no-namespace": "off",
"no-constant-condition": "off",
"no-unused-vars": "off",
"i18n-text/no-en": "off",
"@typescript-eslint/no-unused-vars": "error",
"@typescript-eslint/explicit-member-accessibility": ["error", {"accessibility": "no-public"}],
"@typescript-eslint/no-require-imports": "error",
"@typescript-eslint/array-type": "error",
"@typescript-eslint/await-thenable": "error",
"camelcase": "off",
"@typescript-eslint/explicit-function-return-type": ["error", {"allowExpressions": true}],
"@typescript-eslint/func-call-spacing": ["error", "never"],
"@typescript-eslint/no-array-constructor": "error",
"@typescript-eslint/no-empty-interface": "error",
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/no-extraneous-class": "error",
"@typescript-eslint/no-floating-promises": "error",
"@typescript-eslint/no-for-in-array": "error",
"@typescript-eslint/no-inferrable-types": "error",
"@typescript-eslint/no-misused-new": "error",
"@typescript-eslint/no-namespace": "error",
"@typescript-eslint/no-non-null-assertion": "warn",
"@typescript-eslint/no-unnecessary-qualifier": "error",
"@typescript-eslint/no-unnecessary-type-assertion": "error",
"@typescript-eslint/no-useless-constructor": "error",
"@typescript-eslint/no-var-requires": "error",
"@typescript-eslint/prefer-for-of": "warn",
"@typescript-eslint/prefer-function-type": "warn",
"@typescript-eslint/prefer-includes": "error",
"@typescript-eslint/prefer-string-starts-ends-with": "error",
"@typescript-eslint/promise-function-async": "error",
"@typescript-eslint/require-array-sort-compare": "error",
"@typescript-eslint/restrict-plus-operands": "error",
"semi": "off",
"@typescript-eslint/semi": ["error", "never"],
"@typescript-eslint/type-annotation-spacing": "error",
"@typescript-eslint/unbound-method": "error",
"no-shadow": "off",
"@typescript-eslint/no-shadow": ["error"]
},
"env": {
"node": true,
"es6": true
}
}

View File

@@ -10,7 +10,7 @@ jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- 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/kind-action@v1.2.0 - uses: helm/kind-action@v1.2.0

View File

@@ -42,7 +42,7 @@ jobs:
# Initializes the CodeQL tools for scanning. # Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@v2 uses: github/codeql-action/init@v3
with: with:
languages: ${{ matrix.language }} languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file. # If you wish to specify custom queries, you can do so here or in a config file.
@@ -56,7 +56,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below) # If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild - name: Autobuild
uses: github/codeql-action/autobuild@v2 uses: github/codeql-action/autobuild@v3
# Command-line programs to run using the OS shell. # Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
@@ -69,4 +69,4 @@ jobs:
# ./location_of_script_within_repo/buildscript.sh # ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2 uses: github/codeql-action/analyze@v3

View File

@@ -1,76 +1,70 @@
name: CD - Release new version name: CD - Release new version
on: on:
workflow_dispatch: workflow_dispatch:
permissions:
contents: write
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- run: npm install
name: Install dependencies - name: Install dependencies
- run: npm run bootstrap run: npm install
name: Bootstrap the packages
- run: npm run build-all - name: Bootstrap the packages
name: Build packages run: npm run bootstrap
- uses: actions/github-script@v6
- name: Build packages
run: npm run build-all
- uses: actions/github-script@v7
id: releaseVersion id: releaseVersion
with: with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} result-encoding: string
script: | script: |
const fs = require('fs'); const fs = require('fs');
const hookVersion = require('./package.json').version return require('./package.json').version
core.setOutput('version', hookVersion);
- name: Zip up releases - name: Zip up releases
run: | run: |
zip -r -j actions-runner-hooks-docker-${{ steps.releaseVersion.outputs.version }}.zip packages/docker/dist zip -r -j actions-runner-hooks-docker-${{ steps.releaseVersion.outputs.result }}.zip packages/docker/dist
zip -r -j actions-runner-hooks-k8s-${{ steps.releaseVersion.outputs.version }}.zip packages/k8s/dist zip -r -j actions-runner-hooks-k8s-${{ steps.releaseVersion.outputs.result }}.zip packages/k8s/dist
- name: Calculate SHA - name: Calculate SHA
id: sha id: sha
shell: bash shell: bash
run: | run: |
sha_docker=$(sha256sum actions-runner-hooks-docker-${{ steps.releaseVersion.outputs.version }}.zip | awk '{print $1}') sha_docker=$(sha256sum actions-runner-hooks-docker-${{ steps.releaseVersion.outputs.result }}.zip | awk '{print $1}')
echo "Docker SHA: $sha_docker" echo "Docker SHA: $sha_docker"
echo "docker-sha=$sha_docker" >> $GITHUB_OUTPUT echo "docker-sha=$sha_docker" >> $GITHUB_OUTPUT
sha_k8s=$(sha256sum actions-runner-hooks-k8s-${{ steps.releaseVersion.outputs.version }}.zip | awk '{print $1}') sha_k8s=$(sha256sum actions-runner-hooks-k8s-${{ steps.releaseVersion.outputs.result }}.zip | awk '{print $1}')
echo "K8s SHA: $sha_k8s" echo "K8s SHA: $sha_k8s"
echo "k8s-sha=$sha_k8s" >> $GITHUB_OUTPUT echo "k8s-sha=$sha_k8s" >> $GITHUB_OUTPUT
- name: replace SHA
- name: Create release notes
id: releaseNotes id: releaseNotes
uses: actions/github-script@v6 uses: actions/github-script@v7
with: with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
script: | script: |
const fs = require('fs'); const fs = require('fs');
var releaseNotes = fs.readFileSync('${{ github.workspace }}/releaseNotes.md', 'utf8').replace(/<HOOK_VERSION>/g, '${{ steps.releaseVersion.outputs.version }}') var releaseNotes = fs.readFileSync('${{ github.workspace }}/releaseNotes.md', 'utf8').replace(/<HOOK_VERSION>/g, '${{ steps.releaseVersion.outputs.result }}')
releaseNotes = releaseNotes.replace(/<DOCKER_SHA>/g, '${{ steps.sha.outputs.docker-sha }}') releaseNotes = releaseNotes.replace(/<DOCKER_SHA>/g, '${{ steps.sha.outputs.docker-sha }}')
releaseNotes = releaseNotes.replace(/<K8S_SHA>/g, '${{ steps.sha.outputs.k8s-sha }}') releaseNotes = releaseNotes.replace(/<K8S_SHA>/g, '${{ steps.sha.outputs.k8s-sha }}')
console.log(releaseNotes) console.log(releaseNotes)
core.setOutput('note', releaseNotes); fs.writeFileSync('${{ github.workspace }}/finalReleaseNotes.md', releaseNotes);
- uses: actions/create-release@v1
id: createRelease - name: Create ${{ steps.releaseVersion.outputs.result }} Hook Release
name: Create ${{ steps.releaseVersion.outputs.version }} Hook Release
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with: run: |
tag_name: "v${{ steps.releaseVersion.outputs.version }}" gh release create v${{ steps.releaseVersion.outputs.result }} \
release_name: "v${{ steps.releaseVersion.outputs.version }}" --title "v${{ steps.releaseVersion.outputs.result }}" \
body: | --repo ${{ github.repository }} \
${{ steps.releaseNotes.outputs.note }} --notes-file ${{ github.workspace }}/finalReleaseNotes.md \
- name: Upload K8s hooks --latest \
uses: actions/upload-release-asset@v1 ${{ github.workspace }}/actions-runner-hooks-k8s-${{ steps.releaseVersion.outputs.result }}.zip \
env: ${{ github.workspace }}/actions-runner-hooks-docker-${{ steps.releaseVersion.outputs.result }}.zip
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.createRelease.outputs.upload_url }}
asset_path: ${{ github.workspace }}/actions-runner-hooks-k8s-${{ steps.releaseVersion.outputs.version }}.zip
asset_name: actions-runner-hooks-k8s-${{ steps.releaseVersion.outputs.version }}.zip
asset_content_type: application/octet-stream
- name: Upload docker hooks
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.createRelease.outputs.upload_url }}
asset_path: ${{ github.workspace }}/actions-runner-hooks-docker-${{ steps.releaseVersion.outputs.version }}.zip
asset_name: actions-runner-hooks-docker-${{ steps.releaseVersion.outputs.version }}.zip
asset_content_type: application/octet-stream

View File

@@ -1 +1 @@
* @actions/actions-launch @actions/runner-akvelon * @actions/actions-launch

82
eslint.config.js Normal file
View File

@@ -0,0 +1,82 @@
import { defineConfig, globalIgnores } from 'eslint/config'
import typescriptEslint from '@typescript-eslint/eslint-plugin'
import globals from 'globals'
import tsParser from '@typescript-eslint/parser'
import github from 'eslint-plugin-github'
export default defineConfig([
globalIgnores(['**/dist/', '**/lib/', '**/node_modules/', '**/tests/**/*', 'eslint.config.js']),
github.getFlatConfigs().recommended,
{
plugins: {
'@typescript-eslint': typescriptEslint
},
files: ['packages/**/*.ts'],
languageOptions: {
globals: globals.node,
parser: tsParser,
ecmaVersion: 9,
sourceType: 'module',
parserOptions: {
project: './tsconfig.json'
}
},
rules: {
'eslint-comments/no-use': 'off',
'import/no-namespace': 'off',
'no-constant-condition': 'off',
'no-unused-vars': 'off',
'i18n-text/no-en': 'off',
'@typescript-eslint/no-unused-vars': 'error',
'@typescript-eslint/explicit-member-accessibility': [
'error',
{
accessibility: 'no-public'
}
],
'@typescript-eslint/no-require-imports': 'error',
'@typescript-eslint/array-type': 'error',
'@typescript-eslint/await-thenable': 'error',
camelcase: 'off',
'@typescript-eslint/explicit-function-return-type': [
'error',
{
allowExpressions: true
}
],
'@typescript-eslint/no-array-constructor': 'error',
'@typescript-eslint/no-empty-interface': 'error',
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/no-extraneous-class': 'error',
'@typescript-eslint/no-floating-promises': 'error',
'@typescript-eslint/no-for-in-array': 'error',
'@typescript-eslint/no-inferrable-types': 'error',
'@typescript-eslint/no-misused-new': 'error',
'@typescript-eslint/no-namespace': 'error',
'@typescript-eslint/no-non-null-assertion': 'warn',
'@typescript-eslint/no-unnecessary-qualifier': 'error',
'@typescript-eslint/no-unnecessary-type-assertion': 'error',
'@typescript-eslint/no-useless-constructor': 'error',
'@typescript-eslint/no-var-requires': 'error',
'@typescript-eslint/prefer-for-of': 'warn',
'@typescript-eslint/prefer-function-type': 'warn',
'@typescript-eslint/prefer-includes': 'error',
'@typescript-eslint/prefer-string-starts-ends-with': 'error',
'@typescript-eslint/promise-function-async': 'error',
'@typescript-eslint/require-array-sort-compare': 'error',
'@typescript-eslint/restrict-plus-operands': 'error',
'@typescript-eslint/unbound-method': 'error',
'no-shadow': 'off',
'@typescript-eslint/no-shadow': ['error']
}
}
])

5697
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "hooks", "name": "hooks",
"version": "0.6.1", "version": "0.6.2",
"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": {
@@ -11,7 +11,7 @@
"bootstrap": "npm install --prefix packages/hooklib && npm install --prefix packages/k8s && npm install --prefix packages/docker", "bootstrap": "npm install --prefix packages/hooklib && npm install --prefix packages/k8s && npm install --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",
"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": {
@@ -25,12 +25,12 @@
}, },
"homepage": "https://github.com/actions/runner-container-hooks#readme", "homepage": "https://github.com/actions/runner-container-hooks#readme",
"devDependencies": { "devDependencies": {
"@types/jest": "^27.5.1", "@types/jest": "^29.5.14",
"@types/node": "^17.0.23", "@types/node": "^22.14.1",
"@typescript-eslint/parser": "^5.18.0", "@typescript-eslint/parser": "^8.30.1",
"eslint": "^8.12.0", "eslint": "^9.24.0",
"eslint-plugin-github": "^4.3.6", "eslint-plugin-github": "^6.0.0",
"prettier": "^2.6.2", "prettier": "^3.5.3",
"typescript": "^4.6.3" "typescript": "^5.8.3"
} }
} }

View File

@@ -1,13 +0,0 @@
// eslint-disable-next-line import/no-commonjs
module.exports = {
clearMocks: true,
moduleFileExtensions: ['js', 'ts'],
testEnvironment: 'node',
testMatch: ['**/*-test.ts'],
testRunner: 'jest-circus/runner',
transform: {
'^.+\\.ts$': 'ts-jest'
},
setupFilesAfterEnv: ['./jest.setup.js'],
verbose: true
}

View File

@@ -0,0 +1,12 @@
{
"clearMocks": true,
"moduleFileExtensions": ["js", "ts"],
"testEnvironment": "node",
"testMatch": ["**/*-test.ts"],
"testRunner": "jest-circus/runner",
"transform": {
"^.+\\.ts$": "ts-jest"
},
"verbose": true,
"testTimeout": 500000
}

View File

@@ -1 +0,0 @@
jest.setTimeout(500000)

File diff suppressed because it is too large Load Diff

View File

@@ -10,21 +10,21 @@
"author": "", "author": "",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@actions/core": "^1.9.1", "@actions/core": "^1.11.1",
"@actions/exec": "^1.1.1", "@actions/exec": "^1.1.1",
"hooklib": "file:../hooklib", "hooklib": "file:../hooklib",
"shlex": "^2.1.2", "shlex": "^2.1.2",
"uuid": "^8.3.2" "uuid": "^11.1.0"
}, },
"devDependencies": { "devDependencies": {
"@types/jest": "^27.4.1", "@types/jest": "^29.5.14",
"@types/node": "^17.0.23", "@types/node": "^22.14.1",
"@typescript-eslint/parser": "^5.18.0", "@typescript-eslint/parser": "^8.30.1",
"@vercel/ncc": "^0.33.4", "@vercel/ncc": "^0.38.3",
"jest": "^27.5.1", "jest": "^29.7.0",
"ts-jest": "^27.1.4", "ts-jest": "^29.3.2",
"ts-node": "^10.7.0", "ts-node": "^10.9.2",
"tsconfig-paths": "^3.14.1", "tsconfig-paths": "^4.2.0",
"typescript": "^4.6.3" "typescript": "^5.8.3"
} }
} }

View File

@@ -31,9 +31,13 @@ export async function prepareJob(
core.info('No containers exist, skipping hook invocation') core.info('No containers exist, skipping hook invocation')
exit(0) exit(0)
} }
const networkName = generateNetworkName()
let networkName = process.env.ACTIONS_RUNNER_NETWORK_DRIVER
if (!networkName) {
networkName = generateNetworkName()
// Create network // Create network
await networkCreate(networkName) await networkCreate(networkName)
}
// Create Job Container // Create Job Container
let containerMetadata: ContainerMetadata | undefined = undefined let containerMetadata: ContainerMetadata | undefined = undefined

View File

@@ -45,9 +45,8 @@ export default class TestSetup {
public initialize(): void { public initialize(): void {
env['GITHUB_WORKSPACE'] = this.workingDirectory env['GITHUB_WORKSPACE'] = this.workingDirectory
env['RUNNER_NAME'] = 'test' env['RUNNER_NAME'] = 'test'
env[ env['RUNNER_TEMP'] =
'RUNNER_TEMP' `${this.runnerMockDir}/${this.runnerMockSubdirs.workTemp}`
] = `${this.runnerMockDir}/${this.runnerMockSubdirs.workTemp}`
for (const dir of this.allTestDirectories) { for (const dir of this.allTestDirectories) {
fs.mkdirSync(dir, { recursive: true }) fs.mkdirSync(dir, { recursive: true })

File diff suppressed because it is too large Load Diff

View File

@@ -14,15 +14,15 @@
"author": "", "author": "",
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"@types/node": "^17.0.23", "@types/node": "^22.14.1",
"@typescript-eslint/parser": "^5.18.0", "@typescript-eslint/parser": "^8.30.1",
"@zeit/ncc": "^0.22.3", "@zeit/ncc": "^0.22.3",
"eslint": "^8.12.0", "eslint": "^9.24.0",
"eslint-plugin-github": "^4.3.6", "eslint-plugin-github": "^6.0.0",
"prettier": "^2.6.2", "prettier": "^3.5.3",
"typescript": "^4.6.3" "typescript": "^5.8.3"
}, },
"dependencies": { "dependencies": {
"@actions/core": "^1.9.1" "@actions/core": "^1.11.1"
} }
} }

View File

@@ -1,13 +0,0 @@
// eslint-disable-next-line import/no-commonjs
module.exports = {
clearMocks: true,
moduleFileExtensions: ['js', 'ts'],
testEnvironment: 'node',
testMatch: ['**/*-test.ts'],
testRunner: 'jest-circus/runner',
transform: {
'^.+\\.ts$': 'ts-jest'
},
setupFilesAfterEnv: ['./jest.setup.js'],
verbose: true
}

View File

@@ -0,0 +1,12 @@
{
"clearMocks": true,
"moduleFileExtensions": ["js", "ts"],
"testEnvironment": "node",
"testMatch": ["**/*-test.ts"],
"testRunner": "jest-circus/runner",
"transform": {
"^.+\\.ts$": "ts-jest"
},
"verbose": true,
"testTimeout": 500000
}

View File

@@ -1 +0,0 @@
jest.setTimeout(500000)

File diff suppressed because it is too large Load Diff

View File

@@ -8,25 +8,27 @@
"build": "tsc && npx ncc build", "build": "tsc && npx ncc build",
"format": "prettier --write '**/*.ts'", "format": "prettier --write '**/*.ts'",
"format-check": "prettier --check '**/*.ts'", "format-check": "prettier --check '**/*.ts'",
"lint": "eslint src/**/*.ts" "lint": "eslint src/**/*.ts",
"lint:fix": "eslint src/**/*.ts --fix"
}, },
"author": "", "author": "",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@actions/core": "^1.9.1", "@actions/core": "^1.11.1",
"@actions/exec": "^1.1.1", "@actions/exec": "^1.1.1",
"@actions/io": "^1.1.2", "@actions/io": "^1.1.3",
"@kubernetes/client-node": "^0.18.1", "@kubernetes/client-node": "^1.1.2",
"hooklib": "file:../hooklib", "hooklib": "file:../hooklib",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"shlex": "^2.1.2" "shlex": "^2.1.2",
"uuid": "^11.1.0"
}, },
"devDependencies": { "devDependencies": {
"@types/jest": "^27.4.1", "@types/jest": "^29.5.14",
"@types/node": "^17.0.23", "@types/node": "^22.14.1",
"@vercel/ncc": "^0.33.4", "@vercel/ncc": "^0.38.3",
"jest": "^27.5.1", "jest": "^29.7.0",
"ts-jest": "^27.1.4", "ts-jest": "^29.3.2",
"typescript": "^4.6.3" "typescript": "^5.8.3"
} }
} }

View File

@@ -28,7 +28,13 @@ export async function runContainerStep(
let secretName: string | undefined = undefined let secretName: string | undefined = undefined
if (stepContainer.environmentVariables) { if (stepContainer.environmentVariables) {
try {
secretName = await createSecretForEnvs(stepContainer.environmentVariables) secretName = await createSecretForEnvs(stepContainer.environmentVariables)
} catch (err) {
core.debug(`createSecretForEnvs failed: ${JSON.stringify(err)}`)
const message = (err as any)?.response?.body?.message || err
throw new Error(`failed to create script environment: ${message}`)
}
} }
const extension = readExtensionFromFile() const extension = readExtensionFromFile()

View File

@@ -95,10 +95,12 @@ export async function createPod(
appPod.spec.containers = containers appPod.spec.containers = containers
appPod.spec.restartPolicy = 'Never' appPod.spec.restartPolicy = 'Never'
if (!useKubeScheduler()) { const nodeName = await getCurrentNodeName()
appPod.spec.nodeName = await getCurrentNodeName() if (useKubeScheduler()) {
appPod.spec.affinity = await getPodAffinity(nodeName)
} else {
appPod.spec.nodeName = nodeName
} }
const claimName = getVolumeClaimName() const claimName = getVolumeClaimName()
appPod.spec.volumes = [ appPod.spec.volumes = [
{ {
@@ -125,8 +127,10 @@ export async function createPod(
mergePodSpecWithOptions(appPod.spec, extension.spec) mergePodSpecWithOptions(appPod.spec, extension.spec)
} }
const { body } = await k8sApi.createNamespacedPod(namespace(), appPod) return await k8sApi.createNamespacedPod({
return body namespace: namespace(),
body: appPod
})
} }
export async function createJob( export async function createJob(
@@ -155,8 +159,11 @@ export async function createJob(
job.spec.template.spec.containers = [container] job.spec.template.spec.containers = [container]
job.spec.template.spec.restartPolicy = 'Never' job.spec.template.spec.restartPolicy = 'Never'
if (!useKubeScheduler()) { const nodeName = await getCurrentNodeName()
job.spec.template.spec.nodeName = await getCurrentNodeName() if (useKubeScheduler()) {
job.spec.template.spec.affinity = await getPodAffinity(nodeName)
} else {
job.spec.template.spec.nodeName = nodeName
} }
const claimName = getVolumeClaimName() const claimName = getVolumeClaimName()
@@ -178,46 +185,42 @@ export async function createJob(
} }
} }
const { body } = await k8sBatchV1Api.createNamespacedJob(namespace(), job) return await k8sBatchV1Api.createNamespacedJob({
return body namespace: namespace(),
body: job
})
} }
export async function getContainerJobPodName(jobName: string): Promise<string> { export async function getContainerJobPodName(jobName: string): Promise<string> {
const selector = `job-name=${jobName}` const selector = `job-name=${jobName}`
const backOffManager = new BackOffManager(60) const backOffManager = new BackOffManager(60)
while (true) { while (true) {
const podList = await k8sApi.listNamespacedPod( const podList = await k8sApi.listNamespacedPod({
namespace(), namespace: namespace(),
undefined, labelSelector: selector,
undefined, limit: 1
undefined, })
undefined,
selector,
1
)
if (!podList.body.items?.length) { if (!podList.items?.length) {
await backOffManager.backOff() await backOffManager.backOff()
continue continue
} }
if (!podList.body.items[0].metadata?.name) { if (!podList.items[0].metadata?.name) {
throw new Error( throw new Error(
`Failed to determine the name of the pod for job ${jobName}` `Failed to determine the name of the pod for job ${jobName}`
) )
} }
return podList.body.items[0].metadata.name return podList.items[0].metadata.name
} }
} }
export async function deletePod(podName: string): Promise<void> { export async function deletePod(podName: string): Promise<void> {
await k8sApi.deleteNamespacedPod( await k8sApi.deleteNamespacedPod({
podName, name: podName,
namespace(), namespace: namespace(),
undefined, gracePeriodSeconds: 0
undefined, })
0
)
} }
export async function execPodStep( export async function execPodStep(
@@ -269,7 +272,7 @@ export async function waitForJobToComplete(jobName: string): Promise<void> {
return return
} }
} catch (error) { } catch (error) {
throw new Error(`job ${jobName} has failed`) throw new Error(`job ${jobName} has failed: ${JSON.stringify(error)}`)
} }
await backOffManager.backOff() await backOffManager.backOff()
} }
@@ -310,8 +313,10 @@ export async function createDockerSecret(
) )
} }
const { body } = await k8sApi.createNamespacedSecret(namespace(), secret) return await k8sApi.createNamespacedSecret({
return body namespace: namespace(),
body: secret
})
} }
export async function createSecretForEnvs(envs: { export async function createSecretForEnvs(envs: {
@@ -335,30 +340,30 @@ export async function createSecretForEnvs(envs: {
secret.data[key] = Buffer.from(value).toString('base64') secret.data[key] = Buffer.from(value).toString('base64')
} }
await k8sApi.createNamespacedSecret(namespace(), secret) await k8sApi.createNamespacedSecret({ namespace: namespace(), body: secret })
return secretName return secretName
} }
export async function deleteSecret(secretName: string): Promise<void> { export async function deleteSecret(secretName: string): Promise<void> {
await k8sApi.deleteNamespacedSecret(secretName, namespace()) await k8sApi.deleteNamespacedSecret({
name: secretName,
namespace: namespace()
})
} }
export async function pruneSecrets(): Promise<void> { export async function pruneSecrets(): Promise<void> {
const secretList = await k8sApi.listNamespacedSecret( const secretList = await k8sApi.listNamespacedSecret({
namespace(), namespace: namespace(),
undefined, labelSelector: new RunnerInstanceLabel().toString()
undefined, })
undefined, if (!secretList.items.length) {
undefined,
new RunnerInstanceLabel().toString()
)
if (!secretList.body.items.length) {
return return
} }
await Promise.all( await Promise.all(
secretList.body.items.map( secretList.items.map(
secret => secret.metadata?.name && deleteSecret(secret.metadata.name) async secret =>
secret.metadata?.name && deleteSecret(secret.metadata.name)
) )
) )
} }
@@ -386,7 +391,9 @@ export async function waitForPodPhases(
await backOffManager.backOff() await backOffManager.backOff()
} }
} catch (error) { } catch (error) {
throw new Error(`Pod ${podName} is unhealthy with phase status ${phase}`) throw new Error(
`Pod ${podName} is unhealthy with phase status ${phase}: ${JSON.stringify(error)}`
)
} }
} }
@@ -417,8 +424,10 @@ async function getPodPhase(podName: string): Promise<PodPhase> {
PodPhase.FAILED, PodPhase.FAILED,
PodPhase.UNKNOWN PodPhase.UNKNOWN
]) ])
const { body } = await k8sApi.readNamespacedPod(podName, namespace()) const pod = await k8sApi.readNamespacedPod({
const pod = body name: podName,
namespace: namespace()
})
if (!pod.status?.phase || !podPhaseLookup.has(pod.status.phase)) { if (!pod.status?.phase || !podPhaseLookup.has(pod.status.phase)) {
return PodPhase.UNKNOWN return PodPhase.UNKNOWN
@@ -427,8 +436,10 @@ async function getPodPhase(podName: string): Promise<PodPhase> {
} }
async function isJobSucceeded(jobName: string): Promise<boolean> { async function isJobSucceeded(jobName: string): Promise<boolean> {
const { body } = await k8sBatchV1Api.readNamespacedJob(jobName, namespace()) const job = await k8sBatchV1Api.readNamespacedJob({
const job = body name: jobName,
namespace: namespace()
})
if (job.status?.failed) { if (job.status?.failed) {
throw new Error(`job ${jobName} has failed`) throw new Error(`job ${jobName} has failed`)
} }
@@ -450,31 +461,26 @@ export async function getPodLogs(
process.stderr.write(err.message) process.stderr.write(err.message)
}) })
const r = await log.log(namespace(), podName, containerName, logStream, { await log.log(namespace(), podName, containerName, logStream, {
follow: true, follow: true,
tailLines: 50,
pretty: false, pretty: false,
timestamps: false timestamps: false
}) })
await new Promise(resolve => r.on('close', () => resolve(null))) await new Promise(resolve => logStream.on('close', () => resolve(null)))
} }
export async function prunePods(): Promise<void> { export async function prunePods(): Promise<void> {
const podList = await k8sApi.listNamespacedPod( const podList = await k8sApi.listNamespacedPod({
namespace(), namespace: namespace(),
undefined, labelSelector: new RunnerInstanceLabel().toString()
undefined, })
undefined, if (!podList.items.length) {
undefined,
new RunnerInstanceLabel().toString()
)
if (!podList.body.items.length) {
return return
} }
await Promise.all( await Promise.all(
podList.body.items.map( podList.items.map(
pod => pod.metadata?.name && deletePod(pod.metadata.name) async pod => pod.metadata?.name && deletePod(pod.metadata.name)
) )
) )
} }
@@ -482,16 +488,13 @@ export async function prunePods(): Promise<void> {
export async function getPodStatus( export async function getPodStatus(
name: string name: string
): Promise<k8s.V1PodStatus | undefined> { ): Promise<k8s.V1PodStatus | undefined> {
const { body } = await k8sApi.readNamespacedPod(name, namespace()) const pod = await k8sApi.readNamespacedPod({ name, namespace: namespace() })
return body.status return pod.status
} }
export async function isAuthPermissionsOK(): Promise<boolean> { export async function isAuthPermissionsOK(): Promise<boolean> {
const sar = new k8s.V1SelfSubjectAccessReview() const sar = new k8s.V1SelfSubjectAccessReview()
const asyncs: Promise<{ const asyncs: Promise<k8s.V1SelfSubjectAccessReview>[] = []
response: unknown
body: k8s.V1SelfSubjectAccessReview
}>[] = []
for (const resource of requiredPermissions) { for (const resource of requiredPermissions) {
for (const verb of resource.verbs) { for (const verb of resource.verbs) {
sar.spec = new k8s.V1SelfSubjectAccessReviewSpec() sar.spec = new k8s.V1SelfSubjectAccessReviewSpec()
@@ -501,11 +504,13 @@ export async function isAuthPermissionsOK(): Promise<boolean> {
sar.spec.resourceAttributes.group = resource.group sar.spec.resourceAttributes.group = resource.group
sar.spec.resourceAttributes.resource = resource.resource sar.spec.resourceAttributes.resource = resource.resource
sar.spec.resourceAttributes.subresource = resource.subresource sar.spec.resourceAttributes.subresource = resource.subresource
asyncs.push(k8sAuthorizationV1Api.createSelfSubjectAccessReview(sar)) asyncs.push(
k8sAuthorizationV1Api.createSelfSubjectAccessReview({ body: sar })
)
} }
} }
const responses = await Promise.all(asyncs) const responses = await Promise.all(asyncs)
return responses.every(resp => resp.body.status?.allowed) return responses.every(resp => resp.status?.allowed)
} }
export async function isPodContainerAlpine( export async function isPodContainerAlpine(
@@ -523,7 +528,7 @@ export async function isPodContainerAlpine(
podName, podName,
containerName containerName
) )
} catch (err) { } catch {
isAlpine = false isAlpine = false
} }
@@ -531,15 +536,38 @@ export async function isPodContainerAlpine(
} }
async function getCurrentNodeName(): Promise<string> { async function getCurrentNodeName(): Promise<string> {
const resp = await k8sApi.readNamespacedPod(getRunnerPodName(), namespace()) const resp = await k8sApi.readNamespacedPod({
name: getRunnerPodName(),
namespace: namespace()
})
const nodeName = resp.body.spec?.nodeName const nodeName = resp.spec?.nodeName
if (!nodeName) { if (!nodeName) {
throw new Error('Failed to determine node name') throw new Error('Failed to determine node name')
} }
return nodeName return nodeName
} }
async function getPodAffinity(nodeName: string): Promise<k8s.V1Affinity> {
const affinity = new k8s.V1Affinity()
affinity.nodeAffinity = new k8s.V1NodeAffinity()
affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution =
new k8s.V1NodeSelector()
affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms =
[
{
matchExpressions: [
{
key: 'kubernetes.io/hostname',
operator: 'In',
values: [nodeName]
}
]
}
]
return affinity
}
export function namespace(): string { export function namespace(): string {
if (process.env['ACTIONS_RUNNER_KUBERNETES_NAMESPACE']) { if (process.env['ACTIONS_RUNNER_KUBERNETES_NAMESPACE']) {
return process.env['ACTIONS_RUNNER_KUBERNETES_NAMESPACE'] return process.env['ACTIONS_RUNNER_KUBERNETES_NAMESPACE']
@@ -623,6 +651,5 @@ export function containerPorts(
} }
export async function getPodByName(name): Promise<k8s.V1Pod> { export async function getPodByName(name): Promise<k8s.V1Pod> {
const { body } = await k8sApi.readNamespacedPod(name, namespace()) return await k8sApi.readNamespacedPod({ name, namespace: namespace() })
return body
} }

View File

@@ -42,6 +42,11 @@ export function containerVolumes(
mountPath: '/github/file_commands', mountPath: '/github/file_commands',
subPath: '_temp/_runner_file_commands' subPath: '_temp/_runner_file_commands'
}, },
{
name: POD_VOLUME_NAME,
mountPath: '/github/home',
subPath: '_temp/_github_home'
},
{ {
name: POD_VOLUME_NAME, name: POD_VOLUME_NAME,
mountPath: '/github/workflow', mountPath: '/github/workflow',

View File

@@ -32,16 +32,12 @@ describe('Cleanup Job', () => {
kc.loadFromDefault() kc.loadFromDefault()
const k8sApi = kc.makeApiClient(k8s.CoreV1Api) const k8sApi = kc.makeApiClient(k8s.CoreV1Api)
const podList = await k8sApi.listNamespacedPod( const podList = await k8sApi.listNamespacedPod({
namespace(), namespace: namespace(),
undefined, labelSelector: new RunnerInstanceLabel().toString()
undefined, })
undefined,
undefined,
new RunnerInstanceLabel().toString()
)
expect(podList.body.items.length).toBe(0) expect(podList.items.length).toBe(0)
}) })
it('should have no runner linked secrets', async () => { it('should have no runner linked secrets', async () => {
@@ -51,15 +47,11 @@ describe('Cleanup Job', () => {
kc.loadFromDefault() kc.loadFromDefault()
const k8sApi = kc.makeApiClient(k8s.CoreV1Api) const k8sApi = kc.makeApiClient(k8s.CoreV1Api)
const secretList = await k8sApi.listNamespacedSecret( const secretList = await k8sApi.listNamespacedSecret({
namespace(), namespace: namespace(),
undefined, labelSelector: new RunnerInstanceLabel().toString()
undefined, })
undefined,
undefined,
new RunnerInstanceLabel().toString()
)
expect(secretList.body.items.length).toBe(0) expect(secretList.items.length).toBe(0)
}) })
}) })

View File

@@ -1,15 +1,10 @@
<!-- ## Features --> <!-- ## Features -->
## Bugs <!-- ## Bugs -->
- Skip writing extension containers in output context file [#154]
## Misc ## Misc
- Bump ws from 7.5.7 to 7.5.10 in /packages/docker [#170] - Bump `@kubernetes/client-node` from 0.18.1 to 0.22.0 in /packages/k8s [#182]
- Bump braces from 3.0.2 to 3.0.3 in /packages/docker [#171]
- Bump tar from 6.1.11 to 6.2.1 in /packages/k8s [#156]
## SHA-256 Checksums ## SHA-256 Checksums