aboutsummaryrefslogtreecommitdiffhomepage
path: root/tools/utils/docker.ts
blob: 97df6c4590915410709ad7c6ce7c3fc8fe97bd84 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import fs from 'node:fs/promises';
import path from 'node:path';
import os from 'os';
import { setTimeout } from 'timers/promises';
import type { SemVer } from 'semver';
import { logger } from '../../lib/logger';
import { toMs } from '../../lib/util/pretty-time';
import { exec } from './exec';

const file = 'tools/docker/bake.hcl';
const tmp = fs.mkdtemp(path.join(os.tmpdir(), 'renovate-docker-bake-'));

export type MetaDataItem = {
  'containerimage.digest'?: string;
};
export type MetaData = {
  'push-slim'?: MetaDataItem;
  'push-full'?: MetaDataItem;
};

export async function bake(
  target: string,
  opts: {
    platform?: string;
    version?: SemVer;
    args?: string[];
    delay?: string;
    exitOnError?: boolean;
    tries?: number;
  },
): Promise<MetaData | null> {
  if (opts.version) {
    console.log(`Using version: ${opts.version.version}`);
    process.env.RENOVATE_VERSION = opts.version.version;
    process.env.RENOVATE_MAJOR_VERSION = `${opts.version.major}`;
    process.env.RENOVATE_MAJOR_MINOR_VERSION = `${opts.version.major}.${opts.version.minor}`;
  }

  const metadataFile = path.join(await tmp, 'metadata.json');
  const args = [
    'buildx',
    'bake',
    '--file',
    file,
    '--metadata-file',
    metadataFile,
  ];

  if (opts.platform) {
    console.log(`Using platform: ${opts.platform}`);
    args.push('--set', `settings.platform=${opts.platform}`);
  }

  if (Array.isArray(opts.args)) {
    console.log(`Using args: ${opts.args.join(' ')}`);
    args.push(...opts.args);
  }

  args.push(target);

  for (let tries = opts.tries ?? 0; tries >= 0; tries--) {
    const result = exec(`docker`, args);
    if (result.signal) {
      logger.error(`Signal received: ${result.signal}`);
      process.exit(-1);
    } else if (result.status && result.status !== 0) {
      if (tries > 0) {
        logger.debug(`Error occured:\n ${result.stderr}`);
        const delay = opts.delay ? toMs(opts.delay) : null;
        if (delay) {
          logger.info(`Retrying in ${opts.delay} ...`);
          await setTimeout(delay);
        }
      } else {
        logger.error(`Error occured:\n${result.stderr}`);
        if (opts.exitOnError !== false) {
          process.exit(result.status);
        }
        return null;
      }
    } else {
      logger.debug(`${target} succeeded:\n${result.stdout || result.stderr}`);
      break;
    }
  }

  const meta = JSON.parse(await fs.readFile(metadataFile, 'utf8'));
  logger.debug({ meta }, 'metadata');

  return meta;
}

export function sign(
  image: string,
  opts: {
    args?: string[];
    exitOnError?: boolean;
  },
): void {
  logger.info(`Signing ${image} ...`);
  const result = exec('cosign', ['sign', '--yes', image]);
  if (result.signal) {
    logger.error(`Signal received: ${result.signal}`);
    process.exit(-1);
  } else if (result.status && result.status !== 0) {
    logger.error(`Error occured:\n${result.stderr}`);
    if (opts.exitOnError !== false) {
      process.exit(result.status);
    }
  } else {
    logger.debug(`Succeeded:\n${result.stdout || result.stderr}`);
  }
}