2020-09-02 10:07:11 +02:00
import * as fs from 'fs' ;
2023-09-08 15:28:08 +02:00
import * as path from 'path' ;
2020-09-02 10:07:11 +02:00
import * as stateHelper from './state-helper' ;
2020-08-16 00:36:41 +02:00
import * as core from '@actions/core' ;
2023-02-20 11:11:15 +01:00
import * as actionsToolkit from '@docker/actions-toolkit' ;
2024-04-26 11:20:49 +02:00
2024-05-02 13:49:01 +02:00
import { Buildx } from '@docker/actions-toolkit/lib/buildx/buildx' ;
import { History as BuildxHistory } from '@docker/actions-toolkit/lib/buildx/history' ;
2023-02-20 11:11:15 +01:00
import { Context } from '@docker/actions-toolkit/lib/context' ;
2023-03-12 23:34:24 +01:00
import { Docker } from '@docker/actions-toolkit/lib/docker/docker' ;
2023-02-20 11:11:15 +01:00
import { Exec } from '@docker/actions-toolkit/lib/exec' ;
import { GitHub } from '@docker/actions-toolkit/lib/github' ;
import { Toolkit } from '@docker/actions-toolkit/lib/toolkit' ;
2024-05-02 13:49:01 +02:00
import { Util } from '@docker/actions-toolkit/lib/util' ;
2024-04-26 11:20:49 +02:00
2024-06-27 11:26:35 +02:00
import { BuilderInfo } from '@docker/actions-toolkit/lib/types/buildx/builder' ;
2024-05-27 12:45:25 +02:00
import { ConfigFile } from '@docker/actions-toolkit/lib/types/docker/docker' ;
2024-07-02 18:07:54 +02:00
import { UploadArtifactResponse } from '@docker/actions-toolkit/lib/types/github' ;
2020-08-16 00:36:41 +02:00
2023-02-20 11:11:15 +01:00
import * as context from './context' ;
2022-04-28 09:31:47 +02:00
2023-02-20 11:11:15 +01:00
actionsToolkit . run (
// main
async ( ) = > {
2024-05-02 13:49:01 +02:00
const startedTime = new Date ( ) ;
2023-04-17 01:32:21 +02:00
const inputs : context.Inputs = await context . getInputs ( ) ;
2025-04-09 13:26:53 +02:00
stateHelper . setSummaryInputs ( inputs ) ;
2024-03-06 14:20:33 +01:00
core . debug ( ` inputs: ${ JSON . stringify ( inputs ) } ` ) ;
2023-02-20 11:11:15 +01:00
const toolkit = new Toolkit ( ) ;
2022-04-28 09:31:47 +02:00
2023-02-20 11:11:15 +01:00
await core . group ( ` GitHub Actions runtime token ACs ` , async ( ) = > {
try {
await GitHub . printActionsRuntimeTokenACs ( ) ;
} catch ( e ) {
core . warning ( e . message ) ;
2023-01-12 19:44:15 +01:00
}
} ) ;
2023-02-20 11:11:15 +01:00
await core . group ( ` Docker info ` , async ( ) = > {
try {
await Docker . printVersion ( ) ;
await Docker . printInfo ( ) ;
} catch ( e ) {
core . info ( e . message ) ;
}
} ) ;
2020-08-16 00:36:41 +02:00
2023-09-08 15:28:08 +02:00
await core . group ( ` Proxy configuration ` , async ( ) = > {
let dockerConfig : ConfigFile | undefined ;
let dockerConfigMalformed = false ;
try {
2025-04-30 13:56:09 +02:00
dockerConfig = Docker . configFile ( ) ;
2023-09-08 15:28:08 +02:00
} catch ( e ) {
dockerConfigMalformed = true ;
core . warning ( ` Unable to parse config file ${ path . join ( Docker . configDir , 'config.json' ) } : ${ e } ` ) ;
}
if ( dockerConfig && dockerConfig . proxies ) {
2023-06-13 11:46:07 +02:00
for ( const host in dockerConfig . proxies ) {
let prefix = '' ;
2023-09-07 12:21:01 +02:00
if ( Object . keys ( dockerConfig . proxies ) . length > 1 ) {
2023-06-13 11:46:07 +02:00
prefix = ' ' ;
core . info ( host ) ;
}
for ( const key in dockerConfig . proxies [ host ] ) {
core . info ( ` ${ prefix } ${ key } : ${ dockerConfig . proxies [ host ] [ key ] } ` ) ;
}
}
2023-09-08 15:28:08 +02:00
} else if ( ! dockerConfigMalformed ) {
core . info ( 'No proxy configuration found' ) ;
}
} ) ;
2023-06-13 11:46:07 +02:00
2023-02-20 11:11:15 +01:00
if ( ! ( await toolkit . buildx . isAvailable ( ) ) ) {
2021-04-27 16:16:22 +02:00
core . setFailed ( ` Docker buildx is required. See https://github.com/docker/setup-buildx-action to set up buildx. ` ) ;
return ;
2020-08-16 00:36:41 +02:00
}
2023-02-20 11:11:15 +01:00
stateHelper . setTmpDir ( Context . tmpDir ( ) ) ;
2022-04-28 09:31:47 +02:00
await core . group ( ` Buildx version ` , async ( ) = > {
2023-02-20 11:11:15 +01:00
await toolkit . buildx . printVersion ( ) ;
2022-04-28 09:31:47 +02:00
} ) ;
2020-08-16 22:31:37 +02:00
2024-06-27 11:26:35 +02:00
let builder : BuilderInfo ;
2024-05-29 12:52:50 +02:00
await core . group ( ` Builder info ` , async ( ) = > {
2024-06-27 11:26:35 +02:00
builder = await toolkit . builder . inspect ( inputs . builder ) ;
2024-05-29 12:52:50 +02:00
core . info ( JSON . stringify ( builder , null , 2 ) ) ;
} ) ;
2023-02-20 11:11:15 +01:00
const args : string [ ] = await context . getArgs ( inputs , toolkit ) ;
2024-03-06 14:20:33 +01:00
core . debug ( ` context.getArgs: ${ JSON . stringify ( args ) } ` ) ;
2023-02-20 11:11:15 +01:00
const buildCmd = await toolkit . buildx . getCommand ( args ) ;
2024-03-06 14:20:33 +01:00
core . debug ( ` buildCmd.command: ${ buildCmd . command } ` ) ;
core . debug ( ` buildCmd.args: ${ JSON . stringify ( buildCmd . args ) } ` ) ;
2024-05-02 13:49:01 +02:00
let err : Error | undefined ;
2023-02-20 11:11:15 +01:00
await Exec . getExecOutput ( buildCmd . command , buildCmd . args , {
2024-07-30 17:48:59 +02:00
ignoreReturnCode : true ,
env : Object.assign ( { } , process . env , {
BUILDX_METADATA_WARNINGS : 'true'
} ) as {
[ key : string ] : string ;
}
2023-02-20 11:11:15 +01:00
} ) . then ( res = > {
2024-11-25 17:14:29 +01:00
if ( res . exitCode != 0 ) {
if ( inputs . call && inputs . call === 'check' && res . stdout . length > 0 ) {
// checks warnings are printed to stdout: https://github.com/docker/buildx/pull/2647
// take the first line with the message summaryzing the warnings
2025-04-01 11:36:44 +09:00
err = new Error ( res . stdout . split ( '\n' ) [ 0 ] ? . trim ( ) ) ;
2024-11-25 17:14:29 +01:00
} else if ( res . stderr . length > 0 ) {
2025-04-01 11:36:44 +09:00
err = new Error ( ` buildx failed with: ${ res . stderr . match ( /(.*)\s*$/ ) ? . [ 0 ] ? . trim ( ) ? ? 'unknown error' } ` ) ;
2024-11-25 17:14:29 +01:00
}
2023-02-20 11:11:15 +01:00
}
} ) ;
2020-08-17 22:18:15 +02:00
2024-05-14 14:11:32 +02:00
const imageID = toolkit . buildxBuild . resolveImageID ( ) ;
const metadata = toolkit . buildxBuild . resolveMetadata ( ) ;
2024-07-30 17:48:59 +02:00
const digest = toolkit . buildxBuild . resolveDigest ( metadata ) ;
2022-02-09 11:32:35 +01:00
if ( imageID ) {
2022-03-14 19:30:50 +01:00
await core . group ( ` ImageID ` , async ( ) = > {
2022-02-09 11:32:35 +01:00
core . info ( imageID ) ;
2022-10-12 06:56:31 +02:00
core . setOutput ( 'imageid' , imageID ) ;
2022-03-14 19:30:50 +01:00
} ) ;
}
if ( digest ) {
await core . group ( ` Digest ` , async ( ) = > {
core . info ( digest ) ;
2022-10-12 06:56:31 +02:00
core . setOutput ( 'digest' , digest ) ;
2022-02-09 11:32:35 +01:00
} ) ;
}
if ( metadata ) {
2022-03-14 19:30:50 +01:00
await core . group ( ` Metadata ` , async ( ) = > {
2024-04-26 11:20:49 +02:00
const metadatadt = JSON . stringify ( metadata , null , 2 ) ;
core . info ( metadatadt ) ;
core . setOutput ( 'metadata' , metadatadt ) ;
2022-02-09 11:32:35 +01:00
} ) ;
}
2024-06-27 11:26:35 +02:00
2024-07-30 17:48:59 +02:00
let ref : string | undefined ;
2024-05-02 13:49:01 +02:00
await core . group ( ` Reference ` , async ( ) = > {
2024-06-27 11:26:35 +02:00
ref = await buildRef ( toolkit , startedTime , inputs . builder ) ;
2024-05-02 13:49:01 +02:00
if ( ref ) {
core . info ( ref ) ;
stateHelper . setBuildRef ( ref ) ;
} else {
2024-06-27 11:26:35 +02:00
core . info ( 'No build reference found' ) ;
2024-05-02 13:49:01 +02:00
}
} ) ;
2024-06-27 11:26:35 +02:00
2024-07-31 12:39:27 +02:00
if ( buildChecksAnnotationsEnabled ( ) ) {
const warnings = toolkit . buildxBuild . resolveWarnings ( metadata ) ;
if ( ref && warnings && warnings . length > 0 ) {
const annotations = await Buildx . convertWarningsToGitHubAnnotations ( warnings , [ ref ] ) ;
core . debug ( ` annotations: ${ JSON . stringify ( annotations , null , 2 ) } ` ) ;
if ( annotations && annotations . length > 0 ) {
await core . group ( ` Generating GitHub annotations ( ${ annotations . length } build checks found) ` , async ( ) = > {
for ( const annotation of annotations ) {
core . warning ( annotation . message , annotation ) ;
}
} ) ;
}
2024-07-30 17:48:59 +02:00
}
}
2024-06-27 11:26:35 +02:00
await core . group ( ` Check build summary support ` , async ( ) = > {
2024-07-02 17:38:24 +02:00
if ( ! buildSummaryEnabled ( ) ) {
2024-06-27 11:26:35 +02:00
core . info ( 'Build summary disabled' ) ;
2024-11-25 17:14:29 +01:00
} else if ( inputs . call && inputs . call !== 'build' ) {
core . info ( ` Build summary skipped for ${ inputs . call } subrequest ` ) ;
2024-06-27 11:26:35 +02:00
} else if ( GitHub . isGHES ) {
2024-08-13 11:05:41 +02:00
core . info ( 'Build summary is not yet supported on GHES' ) ;
2024-06-27 11:26:35 +02:00
} else if ( ! ( await toolkit . buildx . versionSatisfies ( '>=0.13.0' ) ) ) {
2024-08-13 11:05:41 +02:00
core . info ( 'Build summary requires Buildx >= 0.13.0' ) ;
2024-06-27 11:26:35 +02:00
} else if ( builder && builder . driver === 'cloud' ) {
2025-04-30 13:56:09 +02:00
core . info ( 'Build summary supported for cloud driver!' ) ;
stateHelper . setSummaryType ( 'cloud' ) ;
2024-06-27 11:26:35 +02:00
} else if ( ! ref ) {
2024-08-13 11:05:41 +02:00
core . info ( 'Build summary requires a build reference' ) ;
2024-06-27 11:26:35 +02:00
} else {
core . info ( 'Build summary supported!' ) ;
2025-04-30 13:56:09 +02:00
stateHelper . setSummaryType ( 'buildx' ) ;
2024-06-27 11:26:35 +02:00
}
} ) ;
2024-05-02 13:49:01 +02:00
if ( err ) {
throw err ;
}
2023-02-20 11:11:15 +01:00
} ,
// post
async ( ) = > {
2025-04-30 13:56:09 +02:00
if ( stateHelper . summaryType === 'buildx' ) {
2024-05-02 13:49:01 +02:00
await core . group ( ` Generating build summary ` , async ( ) = > {
2024-05-02 13:49:01 +02:00
try {
2024-07-02 18:07:54 +02:00
const recordUploadEnabled = buildRecordUploadEnabled ( ) ;
2024-07-02 18:09:36 +02:00
let recordRetentionDays : number | undefined ;
2024-07-02 18:07:54 +02:00
if ( recordUploadEnabled ) {
2024-07-02 18:09:36 +02:00
recordRetentionDays = buildRecordRetentionDays ( ) ;
2024-07-02 18:07:54 +02:00
}
2024-05-02 13:49:01 +02:00
const buildxHistory = new BuildxHistory ( ) ;
const exportRes = await buildxHistory . export ( {
2024-06-27 11:26:35 +02:00
refs : stateHelper.buildRef ? [ stateHelper . buildRef ] : [ ]
2024-05-02 13:49:01 +02:00
} ) ;
2024-07-02 18:07:54 +02:00
core . info ( ` Build record written to ${ exportRes . dockerbuildFilename } ( ${ Util . formatFileSize ( exportRes . dockerbuildSize ) } ) ` ) ;
let uploadRes : UploadArtifactResponse | undefined ;
if ( recordUploadEnabled ) {
uploadRes = await GitHub . uploadArtifact ( {
filename : exportRes.dockerbuildFilename ,
mimeType : 'application/gzip' ,
2024-07-02 18:09:36 +02:00
retentionDays : recordRetentionDays
2024-07-02 18:07:54 +02:00
} ) ;
}
2024-05-02 13:49:01 +02:00
await GitHub . writeBuildSummary ( {
exportRes : exportRes ,
uploadRes : uploadRes ,
2025-04-09 13:26:53 +02:00
inputs : stateHelper.summaryInputs
2024-05-02 13:49:01 +02:00
} ) ;
2024-05-02 13:49:01 +02:00
} catch ( e ) {
core . warning ( e . message ) ;
}
} ) ;
2025-04-30 13:56:09 +02:00
} else if ( stateHelper . summaryType === 'cloud' && stateHelper . buildRef ) {
const [ , platform , refId ] = stateHelper . buildRef . split ( '/' ) ;
if ( platform && refId ) {
const buildUrl = ` https://app.docker.com/build/accounts/docker/builds/ ${ platform } / ${ refId } ` ;
core . info ( ` View build details: ${ buildUrl } ` ) ;
const sum = core . summary . addHeading ( 'Docker Build Cloud summary' , 2 ) ;
sum . addRaw ( '<p>' ) . addRaw ( 'Your build was executed using Docker Build Cloud. ' ) . addRaw ( 'You can view detailed build information, logs, and results here: ' ) . addLink ( buildUrl , buildUrl ) . addRaw ( '</p>' ) ;
sum . addRaw ( '<p>' ) . addRaw ( 'For more information about Docker Build Cloud, see ' ) . addLink ( 'the documentation' , 'https://docs.docker.com/build/cloud/' ) . addRaw ( '.' ) . addRaw ( '</p>' ) ;
await sum . addSeparator ( ) . write ( ) ;
}
2024-05-02 13:49:01 +02:00
}
2023-02-20 11:11:15 +01:00
if ( stateHelper . tmpDir . length > 0 ) {
await core . group ( ` Removing temp folder ${ stateHelper . tmpDir } ` , async ( ) = > {
2025-04-01 11:36:44 +09:00
try {
fs . rmSync ( stateHelper . tmpDir , { recursive : true } ) ;
} catch ( e ) {
core . warning ( ` Failed to remove temp folder ${ stateHelper . tmpDir } ` ) ;
}
2023-02-20 11:11:15 +01:00
} ) ;
}
2020-09-02 10:07:11 +02:00
}
2023-02-20 11:11:15 +01:00
) ;
2024-05-02 13:49:01 +02:00
async function buildRef ( toolkit : Toolkit , since : Date , builder? : string ) : Promise < string > {
// get ref from metadata file
const ref = toolkit . buildxBuild . resolveRef ( ) ;
if ( ref ) {
return ref ;
}
// otherwise, look for the very first build ref since the build has started
if ( ! builder ) {
const currentBuilder = await toolkit . builder . inspect ( ) ;
builder = currentBuilder . name ;
}
const refs = Buildx . refs ( {
dir : Buildx.refsDir ,
builderName : builder ,
since : since
} ) ;
return Object . keys ( refs ) . length > 0 ? Object . keys ( refs ) [ 0 ] : '' ;
}
2024-06-24 10:14:14 +02:00
2024-07-31 12:39:27 +02:00
function buildChecksAnnotationsEnabled ( ) : boolean {
if ( process . env . DOCKER_BUILD_CHECKS_ANNOTATIONS ) {
return Util . parseBool ( process . env . DOCKER_BUILD_CHECKS_ANNOTATIONS ) ;
}
return true ;
}
2024-07-02 17:38:24 +02:00
function buildSummaryEnabled ( ) : boolean {
2024-07-02 14:49:31 +02:00
if ( process . env . DOCKER_BUILD_NO_SUMMARY ) {
2024-07-02 17:38:24 +02:00
core . warning ( 'DOCKER_BUILD_NO_SUMMARY is deprecated. Set DOCKER_BUILD_SUMMARY to false instead.' ) ;
return ! Util . parseBool ( process . env . DOCKER_BUILD_NO_SUMMARY ) ;
} else if ( process . env . DOCKER_BUILD_SUMMARY ) {
return Util . parseBool ( process . env . DOCKER_BUILD_SUMMARY ) ;
2024-07-02 14:49:31 +02:00
}
2024-07-02 17:38:24 +02:00
return true ;
2024-07-02 14:49:31 +02:00
}
2024-07-02 18:07:54 +02:00
function buildRecordUploadEnabled ( ) : boolean {
if ( process . env . DOCKER_BUILD_RECORD_UPLOAD ) {
return Util . parseBool ( process . env . DOCKER_BUILD_RECORD_UPLOAD ) ;
}
return true ;
}
2024-07-02 18:09:36 +02:00
function buildRecordRetentionDays ( ) : number | undefined {
let val : string | undefined ;
2024-06-24 10:14:14 +02:00
if ( process . env . DOCKER_BUILD_EXPORT_RETENTION_DAYS ) {
2024-07-02 18:09:36 +02:00
core . warning ( 'DOCKER_BUILD_EXPORT_RETENTION_DAYS is deprecated. Use DOCKER_BUILD_RECORD_RETENTION_DAYS instead.' ) ;
val = process . env . DOCKER_BUILD_EXPORT_RETENTION_DAYS ;
} else if ( process . env . DOCKER_BUILD_RECORD_RETENTION_DAYS ) {
val = process . env . DOCKER_BUILD_RECORD_RETENTION_DAYS ;
}
if ( val ) {
const res = parseInt ( val ) ;
2024-06-24 10:14:14 +02:00
if ( isNaN ( res ) ) {
2025-04-01 11:36:44 +09:00
throw new Error ( ` Invalid build record retention days: ${ val } ` ) ;
2024-06-24 10:14:14 +02:00
}
return res ;
}
}