scripts/__tests__/docker-publish-cache.test.ts
Rama main · réplica de lectura del repositorio original.
import { readFileSync } from 'node:fs'
import path from 'node:path'
import { describe, expect, it } from 'vitest'
/**
* The shipped image's final stage runs `apk upgrade` so Alpine security fixes
* published after the pinned base digest still land. buildx replays a RUN
* layer from the registry cache whenever its instruction text and parent are
* unchanged, which is always the case here: the digest is pinned. Without a
* cache exclusion for that stage the upgrade silently stops happening and
* the daily Trivy scan stays red on OS packages nobody changed (#2055,
* #2490). This test keeps the exclusion and the stage name in sync.
*/
const read = (file: string) => readFileSync(path.join(process.cwd(), file), 'utf8')
function dockerStages(dockerfile: string) {
const stages: { name: string; body: string[] }[] = []
for (const line of dockerfile.split('\n')) {
const from = /^FROM\s+\S+\s+AS\s+(\S+)/i.exec(line)
if (from) {
stages.push({ name: from[1], body: [] })
} else if (stages.length > 0) {
stages[stages.length - 1].body.push(line)
}
}
return stages
}
describe('docker-publish.yml layer cache vs. Dockerfile apk upgrade', () => {
const stages = dockerStages(read('docker/Dockerfile'))
const shipped = stages[stages.length - 1]
const workflow = read('.github/workflows/docker-publish.yml')
it('the shipped stage still upgrades Alpine packages', () => {
expect(shipped.body.some((l) => /apk\s+upgrade/.test(l))).toBe(true)
})
it('excludes every apk-upgrading stage that ships from the buildx cache', () => {
const filters = /^\s*no-cache-filters:\s*(.+)$/m.exec(workflow)
expect(filters, 'no-cache-filters missing from the build step').not.toBeNull()
const excluded = filters![1].split(',').map((s) => s.trim())
expect(excluded).toContain(shipped.name)
})
})