@@ -28,7 +28,7 @@ import {Exec} from '../exec';
2828import { GitHub } from '../github' ;
2929import { Util } from '../util' ;
3030
31- import { ExportRecordOpts , ExportRecordResponse , Summaries } from '../types/buildx/history' ;
31+ import { ExportOpts , ExportResponse , InspectOpts , InspectResponse , Summaries } from '../types/buildx/history' ;
3232
3333export interface HistoryOpts {
3434 buildx ?: Buildx ;
@@ -37,27 +37,43 @@ export interface HistoryOpts {
3737export class History {
3838 private readonly buildx : Buildx ;
3939
40- private static readonly EXPORT_BUILD_IMAGE_DEFAULT : string = 'docker.io/dockereng/export-build:latest' ;
41- private static readonly EXPORT_BUILD_IMAGE_ENV : string = 'DOCKER_BUILD_EXPORT_BUILD_IMAGE' ;
42-
4340 constructor ( opts ?: HistoryOpts ) {
4441 this . buildx = opts ?. buildx || new Buildx ( ) ;
4542 }
4643
47- public async export ( opts : ExportRecordOpts ) : Promise < ExportRecordResponse > {
48- if ( os . platform ( ) === 'win32' ) {
49- throw new Error ( 'Exporting a build record is currently not supported on Windows' ) ;
50- }
51- if ( ! ( await Docker . isAvailable ( ) ) ) {
52- throw new Error ( 'Docker is required to export a build record' ) ;
44+ public async getCommand ( args : Array < string > ) {
45+ return await this . buildx . getCommand ( [ 'history' , ...args ] ) ;
46+ }
47+
48+ public async getInspectCommand ( args : Array < string > ) {
49+ return await this . getCommand ( [ 'inspect' , ...args ] ) ;
50+ }
51+
52+ public async getExportCommand ( args : Array < string > ) {
53+ return await this . getCommand ( [ 'export' , ...args ] ) ;
54+ }
55+
56+ public async inspect ( opts : InspectOpts ) : Promise < InspectResponse > {
57+ const args : Array < string > = [ '--format' , 'json' ] ;
58+ if ( opts . builder ) {
59+ args . push ( '--builder' , opts . builder ) ;
5360 }
54- if ( ! ( await Docker . isDaemonRunning ( ) ) ) {
55- throw new Error ( 'Docker daemon needs to be running to export a build record' ) ;
56- }
57- if ( ! ( await this . buildx . versionSatisfies ( '>=0.13.0' ) ) ) {
58- throw new Error ( 'Buildx >= 0.13.0 is required to export a build record' ) ;
61+ if ( opts . ref ) {
62+ args . push ( opts . ref ) ;
5963 }
64+ const cmd = await this . getInspectCommand ( args ) ;
65+ return await Exec . getExecOutput ( cmd . command , cmd . args , {
66+ ignoreReturnCode : true ,
67+ silent : true
68+ } ) . then ( res => {
69+ if ( res . stderr . length > 0 && res . exitCode != 0 ) {
70+ throw new Error ( res . stderr . trim ( ) ) ;
71+ }
72+ return < InspectResponse > JSON . parse ( res . stdout ) ;
73+ } ) ;
74+ }
6075
76+ public async export ( opts : ExportOpts ) : Promise < ExportResponse > {
6177 let builderName : string = '' ;
6278 let nodeName : string = '' ;
6379 const refs : Array < string > = [ ] ;
@@ -85,6 +101,72 @@ export class History {
85101 core . info ( `exporting build record to ${ outDir } ` ) ;
86102 fs . mkdirSync ( outDir , { recursive : true } ) ;
87103
104+ if ( opts . useContainer || ( await this . buildx . versionSatisfies ( '<0.23.0' ) ) ) {
105+ return await this . exportLegacy ( builderName , nodeName , refs , outDir , opts . image ) ;
106+ }
107+
108+ // wait 3 seconds to ensure build records are finalized: https://github.com/moby/buildkit/pull/5109
109+ await Util . sleep ( 3 ) ;
110+
111+ const summaries : Summaries = { } ;
112+ if ( ! opts . noSummaries ) {
113+ for ( const ref of refs ) {
114+ await this . inspect ( {
115+ ref : ref ,
116+ builder : builderName
117+ } ) . then ( res => {
118+ let errorLogs = '' ;
119+ if ( res . Error && res . Status !== 'canceled' ) {
120+ if ( res . Error . Message ) {
121+ errorLogs = res . Error . Message ;
122+ } else if ( res . Error . Name && res . Error . Logs ) {
123+ errorLogs = `=> ${ res . Error . Name } \n${ res . Error . Logs } ` ;
124+ }
125+ }
126+ summaries [ ref ] = {
127+ name : res . Name ,
128+ status : res . Status ,
129+ duration : Util . formatDuration ( res . Duration ) ,
130+ numCachedSteps : res . NumCachedSteps ,
131+ numTotalSteps : res . NumTotalSteps ,
132+ numCompletedSteps : res . NumCompletedSteps ,
133+ error : errorLogs
134+ } ;
135+ } ) ;
136+ }
137+ }
138+
139+ const dockerbuildPath = path . join ( outDir , `${ History . exportFilename ( refs ) } .dockerbuild` ) ;
140+
141+ const cmd = await this . getExportCommand ( [ '--builder' , builderName , '--output' , dockerbuildPath , ...refs ] ) ;
142+ await Exec . getExecOutput ( cmd . command , cmd . args ) ;
143+
144+ const dockerbuildStats = fs . statSync ( dockerbuildPath ) ;
145+
146+ return {
147+ dockerbuildFilename : dockerbuildPath ,
148+ dockerbuildSize : dockerbuildStats . size ,
149+ builderName : builderName ,
150+ nodeName : nodeName ,
151+ refs : refs ,
152+ summaries : summaries
153+ } ;
154+ }
155+
156+ private async exportLegacy ( builderName : string , nodeName : string , refs : Array < string > , outDir : string , image ?: string ) : Promise < ExportResponse > {
157+ if ( os . platform ( ) === 'win32' ) {
158+ throw new Error ( 'Exporting a build record is currently not supported on Windows' ) ;
159+ }
160+ if ( ! ( await Docker . isAvailable ( ) ) ) {
161+ throw new Error ( 'Docker is required to export a build record' ) ;
162+ }
163+ if ( ! ( await Docker . isDaemonRunning ( ) ) ) {
164+ throw new Error ( 'Docker daemon needs to be running to export a build record' ) ;
165+ }
166+ if ( ! ( await this . buildx . versionSatisfies ( '>=0.13.0' ) ) ) {
167+ throw new Error ( 'Buildx >= 0.13.0 is required to export a build record' ) ;
168+ }
169+
88170 // wait 3 seconds to ensure build records are finalized: https://github.com/moby/buildkit/pull/5109
89171 await Util . sleep ( 3 ) ;
90172
@@ -139,7 +221,7 @@ export class History {
139221 'run' , '--rm' , '-i' ,
140222 '-v' , `${ Buildx . refsDir } :/buildx-refs` ,
141223 '-v' , `${ outDir } :/out` ,
142- opts . image || process . env [ History . EXPORT_BUILD_IMAGE_ENV ] || History . EXPORT_BUILD_IMAGE_DEFAULT ,
224+ image || process . env [ 'DOCKER_BUILD_EXPORT_BUILD_IMAGE' ] || 'docker.io/dockereng/export-build:latest' ,
143225 ...ebargs
144226 ]
145227 core . info ( `[command]docker ${ dockerRunArgs . join ( ' ' ) } ` ) ;
@@ -190,12 +272,7 @@ export class History {
190272 }
191273 } ) ;
192274
193- let dockerbuildFilename = `${ GitHub . context . repo . owner } ~${ GitHub . context . repo . repo } ~${ refs [ 0 ] . substring ( 0 , 6 ) . toUpperCase ( ) } ` ;
194- if ( refs . length > 1 ) {
195- dockerbuildFilename += `+${ refs . length - 1 } ` ;
196- }
197-
198- const dockerbuildPath = path . join ( outDir , `${ dockerbuildFilename } .dockerbuild` ) ;
275+ const dockerbuildPath = path . join ( outDir , `${ History . exportFilename ( refs ) } .dockerbuild` ) ;
199276 fs . renameSync ( tmpDockerbuildFilename , dockerbuildPath ) ;
200277 const dockerbuildStats = fs . statSync ( dockerbuildPath ) ;
201278
@@ -212,4 +289,12 @@ export class History {
212289 refs : refs
213290 } ;
214291 }
292+
293+ private static exportFilename ( refs : Array < string > ) : string {
294+ let name = `${ GitHub . context . repo . owner } ~${ GitHub . context . repo . repo } ~${ refs [ 0 ] . substring ( 0 , 6 ) . toUpperCase ( ) } ` ;
295+ if ( refs . length > 1 ) {
296+ name += `+${ refs . length - 1 } ` ;
297+ }
298+ return name ;
299+ }
215300}
0 commit comments