/** * Copyright 2024 actions-toolkit authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import {describe, expect, it, test} from '@jest/globals'; import fs from 'fs'; import * as path from 'path'; import {Buildx} from '../src/buildx/buildx'; import {Bake} from '../src/buildx/bake'; import {Build} from '../src/buildx/build'; import {Exec} from '../src/exec'; import {GitHub} from '../src/github'; import {History} from '../src/buildx/history'; const fixturesDir = path.join(__dirname, 'fixtures'); // prettier-ignore const tmpDir = path.join(process.env.TEMP || '/tmp', 'github-jest'); const maybe = !process.env.GITHUB_ACTIONS || (process.env.GITHUB_ACTIONS === 'true' && process.env.ImageOS && process.env.ImageOS.startsWith('ubuntu')) ? describe : describe.skip; maybe('uploadArtifact', () => { it('uploads an artifact', async () => { const res = await GitHub.uploadArtifact({ filename: path.join(fixturesDir, 'github-repo.json'), mimeType: 'application/json', retentionDays: 1 }); expect(res).toBeDefined(); console.log('uploadArtifactResponse', res); expect(res?.url).toBeDefined(); }); }); maybe('writeBuildSummary', () => { // prettier-ignore test.each([ [ "single", [ 'build', '-f', path.join(fixturesDir, 'hello.Dockerfile'), fixturesDir ], ], [ "multiplatform", [ 'build', '-f', path.join(fixturesDir, 'hello.Dockerfile'), '--platform', 'linux/amd64,linux/arm64', fixturesDir ], ] ])('write build summary %p', async (_, bargs) => { const buildx = new Buildx(); const build = new Build({buildx: buildx}); fs.mkdirSync(tmpDir, {recursive: true}); await expect( (async () => { // prettier-ignore const buildCmd = await buildx.getCommand([ '--builder', process.env.CTN_BUILDER_NAME ?? 'default', ...bargs, '--metadata-file', build.getMetadataFilePath() ]); await Exec.exec(buildCmd.command, buildCmd.args); })() ).resolves.not.toThrow(); const metadata = build.resolveMetadata(); expect(metadata).toBeDefined(); const buildRef = build.resolveRef(metadata); expect(buildRef).toBeDefined(); const history = new History({buildx: buildx}); const exportRes = await history.export({ refs: [buildRef ?? ''] }); expect(exportRes).toBeDefined(); expect(exportRes?.dockerbuildFilename).toBeDefined(); expect(exportRes?.dockerbuildSize).toBeDefined(); expect(exportRes?.summaries).toBeDefined(); const uploadRes = await GitHub.uploadArtifact({ filename: exportRes?.dockerbuildFilename, mimeType: 'application/gzip', retentionDays: 1 }); expect(uploadRes).toBeDefined(); expect(uploadRes?.url).toBeDefined(); await GitHub.writeBuildSummary({ exportRes: exportRes, uploadRes: uploadRes, inputs: { context: fixturesDir, file: path.join(fixturesDir, 'hello.Dockerfile') } }); }); // prettier-ignore test.each([ [ 'single', path.join(fixturesDir, 'hello-bake.hcl'), 'hello' ], [ 'group', path.join(fixturesDir, 'hello-bake.hcl'), 'hello-all' ], [ 'matrix', path.join(fixturesDir, 'hello-bake.hcl'), 'hello-matrix' ] ])('write bake summary %p', async (_, file, target) => { const buildx = new Buildx(); const bake = new Bake({buildx: buildx}); fs.mkdirSync(tmpDir, {recursive: true}); await expect( (async () => { // prettier-ignore const buildCmd = await buildx.getCommand([ '--builder', process.env.CTN_BUILDER_NAME ?? 'default', 'bake', '-f', file, target, '--metadata-file', bake.getMetadataFilePath() ]); await Exec.exec(buildCmd.command, buildCmd.args, { cwd: fixturesDir }); })() ).resolves.not.toThrow(); const definition = await bake.getDefinition( { files: [file], targets: [target], }, { cwd: fixturesDir } ); const metadata = bake.resolveMetadata(); expect(metadata).toBeDefined(); const buildRefs = bake.resolveRefs(metadata); expect(buildRefs).toBeDefined(); const history = new History({buildx: buildx}); const exportRes = await history.export({ refs: buildRefs ?? [] }); expect(exportRes).toBeDefined(); expect(exportRes?.dockerbuildFilename).toBeDefined(); expect(exportRes?.dockerbuildSize).toBeDefined(); expect(exportRes?.summaries).toBeDefined(); const uploadRes = await GitHub.uploadArtifact({ filename: exportRes?.dockerbuildFilename, mimeType: 'application/gzip', retentionDays: 1 }); expect(uploadRes).toBeDefined(); expect(uploadRes?.url).toBeDefined(); await GitHub.writeBuildSummary({ exportRes: exportRes, uploadRes: uploadRes, inputs: { files: path.join(fixturesDir, 'hello-bake.hcl') }, bakeDefinition: definition }); }); it('fails with dockerfile syntax issue', async () => { const startedTime = new Date(); const buildx = new Buildx(); const build = new Build({buildx: buildx}); fs.mkdirSync(tmpDir, {recursive: true}); await expect( (async () => { // prettier-ignore const buildCmd = await buildx.getCommand([ '--builder', process.env.CTN_BUILDER_NAME ?? 'default', 'build', '-f', path.join(fixturesDir, 'hello-err.Dockerfile'), fixturesDir, '--metadata-file', build.getMetadataFilePath() ]); await Exec.exec(buildCmd.command, buildCmd.args); })() ).rejects.toThrow(); const refs = Buildx.refs({ dir: Buildx.refsDir, builderName: process.env.CTN_BUILDER_NAME ?? 'default', since: startedTime }); expect(refs).toBeDefined(); expect(Object.keys(refs).length).toBeGreaterThan(0); const history = new History({buildx: buildx}); const exportRes = await history.export({ refs: [Object.keys(refs)[0] ?? ''] }); expect(exportRes).toBeDefined(); expect(exportRes?.dockerbuildFilename).toBeDefined(); expect(exportRes?.dockerbuildSize).toBeDefined(); expect(exportRes?.summaries).toBeDefined(); const uploadRes = await GitHub.uploadArtifact({ filename: exportRes?.dockerbuildFilename, mimeType: 'application/gzip', retentionDays: 1 }); expect(uploadRes).toBeDefined(); expect(uploadRes?.url).toBeDefined(); await GitHub.writeBuildSummary({ exportRes: exportRes, uploadRes: uploadRes, inputs: { context: fixturesDir, file: path.join(fixturesDir, 'hello-err.Dockerfile') } }); }); it('without build record', async () => { const startedTime = new Date(); const buildx = new Buildx(); const build = new Build({buildx: buildx}); fs.mkdirSync(tmpDir, {recursive: true}); await expect( (async () => { // prettier-ignore const buildCmd = await buildx.getCommand([ '--builder', process.env.CTN_BUILDER_NAME ?? 'default', 'build', '-f', path.join(fixturesDir, 'hello.Dockerfile'), fixturesDir, '--metadata-file', build.getMetadataFilePath() ]); await Exec.exec(buildCmd.command, buildCmd.args); })() ).resolves.not.toThrow(); const refs = Buildx.refs({ dir: Buildx.refsDir, builderName: process.env.CTN_BUILDER_NAME ?? 'default', since: startedTime }); expect(refs).toBeDefined(); expect(Object.keys(refs).length).toBeGreaterThan(0); const history = new History({buildx: buildx}); const exportRes = await history.export({ refs: [Object.keys(refs)[0] ?? ''] }); expect(exportRes).toBeDefined(); expect(exportRes?.dockerbuildFilename).toBeDefined(); expect(exportRes?.dockerbuildSize).toBeDefined(); expect(exportRes?.summaries).toBeDefined(); await GitHub.writeBuildSummary({ exportRes: exportRes, inputs: { context: fixturesDir, file: path.join(fixturesDir, 'hello.Dockerfile') } }); }); }); maybe('annotateBuildWarnings', () => { it('annoate lint issues', async () => { const buildx = new Buildx(); const build = new Build({buildx: buildx}); fs.mkdirSync(tmpDir, {recursive: true}); await expect( (async () => { // prettier-ignore const buildCmd = await buildx.getCommand([ '--builder', process.env.CTN_BUILDER_NAME ?? 'default', 'build', '-f', path.join(fixturesDir, 'lint.Dockerfile'), fixturesDir, '--metadata-file', build.getMetadataFilePath() ]); await Exec.exec(buildCmd.command, buildCmd.args, { env: Object.assign({}, process.env, { BUILDX_METADATA_WARNINGS: 'true' }) as { [key: string]: string; } }); })() ).resolves.not.toThrow(); const metadata = build.resolveMetadata(); expect(metadata).toBeDefined(); const buildRef = build.resolveRef(metadata); expect(buildRef).toBeDefined(); const buildWarnings = build.resolveWarnings(metadata); expect(buildWarnings).toBeDefined(); await GitHub.annotateBuildWarnings(path.join(fixturesDir, 'lint.Dockerfile'), buildWarnings); }); });