Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Incorporate image cleanup
Barry Gordon committed Mar 1, 2022
1 parent 253310b commit cb1fa6a
Showing 7 changed files with 218 additions and 4 deletions.
77 changes: 76 additions & 1 deletion __tests__/docker-tags.test.ts
@@ -1,4 +1,8 @@
import {UPDATER_IMAGE_NAME, PROXY_IMAGE_NAME} from '../src/docker-tags'
import {
UPDATER_IMAGE_NAME,
PROXY_IMAGE_NAME,
repositoryName
} from '../src/docker-tags'
import {getImageName} from '../src/update-containers'

describe('Docker tags', () => {
@@ -17,4 +21,75 @@ describe('Docker tags', () => {

expect(PROXY_IMAGE_NAME).toEqual(getImageName('Dockerfile.proxy'))
})

test('repositoryName returns the image name minus the tagged version or reference for our real values', () => {
expect(repositoryName(UPDATER_IMAGE_NAME)).toMatch(
'docker.pkg.github.com/dependabot/dependabot-updater'
)

expect(repositoryName(PROXY_IMAGE_NAME)).toMatch(
'docker.pkg.github.com/github/dependabot-update-job-proxy'
)
})

test('repositoryName handles named tags', () => {
// We currently use pinned SHA references instead of tags, but we should account for both notations
// to avoid any surprises
expect(
repositoryName('docker.pkg.github.com/dependabot/dependabot-updater:v1')
).toMatch('docker.pkg.github.com/dependabot/dependabot-updater')

expect(
repositoryName('docker.pkg.github.com/dependabot/dependabot-updater:v1')
).toMatch('docker.pkg.github.com/dependabot/dependabot-updater')
})

test('repositoryName handles ghcr.io images', () => {
expect(
repositoryName('ghcr.io/dependabot/dependabot-core:0.175.0')
).toMatch('ghcr.io/dependabot/dependabot-core')
})

test('repositoryName handles other images', () => {
// A GitHub-hosted image isn't an implicit requirement of the function
expect(repositoryName('hello_world:latest')).toMatch('hello_world')

expect(repositoryName('127.0.0.1:443/hello_world')).toMatch(
'127.0.0.1:443/hello_world'
)

expect(repositoryName('127.0.0.1:443/hello_world:443')).toMatch(
'127.0.0.1:443/hello_world'
)

expect(
repositoryName(
'127.0.0.1:443/hello_world@sha256:3d6c07043f4f2baf32047634a00a6581cf1124f12a30dcc859ab128f24333a3a'
)
).toMatch('127.0.0.1:443/hello_world')
})

test('repositoryName handles garbage inputs', () => {
expect(() => {
repositoryName('this is just some random nonsense')
}).toThrow('invalid image name')

expect(() => {
repositoryName('this-is-just-some-random-nonsense-with-an-@-in-it')
}).toThrow('invalid image name')

expect(() => {
repositoryName('this-is-just-some-random-nonsense-with-an-@sha256-in-it')
}).toThrow('invalid image name')
})

test('repositoryName handles garbage inputs that look like tags', () => {
expect(
repositoryName('this-is-just-some-random-nonsense-but-looks-like-a-tag')
).toMatch('this-is-just-some-random-nonsense-but-looks-like-a-tag')

expect(
repositoryName('this-is-just-some-random-nonsense-with-a:in-it')
).toMatch('this-is-just-some-random-nonsense-with-a')
})
})
72 changes: 72 additions & 0 deletions dist/cleanup/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/cleanup/index.js.map

Large diffs are not rendered by default.

13 changes: 12 additions & 1 deletion dist/main/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/main/index.js.map

Large diffs are not rendered by default.

43 changes: 43 additions & 0 deletions src/cleanup.ts
@@ -1,5 +1,10 @@
import * as core from '@actions/core'
import Docker from 'dockerode'
import {
UPDATER_IMAGE_NAME,
PROXY_IMAGE_NAME,
repositoryName
} from './docker-tags'

// This method performs housekeeping checks to remove Docker artifacts
// which were left behind by old versions of the action or any jobs
@@ -14,9 +19,47 @@ export async function run(cutoff = '24h'): Promise<void> {
await docker.pruneNetworks({filters: untilFilter})
core.info(`Pruning containers older than ${cutoff}`)
await docker.pruneContainers({filters: untilFilter})
await cleanupOldImageVersions(docker, UPDATER_IMAGE_NAME)
await cleanupOldImageVersions(docker, PROXY_IMAGE_NAME)
} catch (error) {
core.error(`Error cleaning up: ${error.message}`)
}
}

async function cleanupOldImageVersions(
docker: Docker,
imageName: string
): Promise<void> {
const repo = repositoryName(imageName)
const options = {
filters: {
reference: [repo]
}
}

core.info(`Cleaning up images for ${repo}`)

docker.listImages(options, async function (err, images) {
if (images && images.length > 0) {
for (const imageInfo of images) {
if (imageInfo.RepoDigests?.includes(imageName)) {
core.info(`Skipping current image ${imageInfo.RepoDigests}`)
continue
}

core.info(`Removing image ${imageInfo.RepoDigests}`)
try {
await docker.getImage(imageInfo.Id).remove()
} catch (error) {
if (error.statusCode === 409) {
core.info(
`Unable to remove ${imageInfo.RepoDigests} as it is currently in use`
)
}
}
}
}
})
}

run()
13 changes: 13 additions & 0 deletions src/docker-tags.ts
@@ -2,3 +2,16 @@ import dockerContainerConfig from '../docker/containers.json'

export const UPDATER_IMAGE_NAME = dockerContainerConfig.updater
export const PROXY_IMAGE_NAME = dockerContainerConfig.proxy

const imageNamePattern =
'^(?<repository>(([a-zA-Z0-9._-]+([:[0-9]+[^/]))?([a-zA-Z0-9._/-]+)?))((:[a-zA-Z0-9._/-]+)|(@sha256:[a-zA-Z0-9]{64}))?$'

export function repositoryName(imageName: string): string {
const match = imageName.match(imageNamePattern)

if (match?.groups) {
return match.groups['repository']
} else {
throw Error('invalid image name')
}
}

0 comments on commit cb1fa6a

Please sign in to comment.