Compare commits

...
Author SHA1 Message Date
Nikola Jokic 1aa7336686 fix test 2026-01-26 16:16:50 +01:00
Nikola Jokic 04b61b9744 Resolve service name conflicts when the name is computed to the same value 2026-01-26 12:44:11 +01:00
Nikola JokicandGitHub d8d2e74810 Bump packages (#304)
* Bump packages

* bump exec
2026-01-15 21:21:58 +01:00
Will HopkinsandGitHub 5f5708a2b8 Overwrite runnner file commands (#298) 2025-12-12 13:57:47 +01:00
9 changed files with 1302 additions and 1541 deletions
+550 -643
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -13,8 +13,8 @@
"author": "",
"license": "MIT",
"dependencies": {
"@actions/core": "^1.11.1",
"@actions/exec": "^1.1.1",
"@actions/core": "^2.0.2",
"@actions/exec": "^2.0.0",
"hooklib": "file:../hooklib",
"shlex": "^3.0.0",
"uuid": "^11.1.0"
+234 -426
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -22,6 +22,6 @@
"typescript": "^5.8.3"
},
"dependencies": {
"@actions/core": "^1.11.1"
"@actions/core": "^2.0.2"
}
}
+450 -457
View File
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -13,9 +13,9 @@
"author": "",
"license": "MIT",
"dependencies": {
"@actions/core": "^1.11.1",
"@actions/exec": "^1.1.1",
"@actions/io": "^1.1.3",
"@actions/core": "^2.0.2",
"@actions/exec": "^2.0.0",
"@actions/io": "^2.0.0",
"@kubernetes/client-node": "^1.3.0",
"hooklib": "file:../hooklib",
"js-yaml": "^4.1.0",
+28 -9
View File
@@ -58,14 +58,30 @@ export async function prepareJob(
}
let services: k8s.V1Container[] = []
let serviceNames: string[] = []
if (args.services?.length) {
const occurrences = new Map<string, number>()
for (const s of args.services) {
const base = generateContainerName(s.image)
occurrences.set(base, (occurrences.get(base) || 0) + 1)
}
const indices = new Map<string, number>()
services = args.services.map(service => {
return createContainerSpec(
service,
generateContainerName(service.image),
false,
extension
)
const base = generateContainerName(service.image)
const total = occurrences.get(base) || 0
const idx = indices.get(base) || 0
let name: string
if (total > 1) {
name = `${base}-${idx}`
} else {
name = base
}
indices.set(base, idx + 1)
serviceNames.push(name)
return createContainerSpec(service, name, false, extension)
})
}
@@ -153,14 +169,15 @@ export async function prepareJob(
throw new Error(`failed to determine if the pod is alpine: ${message}`)
}
core.debug(`Setting isAlpine to ${isAlpine}`)
generateResponseFile(responseFile, args, createdPod, isAlpine)
generateResponseFile(responseFile, args, createdPod, isAlpine, serviceNames)
}
function generateResponseFile(
responseFile: string,
args: PrepareJobArgs,
appPod: k8s.V1Pod,
isAlpine: boolean
isAlpine: boolean,
serviceNames?: string[]
): void {
if (!appPod.metadata?.name) {
throw new Error('app pod must have metadata.name specified')
@@ -193,7 +210,9 @@ function generateResponseFile(
if (args.services?.length) {
const serviceContainerNames =
args.services?.map(s => generateContainerName(s.image)) || []
serviceNames && serviceNames.length
? serviceNames
: args.services?.map(s => generateContainerName(s.image)) || []
response.context['services'] = appPod?.spec?.containers
?.filter(c => serviceContainerNames.includes(c.name))
@@ -48,6 +48,7 @@ export async function runScriptStep(
'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"
+33
View File
@@ -243,4 +243,37 @@ describe('Prepare job', () => {
'ghcr.io/actions/actions-runner:latest'
)
})
it('should create unique service container names when images collide', async () => {
// Use fixed, non-colliding high ports. (Kubernetes hostPort must be unique per node.)
prepareJobData.args.container.portMappings = ['31080:8080']
// make two services with the same image
const svc = JSON.parse(JSON.stringify(prepareJobData.args.services[0]))
const svc2 = JSON.parse(JSON.stringify(svc))
// Ensure unique host ports so the pod spec is valid even with two services.
// (Kubernetes hostPort must be unique per node.)
svc.portMappings = ['31081:80', '31082:8080']
svc2.portMappings = ['31083:80', '31084:8080']
prepareJobData.args.services = [svc, svc2]
// ensure registries are null as TestHelper expects
prepareJobData.args.services.forEach((s: any) => (s.registry = null))
await expect(
prepareJob(prepareJobData.args, prepareJobOutputFilePath)
).resolves.not.toThrow()
const content = JSON.parse(
fs.readFileSync(prepareJobOutputFilePath).toString()
)
expect(content.context.services).toBeTruthy()
expect(content.context.services.length).toBe(2)
const got = await getPodByName(content.state.jobPod)
const names = (got.spec?.containers || []).map(c => c.name)
// when images collide, names should be suffixed with -0, -1
expect(names).toEqual(expect.arrayContaining(['redis-0', 'redis-1']))
})
})