diff --git a/__tests__/buildx/history.test.itg.ts b/__tests__/buildx/history.test.itg.ts index 52e4895..7416ef6 100644 --- a/__tests__/buildx/history.test.itg.ts +++ b/__tests__/buildx/history.test.itg.ts @@ -30,6 +30,48 @@ const tmpDir = fs.mkdtempSync(path.join(process.env.TEMP || os.tmpdir(), 'buildx const maybe = !process.env.GITHUB_ACTIONS || (process.env.GITHUB_ACTIONS === 'true' && process.env.ImageOS && process.env.ImageOS.startsWith('ubuntu')) ? describe : describe.skip; +maybe('inspect', () => { + it('build', 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, 'hello.Dockerfile'), + '--metadata-file', build.getMetadataFilePath(), + fixturesDir + ]); + await Exec.exec(buildCmd.command, buildCmd.args); + })() + ).resolves.not.toThrow(); + + const metadata = build.resolveMetadata(); + expect(metadata).toBeDefined(); + const buildRef = build.resolveRef(metadata); + if (!buildRef) { + throw new Error('buildRef is undefined'); + } + const [builderName, nodeName, ref] = buildRef.split('/'); + expect(builderName).toBeDefined(); + expect(nodeName).toBeDefined(); + expect(ref).toBeDefined(); + + const history = new History({buildx: buildx}); + const res = await history.inspect({ + ref: ref, + builder: builderName + }); + + expect(res).toBeDefined(); + expect(res?.Name).toBeDefined(); + expect(res?.Ref).toBeDefined(); + }); +}); + maybe('exportBuild', () => { // prettier-ignore test.each([ diff --git a/src/buildx/history.ts b/src/buildx/history.ts index 13dc886..eb5a21b 100644 --- a/src/buildx/history.ts +++ b/src/buildx/history.ts @@ -28,7 +28,7 @@ import {Exec} from '../exec'; import {GitHub} from '../github'; import {Util} from '../util'; -import {ExportRecordOpts, ExportRecordResponse, Summaries} from '../types/buildx/history'; +import {ExportBuildOpts, ExportBuildResponse, InspectOpts, InspectResponse, Summaries} from '../types/buildx/history'; export interface HistoryOpts { buildx?: Buildx; @@ -44,7 +44,35 @@ export class History { this.buildx = opts?.buildx || new Buildx(); } - public async export(opts: ExportRecordOpts): Promise { + public async getCommand(args: Array) { + return await this.buildx.getCommand(['history', ...args]); + } + + public async getInspectCommand(args: Array) { + return await this.getCommand(['inspect', ...args]); + } + + public async inspect(opts: InspectOpts): Promise { + const args: Array = ['--format', 'json']; + if (opts.builder) { + args.push('--builder', opts.builder); + } + if (opts.ref) { + args.push(opts.ref); + } + const cmd = await this.getInspectCommand(args); + return await Exec.getExecOutput(cmd.command, cmd.args, { + ignoreReturnCode: true, + silent: true + }).then(res => { + if (res.stderr.length > 0 && res.exitCode != 0) { + throw new Error(res.stderr.trim()); + } + return JSON.parse(res.stdout); + }); + } + + public async export(opts: ExportBuildOpts): Promise { if (os.platform() === 'win32') { throw new Error('Exporting a build record is currently not supported on Windows'); } diff --git a/src/types/buildx/history.ts b/src/types/buildx/history.ts index 67fbe68..42ab199 100644 --- a/src/types/buildx/history.ts +++ b/src/types/buildx/history.ts @@ -14,12 +14,101 @@ * limitations under the License. */ -export interface ExportRecordOpts { +export interface InspectOpts { + ref?: string; + builder?: string; +} + +export type BuildStatus = 'completed' | 'running' | 'failed' | 'canceled'; + +export interface InspectResponse { + Name?: string; + Ref: string; + + Context?: string; + Dockerfile?: string; + VCSRepository?: string; + VCSRevision?: string; + Target?: string; + Platform?: Array; + KeepGitDir?: boolean; + + NamedContexts?: Array; + + StartedAt?: Date; + CompletedAt?: Date; + Duration?: number; + Status?: BuildStatus; + Error?: InspectErrorOutput; + + NumCompletedSteps: number; + NumTotalSteps: number; + NumCachedSteps: number; + + BuildArgs?: Array; + Labels?: Array; + + Config?: InspectConfigOutput; + + Materials?: InspectMaterialOutput[]; + Attachments?: InspectAttachmentOutput[]; + + Errors?: Array; +} + +export interface InspectConfigOutput { + Network?: string; + ExtraHosts?: Array; + Hostname?: string; + CgroupParent?: string; + ImageResolveMode?: string; + MultiPlatform?: boolean; + NoCache?: boolean; + NoCacheFilter?: Array; + + ShmSize?: string; + Ulimit?: string; + CacheMountNS?: string; + DockerfileCheckConfig?: string; + SourceDateEpoch?: string; + SandboxHostname?: string; + + RestRaw?: Array; +} + +export interface InspectMaterialOutput { + URI?: string; + Digests?: Array; +} + +export interface InspectAttachmentOutput { + Digest?: string; + Platform?: string; + Type?: string; +} + +export interface InspectErrorOutput { + Code?: number; + Message?: string; + Name?: string; + Logs?: Array; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + Sources?: any; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + Stack?: any; +} + +export interface InspectKeyValueOutput { + Name?: string; + Value?: string; +} + +export interface ExportBuildOpts { refs: Array; image?: string; } -export interface ExportRecordResponse { +export interface ExportBuildResponse { dockerbuildFilename: string; dockerbuildSize: number; summaries: Summaries; @@ -29,10 +118,10 @@ export interface ExportRecordResponse { } export interface Summaries { - [ref: string]: RecordSummary; + [ref: string]: Summary; } -export interface RecordSummary { +export interface Summary { name: string; status: string; duration: string;