mirror of
https://github.com/actions/runner-container-hooks.git
synced 2025-12-14 00:26:44 +00:00
Sort 'find' output before hashing for consistency (#267)
* Sort 'find' output before hashing for consistency across different platforms * fix style issues
This commit is contained in:
@@ -271,19 +271,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 +297,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 +319,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}`))
|
||||||
@@ -400,7 +418,7 @@ export async function execCpToPod(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const want = await localCalculateOutputHash([
|
const want = await localCalculateOutputHashSorted([
|
||||||
'sh',
|
'sh',
|
||||||
'-c',
|
'-c',
|
||||||
listDirAllCommand(runnerPath)
|
listDirAllCommand(runnerPath)
|
||||||
@@ -410,11 +428,11 @@ export async function execCpToPod(
|
|||||||
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 got = await execCalculateOutputHashSorted(
|
||||||
'sh',
|
podName,
|
||||||
'-c',
|
JOB_CONTAINER_NAME,
|
||||||
listDirAllCommand(containerPath)
|
['sh', '-c', listDirAllCommand(containerPath)]
|
||||||
])
|
)
|
||||||
|
|
||||||
if (got !== want) {
|
if (got !== want) {
|
||||||
core.debug(
|
core.debug(
|
||||||
@@ -441,11 +459,11 @@ 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, [
|
const want = await execCalculateOutputHashSorted(
|
||||||
'sh',
|
podName,
|
||||||
'-c',
|
JOB_CONTAINER_NAME,
|
||||||
listDirAllCommand(containerPath)
|
['sh', '-c', listDirAllCommand(containerPath)]
|
||||||
])
|
)
|
||||||
|
|
||||||
let attempt = 0
|
let attempt = 0
|
||||||
while (true) {
|
while (true) {
|
||||||
@@ -506,7 +524,7 @@ 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 got = await localCalculateOutputHashSorted([
|
||||||
'sh',
|
'sh',
|
||||||
'-c',
|
'-c',
|
||||||
listDirAllCommand(targetRunnerPath)
|
listDirAllCommand(targetRunnerPath)
|
||||||
|
|||||||
Reference in New Issue
Block a user