From 4f45ee316ff537466c7ab4cb7b733462fdc60828 Mon Sep 17 00:00:00 2001 From: David Herberth Date: Fri, 20 Sep 2024 11:18:36 +0200 Subject: [PATCH 1/2] ref(docker): Support multi-arch images through docker buildkit --- src/targets/docker.ts | 48 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/src/targets/docker.ts b/src/targets/docker.ts index ca12abd6..4a51f2dd 100644 --- a/src/targets/docker.ts +++ b/src/targets/docker.ts @@ -86,7 +86,7 @@ export class DockerTarget extends BaseTarget { * Pushes the the source image into local * @param revision Image tag, usually the git revision */ - public async pull(revision: string): Promise { + async pull(revision: string): Promise { this.logger.debug('Pulling source image...'); const sourceImage = renderTemplateSafe(this.dockerConfig.sourceTemplate, { ...this.dockerConfig, @@ -105,7 +105,7 @@ export class DockerTarget extends BaseTarget { * @param sourceRevision The tag/revision for the source image * @param version The release version for the target image */ - public async push(sourceRevision: string, version: string): Promise { + async push(sourceRevision: string, version: string): Promise { const sourceImage = renderTemplateSafe(this.dockerConfig.sourceTemplate, { ...this.dockerConfig, revision: sourceRevision, @@ -124,6 +124,40 @@ export class DockerTarget extends BaseTarget { ); } + /** + * Checks whether Docker BuildKit is installed. + */ + async hasBuildKit(): Promise { + return spawnProcess(DOCKER_BIN, ['buildx', 'version']).then(() => true).catch(() => false); + } + + /** + * Copies an existing local or remote docker image to a new destination. + * + * Requires BuildKit / `docker buildx` to be installed. + * + * @param sourceRevision The tag/revision for the source image + * @param version The release version for the target image + */ + async copy(sourceRevision: string, version: string): Promise { + const sourceImage = renderTemplateSafe(this.dockerConfig.sourceTemplate, { + ...this.dockerConfig, + revision: sourceRevision, + }); + const targetImage = renderTemplateSafe(this.dockerConfig.targetTemplate, { + ...this.dockerConfig, + version, + }); + + this.logger.debug(`Copying image from ${sourceImage} to ${targetImage}...`); + return spawnProcess( + DOCKER_BIN, + ['buildx', 'imagetools', 'create', '--tag', targetImage, sourceImage], + {}, + { showStdout: true } + ); + } + /** * Pushes a source image to Docker Hub * @@ -132,8 +166,14 @@ export class DockerTarget extends BaseTarget { */ public async publish(version: string, revision: string): Promise { await this.login(); - await this.pull(revision); - await this.push(revision, version); + + if (await this.hasBuildKit()) { + await this.copy(revision, version); + } else { + // Fall back to slow/old pull and push method. + await this.pull(revision); + await this.push(revision, version); + } this.logger.info('Docker release complete'); } From ba6ea1b45bf2e385a1923ba1b65f8f09f0b6486c Mon Sep 17 00:00:00 2001 From: David Herberth Date: Thu, 26 Sep 2024 09:21:01 +0200 Subject: [PATCH 2/2] remove pull/push routine --- README.md | 15 ++++++----- src/targets/docker.ts | 58 +------------------------------------------ 2 files changed, 8 insertions(+), 65 deletions(-) diff --git a/README.md b/README.md index d8275702..25685e5c 100644 --- a/README.md +++ b/README.md @@ -904,17 +904,16 @@ targets: ### Docker (`docker`) -Pulls an existing source image tagged with the revision SHA, and then pushed it -to a new target tagged with the released version. No release -assets are required for this target except for the source image at the provided -source image location so it would be a good idea to add a status check that -ensures the source image exists, otherwise `craft publish` will fail at the -`docker pull` step, causing an interrupted publish. This is an issue for other, -non-idempotent targets, not for the Docker target. +Copies an existing source image tagged with the revision SHA to a new target +tagged with the released version. No release assets are required for this target +except for the source image at the provided source image location so it would be +a good idea to add a status check that ensures the source image exists, otherwise +`craft publish` will fail at the copy step, causing an interrupted publish. +This is an issue for other, non-idempotent targets, not for the Docker target. **Environment** -`docker` executable (or something equivalent) must be installed on the system. +`docker` executable (or something equivalent) with BuildKit must be installed on the system. | Name | Description | | ----------------- | ------------------------------------------ | diff --git a/src/targets/docker.ts b/src/targets/docker.ts index 4a51f2dd..3545fb7b 100644 --- a/src/targets/docker.ts +++ b/src/targets/docker.ts @@ -82,55 +82,6 @@ export class DockerTarget extends BaseTarget { ]); } - /** - * Pushes the the source image into local - * @param revision Image tag, usually the git revision - */ - async pull(revision: string): Promise { - this.logger.debug('Pulling source image...'); - const sourceImage = renderTemplateSafe(this.dockerConfig.sourceTemplate, { - ...this.dockerConfig, - revision, - }); - return spawnProcess( - DOCKER_BIN, - ['pull', sourceImage], - {}, - { enableInDryRunMode: true } - ); - } - - /** - * Pushes the locally tagged source image to Docker Hub - * @param sourceRevision The tag/revision for the source image - * @param version The release version for the target image - */ - async push(sourceRevision: string, version: string): Promise { - const sourceImage = renderTemplateSafe(this.dockerConfig.sourceTemplate, { - ...this.dockerConfig, - revision: sourceRevision, - }); - const targetImage = renderTemplateSafe(this.dockerConfig.targetTemplate, { - ...this.dockerConfig, - version, - }); - this.logger.debug('Tagging target image...'); - await spawnProcess(DOCKER_BIN, ['tag', sourceImage, targetImage]); - return spawnProcess( - DOCKER_BIN, - ['push', targetImage], - {}, - { showStdout: true } - ); - } - - /** - * Checks whether Docker BuildKit is installed. - */ - async hasBuildKit(): Promise { - return spawnProcess(DOCKER_BIN, ['buildx', 'version']).then(() => true).catch(() => false); - } - /** * Copies an existing local or remote docker image to a new destination. * @@ -166,14 +117,7 @@ export class DockerTarget extends BaseTarget { */ public async publish(version: string, revision: string): Promise { await this.login(); - - if (await this.hasBuildKit()) { - await this.copy(revision, version); - } else { - // Fall back to slow/old pull and push method. - await this.pull(revision); - await this.push(revision, version); - } + await this.copy(revision, version); this.logger.info('Docker release complete'); }