233 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			233 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
import * as fs from 'fs';
 | 
						|
import * as path from 'path';
 | 
						|
import * as stateHelper from './state-helper';
 | 
						|
import * as core from '@actions/core';
 | 
						|
import * as actionsToolkit from '@docker/actions-toolkit';
 | 
						|
 | 
						|
import {Buildx} from '@docker/actions-toolkit/lib/buildx/buildx';
 | 
						|
import {History as BuildxHistory} from '@docker/actions-toolkit/lib/buildx/history';
 | 
						|
import {Context} from '@docker/actions-toolkit/lib/context';
 | 
						|
import {Docker} from '@docker/actions-toolkit/lib/docker/docker';
 | 
						|
import {Exec} from '@docker/actions-toolkit/lib/exec';
 | 
						|
import {GitHub} from '@docker/actions-toolkit/lib/github';
 | 
						|
import {Toolkit} from '@docker/actions-toolkit/lib/toolkit';
 | 
						|
import {Util} from '@docker/actions-toolkit/lib/util';
 | 
						|
 | 
						|
import {BuilderInfo} from '@docker/actions-toolkit/lib/types/buildx/builder';
 | 
						|
import {ConfigFile} from '@docker/actions-toolkit/lib/types/docker/docker';
 | 
						|
 | 
						|
import * as context from './context';
 | 
						|
 | 
						|
actionsToolkit.run(
 | 
						|
  // main
 | 
						|
  async () => {
 | 
						|
    const startedTime = new Date();
 | 
						|
    const inputs: context.Inputs = await context.getInputs();
 | 
						|
    core.debug(`inputs: ${JSON.stringify(inputs)}`);
 | 
						|
    stateHelper.setInputs(inputs);
 | 
						|
 | 
						|
    const toolkit = new Toolkit();
 | 
						|
 | 
						|
    await core.group(`GitHub Actions runtime token ACs`, async () => {
 | 
						|
      try {
 | 
						|
        await GitHub.printActionsRuntimeTokenACs();
 | 
						|
      } catch (e) {
 | 
						|
        core.warning(e.message);
 | 
						|
      }
 | 
						|
    });
 | 
						|
 | 
						|
    await core.group(`Docker info`, async () => {
 | 
						|
      try {
 | 
						|
        await Docker.printVersion();
 | 
						|
        await Docker.printInfo();
 | 
						|
      } catch (e) {
 | 
						|
        core.info(e.message);
 | 
						|
      }
 | 
						|
    });
 | 
						|
 | 
						|
    await core.group(`Proxy configuration`, async () => {
 | 
						|
      let dockerConfig: ConfigFile | undefined;
 | 
						|
      let dockerConfigMalformed = false;
 | 
						|
      try {
 | 
						|
        dockerConfig = await Docker.configFile();
 | 
						|
      } catch (e) {
 | 
						|
        dockerConfigMalformed = true;
 | 
						|
        core.warning(`Unable to parse config file ${path.join(Docker.configDir, 'config.json')}: ${e}`);
 | 
						|
      }
 | 
						|
      if (dockerConfig && dockerConfig.proxies) {
 | 
						|
        for (const host in dockerConfig.proxies) {
 | 
						|
          let prefix = '';
 | 
						|
          if (Object.keys(dockerConfig.proxies).length > 1) {
 | 
						|
            prefix = '  ';
 | 
						|
            core.info(host);
 | 
						|
          }
 | 
						|
          for (const key in dockerConfig.proxies[host]) {
 | 
						|
            core.info(`${prefix}${key}: ${dockerConfig.proxies[host][key]}`);
 | 
						|
          }
 | 
						|
        }
 | 
						|
      } else if (!dockerConfigMalformed) {
 | 
						|
        core.info('No proxy configuration found');
 | 
						|
      }
 | 
						|
    });
 | 
						|
 | 
						|
    if (!(await toolkit.buildx.isAvailable())) {
 | 
						|
      core.setFailed(`Docker buildx is required. See https://github.com/docker/setup-buildx-action to set up buildx.`);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    stateHelper.setTmpDir(Context.tmpDir());
 | 
						|
 | 
						|
    await core.group(`Buildx version`, async () => {
 | 
						|
      await toolkit.buildx.printVersion();
 | 
						|
    });
 | 
						|
 | 
						|
    let builder: BuilderInfo;
 | 
						|
    await core.group(`Builder info`, async () => {
 | 
						|
      builder = await toolkit.builder.inspect(inputs.builder);
 | 
						|
      core.info(JSON.stringify(builder, null, 2));
 | 
						|
    });
 | 
						|
 | 
						|
    const args: string[] = await context.getArgs(inputs, toolkit);
 | 
						|
    core.debug(`context.getArgs: ${JSON.stringify(args)}`);
 | 
						|
 | 
						|
    const buildCmd = await toolkit.buildx.getCommand(args);
 | 
						|
    core.debug(`buildCmd.command: ${buildCmd.command}`);
 | 
						|
    core.debug(`buildCmd.args: ${JSON.stringify(buildCmd.args)}`);
 | 
						|
 | 
						|
    let err: Error | undefined;
 | 
						|
    await Exec.getExecOutput(buildCmd.command, buildCmd.args, {
 | 
						|
      ignoreReturnCode: true
 | 
						|
    }).then(res => {
 | 
						|
      if (res.stderr.length > 0 && res.exitCode != 0) {
 | 
						|
        err = Error(`buildx failed with: ${res.stderr.match(/(.*)\s*$/)?.[0]?.trim() ?? 'unknown error'}`);
 | 
						|
      }
 | 
						|
    });
 | 
						|
 | 
						|
    const imageID = toolkit.buildxBuild.resolveImageID();
 | 
						|
    const metadata = toolkit.buildxBuild.resolveMetadata();
 | 
						|
    const digest = toolkit.buildxBuild.resolveDigest();
 | 
						|
    if (imageID) {
 | 
						|
      await core.group(`ImageID`, async () => {
 | 
						|
        core.info(imageID);
 | 
						|
        core.setOutput('imageid', imageID);
 | 
						|
      });
 | 
						|
    }
 | 
						|
    if (digest) {
 | 
						|
      await core.group(`Digest`, async () => {
 | 
						|
        core.info(digest);
 | 
						|
        core.setOutput('digest', digest);
 | 
						|
      });
 | 
						|
    }
 | 
						|
    if (metadata) {
 | 
						|
      await core.group(`Metadata`, async () => {
 | 
						|
        const metadatadt = JSON.stringify(metadata, null, 2);
 | 
						|
        core.info(metadatadt);
 | 
						|
        core.setOutput('metadata', metadatadt);
 | 
						|
      });
 | 
						|
    }
 | 
						|
 | 
						|
    let ref: string;
 | 
						|
    await core.group(`Reference`, async () => {
 | 
						|
      ref = await buildRef(toolkit, startedTime, inputs.builder);
 | 
						|
      if (ref) {
 | 
						|
        core.info(ref);
 | 
						|
        stateHelper.setBuildRef(ref);
 | 
						|
      } else {
 | 
						|
        core.info('No build reference found');
 | 
						|
      }
 | 
						|
    });
 | 
						|
 | 
						|
    await core.group(`Check build summary support`, async () => {
 | 
						|
      if (buildSummaryDisabled()) {
 | 
						|
        core.info('Build summary disabled');
 | 
						|
      } else if (GitHub.isGHES) {
 | 
						|
        core.warning('Build summary is not yet supported on GHES');
 | 
						|
      } else if (!(await toolkit.buildx.versionSatisfies('>=0.13.0'))) {
 | 
						|
        core.warning('Build summary requires Buildx >= 0.13.0');
 | 
						|
      } else if (builder && builder.driver === 'cloud') {
 | 
						|
        core.warning('Build summary is not yet supported with Docker Build Cloud');
 | 
						|
      } else if (!ref) {
 | 
						|
        core.warning('Build summary requires a build reference');
 | 
						|
      } else {
 | 
						|
        core.info('Build summary supported!');
 | 
						|
        stateHelper.setSummarySupported();
 | 
						|
      }
 | 
						|
    });
 | 
						|
 | 
						|
    if (err) {
 | 
						|
      throw err;
 | 
						|
    }
 | 
						|
  },
 | 
						|
  // post
 | 
						|
  async () => {
 | 
						|
    if (stateHelper.isSummarySupported) {
 | 
						|
      await core.group(`Generating build summary`, async () => {
 | 
						|
        try {
 | 
						|
          const exportRetentionDays = buildExportRetentionDays();
 | 
						|
          const buildxHistory = new BuildxHistory();
 | 
						|
          const exportRes = await buildxHistory.export({
 | 
						|
            refs: stateHelper.buildRef ? [stateHelper.buildRef] : []
 | 
						|
          });
 | 
						|
          core.info(`Build record exported to ${exportRes.dockerbuildFilename} (${Util.formatFileSize(exportRes.dockerbuildSize)})`);
 | 
						|
          const uploadRes = await GitHub.uploadArtifact({
 | 
						|
            filename: exportRes.dockerbuildFilename,
 | 
						|
            mimeType: 'application/gzip',
 | 
						|
            retentionDays: exportRetentionDays
 | 
						|
          });
 | 
						|
          await GitHub.writeBuildSummary({
 | 
						|
            exportRes: exportRes,
 | 
						|
            uploadRes: uploadRes,
 | 
						|
            inputs: stateHelper.inputs
 | 
						|
          });
 | 
						|
        } catch (e) {
 | 
						|
          core.warning(e.message);
 | 
						|
        }
 | 
						|
      });
 | 
						|
    }
 | 
						|
    if (stateHelper.tmpDir.length > 0) {
 | 
						|
      await core.group(`Removing temp folder ${stateHelper.tmpDir}`, async () => {
 | 
						|
        fs.rmSync(stateHelper.tmpDir, {recursive: true});
 | 
						|
      });
 | 
						|
    }
 | 
						|
  }
 | 
						|
);
 | 
						|
 | 
						|
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] : '';
 | 
						|
}
 | 
						|
 | 
						|
function buildSummaryDisabled(): boolean {
 | 
						|
  if (process.env.DOCKER_BUILD_NO_SUMMARY) {
 | 
						|
    core.warning('DOCKER_BUILD_NO_SUMMARY is deprecated. Use DOCKER_BUILD_SUMMARY_DISABLE instead.');
 | 
						|
    return Util.parseBool(process.env.DOCKER_BUILD_NO_SUMMARY);
 | 
						|
  } else if (process.env.DOCKER_BUILD_SUMMARY_DISABLE) {
 | 
						|
    return Util.parseBool(process.env.DOCKER_BUILD_SUMMARY_DISABLE);
 | 
						|
  }
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
function buildExportRetentionDays(): number | undefined {
 | 
						|
  if (process.env.DOCKER_BUILD_EXPORT_RETENTION_DAYS) {
 | 
						|
    const res = parseInt(process.env.DOCKER_BUILD_EXPORT_RETENTION_DAYS);
 | 
						|
    if (isNaN(res)) {
 | 
						|
      throw Error(`Invalid build export retention days: ${process.env.DOCKER_BUILD_EXPORT_RETENTION_DAYS}`);
 | 
						|
    }
 | 
						|
    return res;
 | 
						|
  }
 | 
						|
}
 |