From e9f6a3bfc9b63f8c3137a1269b1bfd5d4c338120 Mon Sep 17 00:00:00 2001 From: Eric Satterwhite Date: Sat, 3 Feb 2024 20:40:05 -0600 Subject: [PATCH] fix(image): account for stderr when looking for an image sha in the case the progress output is set to plain, the build output is written to stderr rather than stdout. This updates the logic to check each line with a regex starting from the end of the output Fixes: #46 --- lib/docker/image.js | 18 +++++-- test/fixture/docker/Dockerfile.publish | 2 +- test/integration/prepare.js | 67 ++++++++++++++++++++++++-- 3 files changed, 77 insertions(+), 10 deletions(-) diff --git a/lib/docker/image.js b/lib/docker/image.js index dd80ea5..0e35298 100644 --- a/lib/docker/image.js +++ b/lib/docker/image.js @@ -1,9 +1,11 @@ 'use strict' +const os = require('os') const path = require('path') const crypto = require('crypto') const execa = require('execa') const array = require('../lang/array/index.js') +const SHA_REGEX = /(?:writing image\s)?[^@](?:sha\d{3}):(?\w+)/i class Image { constructor(opts) { @@ -160,10 +162,18 @@ class Image { const stream = execa('docker', this.build_cmd) stream.stdout.pipe(process.stdout) stream.stderr.pipe(process.stderr) - const {stdout} = await stream - const [_, sha] = stdout.split(':') - this.sha = sha.substring(0, 12) - return this.sha + const {stdout, stderr} = await stream + const lines = (stdout || stderr).split(os.EOL) + const len = lines.length - 1 + + for (let x = len; x >= 0; x--) { + const line = lines[x] + const match = SHA_REGEX.exec(line) + if (match) { + this.sha = match.groups.sha.substring(0, 12) + return this.sha + } + } } async tag(tag, push = true) { diff --git a/test/fixture/docker/Dockerfile.publish b/test/fixture/docker/Dockerfile.publish index 380c614..2d0b7f8 100644 --- a/test/fixture/docker/Dockerfile.publish +++ b/test/fixture/docker/Dockerfile.publish @@ -1,4 +1,4 @@ -FROM debian:buster-slim +FROM debian:bullseye-slim COPY . /opt/whizbang WORKDIR /opt/whizbang CMD ["echo", "$PWD"] diff --git a/test/integration/prepare.js b/test/integration/prepare.js index 8e3ad19..fba6fc3 100644 --- a/test/integration/prepare.js +++ b/test/integration/prepare.js @@ -72,11 +72,6 @@ test('steps::prepare', async (t) => { tt.equal(image.opts.args.get('MAJOR_TEMPLATE'), '2', 'MAJOR_TEMPLATE value') tt.equal(image.opts.args.get('GIT_REF'), 'abacadaba', 'GIT_REF value') tt.match(image.opts.args.get('BUILD_DATE'), DATE_REGEX, 'BUILD_DATE value') - tt.match( - image.opts.flags.get('TAG_TEMPLATE') - , ['v2.1.2'] - , 'TAG_TEMPLATE stored as a flag' - ) tt.equal(image.context, path.join(context.cwd, config.context), 'docker context path') const {stdout} = await execa('docker', [ @@ -85,4 +80,66 @@ test('steps::prepare', async (t) => { ]) tt.equal(stdout, build_id, 'build image fully built') }) + + t.test('build image created - progress plain', async (tt) => { + const build_id = crypto.randomBytes(5).toString('hex') + const context = { + env: { + ...process.env + , DOCKER_REGISTRY_USER: 'iamweasel' + , DOCKER_REGISTRY_PASSWORD: 'secretsquirrel' + } + , cwd: fixturedir + , nextRelease: { + version: '2.1.2' + , gitTag: 'v2.1.2' + , gitHead: 'abacadaba' + } + , logger: { + success: sinon.stub() + , info: sinon.stub() + , debug: sinon.stub() + , fatal: sinon.stub() + } + } + + const config = await buildConfig(build_id, { + dockerRegistry: DOCKER_REGISTRY_HOST + , dockerProject: 'docker-prepare' + , dockerImage: 'alternate' + , dockerBuildQuiet: false + , dockerBuildFlags: { + progress: 'plain' + , 'no-cache': null + } + , dockerArgs: { + MY_VARIABLE: '1' + , TAG_TEMPLATE: '{{git_tag}}' + , MAJOR_TEMPLATE: '{{major}}' + , GIT_REF: '{{git_sha}}' + , BUILD_DATE: '{{now}}' + } + , dockerFile: 'docker/Dockerfile.prepare' + , dockerContext: 'docker' + }, context) + + const image = await prepare(config, context) + + tt.on('end', () => { + image.clean() + }) + + tt.equal(image.opts.args.get('TAG_TEMPLATE'), 'v2.1.2', 'TAG_TEMPLATE value') + tt.equal(image.opts.args.get('MAJOR_TEMPLATE'), '2', 'MAJOR_TEMPLATE value') + tt.equal(image.opts.args.get('GIT_REF'), 'abacadaba', 'GIT_REF value') + tt.match(image.opts.args.get('BUILD_DATE'), DATE_REGEX, 'BUILD_DATE value') + tt.equal(image.context, path.join(context.cwd, config.context), 'docker context path') + tt.equal(image.id.length, 12, 'image sha set to 12 char value') + + const {stdout} = await execa('docker', [ + 'images', image.name + , '-q', '--format={{ .Tag }}' + ]) + tt.equal(stdout, build_id, 'build image fully built') + }) }).catch(threw)