mirror of
https://github.com/actions/runner-container-hooks.git
synced 2025-12-16 09:46:43 +00:00
* feat: check if required binaries are present Previously the necessary binaries were copied over using the runner container. This lead to issues in case your main container was using the musl libc implementation. Instead of copying over any binaries, the initContainer now checks if the required binaries are present in the main container. * feat: get rid of the init container * fix: add _runner_file_commands * fix: do not fail if _runner_file_commands does not exist It seems that for container actions this directory does not exist.
154 lines
4.1 KiB
TypeScript
154 lines
4.1 KiB
TypeScript
import * as core from '@actions/core'
|
|
import * as fs from 'fs'
|
|
import * as k8s from '@kubernetes/client-node'
|
|
import { RunContainerStepArgs } from 'hooklib'
|
|
import { dirname } from 'path'
|
|
import {
|
|
createContainerStepPod,
|
|
deletePod,
|
|
execCpFromPod,
|
|
execCpToPod,
|
|
execPodStep,
|
|
getPrepareJobTimeoutSeconds,
|
|
waitForPodPhases
|
|
} from '../k8s'
|
|
import {
|
|
CONTAINER_VOLUMES,
|
|
mergeContainerWithOptions,
|
|
PodPhase,
|
|
readExtensionFromFile,
|
|
DEFAULT_CONTAINER_ENTRY_POINT_ARGS,
|
|
writeContainerStepScript
|
|
} from '../k8s/utils'
|
|
import {
|
|
getJobPodName,
|
|
getStepPodName,
|
|
JOB_CONTAINER_EXTENSION_NAME,
|
|
JOB_CONTAINER_NAME
|
|
} from './constants'
|
|
|
|
export async function runContainerStep(
|
|
stepContainer: RunContainerStepArgs
|
|
): Promise<number> {
|
|
if (stepContainer.dockerfile) {
|
|
throw new Error('Building container actions is not currently supported')
|
|
}
|
|
|
|
if (!stepContainer.entryPoint) {
|
|
throw new Error(
|
|
'failed to start the container since the entrypoint is overwritten'
|
|
)
|
|
}
|
|
|
|
const envs = stepContainer.environmentVariables || {}
|
|
envs['GITHUB_ACTIONS'] = 'true'
|
|
if (!('CI' in envs)) {
|
|
envs.CI = 'true'
|
|
}
|
|
|
|
const extension = readExtensionFromFile()
|
|
|
|
const container = createContainerSpec(stepContainer, extension)
|
|
|
|
let pod: k8s.V1Pod
|
|
try {
|
|
pod = await createContainerStepPod(getStepPodName(), container, extension)
|
|
} catch (err) {
|
|
core.debug(`createJob failed: ${JSON.stringify(err)}`)
|
|
const message = (err as any)?.response?.body?.message || err
|
|
throw new Error(`failed to run script step: ${message}`)
|
|
}
|
|
|
|
if (!pod.metadata?.name) {
|
|
throw new Error(
|
|
`Expected job ${JSON.stringify(
|
|
pod
|
|
)} to have correctly set the metadata.name`
|
|
)
|
|
}
|
|
const podName = pod.metadata.name
|
|
|
|
try {
|
|
await waitForPodPhases(
|
|
podName,
|
|
new Set([PodPhase.RUNNING]),
|
|
new Set([PodPhase.PENDING, PodPhase.UNKNOWN]),
|
|
getPrepareJobTimeoutSeconds()
|
|
)
|
|
|
|
const runnerWorkspace = dirname(process.env.RUNNER_WORKSPACE as string)
|
|
const githubWorkspace = process.env.GITHUB_WORKSPACE as string
|
|
const parts = githubWorkspace.split('/').slice(-2)
|
|
if (parts.length !== 2) {
|
|
throw new Error(`Invalid github workspace directory: ${githubWorkspace}`)
|
|
}
|
|
const relativeWorkspace = parts.join('/')
|
|
|
|
core.debug(
|
|
`Copying files from pod ${getJobPodName()} to ${runnerWorkspace}/${relativeWorkspace}`
|
|
)
|
|
await execCpFromPod(getJobPodName(), `/__w`, `${runnerWorkspace}`)
|
|
|
|
const { containerPath, runnerPath } = writeContainerStepScript(
|
|
`${runnerWorkspace}/__w/_temp`,
|
|
githubWorkspace,
|
|
stepContainer.entryPoint,
|
|
stepContainer.entryPointArgs,
|
|
envs
|
|
)
|
|
|
|
await execCpToPod(podName, `${runnerWorkspace}/__w`, '/__w')
|
|
|
|
fs.rmSync(`${runnerWorkspace}/__w`, { recursive: true, force: true })
|
|
|
|
try {
|
|
core.debug(`Executing container step script in pod ${podName}`)
|
|
return await execPodStep(
|
|
['sh', '-e', containerPath],
|
|
pod.metadata.name,
|
|
JOB_CONTAINER_NAME
|
|
)
|
|
} catch (err) {
|
|
core.debug(`execPodStep failed: ${JSON.stringify(err)}`)
|
|
const message = (err as any)?.response?.body?.message || err
|
|
throw new Error(`failed to run script step: ${message}`)
|
|
} finally {
|
|
fs.rmSync(runnerPath, { force: true })
|
|
}
|
|
} catch (error) {
|
|
core.error(`Failed to run container step: ${error}`)
|
|
throw error
|
|
} finally {
|
|
await deletePod(podName).catch(err => {
|
|
core.error(`Failed to delete step pod ${podName}: ${err}`)
|
|
})
|
|
}
|
|
}
|
|
|
|
function createContainerSpec(
|
|
container: RunContainerStepArgs,
|
|
extension?: k8s.V1PodTemplateSpec
|
|
): k8s.V1Container {
|
|
const podContainer = new k8s.V1Container()
|
|
podContainer.name = JOB_CONTAINER_NAME
|
|
podContainer.image = container.image
|
|
podContainer.workingDir = '/__w'
|
|
podContainer.command = ['tail']
|
|
podContainer.args = DEFAULT_CONTAINER_ENTRY_POINT_ARGS
|
|
|
|
podContainer.volumeMounts = CONTAINER_VOLUMES
|
|
|
|
if (!extension) {
|
|
return podContainer
|
|
}
|
|
|
|
const from = extension.spec?.containers?.find(
|
|
c => c.name === JOB_CONTAINER_EXTENSION_NAME
|
|
)
|
|
if (from) {
|
|
mergeContainerWithOptions(podContainer, from)
|
|
}
|
|
|
|
return podContainer
|
|
}
|