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

Unverified

No user is associated with the committer email.
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.