diff --git a/lib/manager/batect/__fixtures__/batect.yml b/lib/manager/batect/__fixtures__/batect.yml new file mode 100644 index 0000000000000000000000000000000000000000..d8fb1ac8178c1d36571cc12d2d088ed8f8f469ea --- /dev/null +++ b/lib/manager/batect/__fixtures__/batect.yml @@ -0,0 +1,15 @@ +containers: + container-1: + image: alpine:1.2.3 + + container-2: + image: alpine:1.2.3 + + container-3: + image: ubuntu:20.04 + + container-4: + build_directory: some_build_directory + + container-5: + image: postgres:9.6.20@sha256:166179811e4c75f8a092367afed6091208c8ecf60b111c7e49f29af45ca05e08 diff --git a/lib/manager/batect/extract.spec.ts b/lib/manager/batect/extract.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..abd36560a1b7f72d7e704903aed07f4de50dab2a --- /dev/null +++ b/lib/manager/batect/extract.spec.ts @@ -0,0 +1,41 @@ +import { readFileSync } from 'fs'; +import { getDep } from '../dockerfile/extract'; +import { extractPackageFile } from './extract'; + +const sampleFile = readFileSync( + 'lib/manager/batect/__fixtures__/batect.yml', + 'utf8' +); + +describe('lib/manager/batect/extract', () => { + describe('extractPackageFile()', () => { + it('returns null for empty configuration file', () => { + expect(extractPackageFile('')).toBeNull(); + }); + + it('returns null for non-object configuration file', () => { + expect(extractPackageFile('nothing here')).toBeNull(); + }); + + it('returns null for malformed configuration file', () => { + expect(extractPackageFile('nothing here\n:::::::')).toBeNull(); + }); + + it('returns null for configuration file without containers', () => { + expect(extractPackageFile('something_else: some_value')).toBeNull(); + }); + + it('extracts all available images from a valid Batect configuration file', () => { + const res = extractPackageFile(sampleFile); + + expect(res.deps).toEqual([ + getDep('alpine:1.2.3'), + getDep('alpine:1.2.3'), + getDep('ubuntu:20.04'), + getDep( + 'postgres:9.6.20@sha256:166179811e4c75f8a092367afed6091208c8ecf60b111c7e49f29af45ca05e08' + ), + ]); + }); + }); +}); diff --git a/lib/manager/batect/extract.ts b/lib/manager/batect/extract.ts new file mode 100644 index 0000000000000000000000000000000000000000..3846a9008bdb011420f70ebb0e732c11c1cfec06 --- /dev/null +++ b/lib/manager/batect/extract.ts @@ -0,0 +1,59 @@ +import { safeLoad } from 'js-yaml'; + +import { logger } from '../../logger'; +import { PackageFile } from '../common'; +import { getDep } from '../dockerfile/extract'; +import { BatectConfig } from './types'; + +function loadConfig(content: string): BatectConfig { + const config = safeLoad(content); + + if (typeof config !== 'object') { + throw new Error( + `Configuration file does not contain a YAML object (it is ${typeof config}).` + ); + } + + return config as BatectConfig; +} + +function extractImages(config: BatectConfig): string[] { + if (config.containers === undefined) { + return []; + } + + return Object.values(config.containers) + .filter((container) => container.image !== undefined) + .map((container) => container.image); +} + +export function extractPackageFile( + content: string, + fileName?: string +): PackageFile | null { + logger.debug('batect.extractPackageFile()'); + + try { + const config = loadConfig(content); + const images = extractImages(config); + const deps = images.map((image) => getDep(image)); + + if (deps.length === 0) { + return null; + } + + logger.trace( + { deps, fileName }, + 'Loaded images from Batect configuration file' + ); + + return { deps }; + } catch (err) { + logger.warn( + { err, fileName }, + 'Extracting dependencies from Batect configuration file failed' + ); + + return null; + } +} diff --git a/lib/manager/batect/index.ts b/lib/manager/batect/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..78ace05e2846c453192b3fdc8824a2216b0a7ff7 --- /dev/null +++ b/lib/manager/batect/index.ts @@ -0,0 +1,7 @@ +import { extractPackageFile } from './extract'; + +export { extractPackageFile }; + +export const defaultConfig = { + fileMatch: ['(^|/)batect(-bundle)?\\.yml$'], +}; diff --git a/lib/manager/batect/readme.md b/lib/manager/batect/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..980e2bd926bfd2685f2e85ffaa97286857051129 --- /dev/null +++ b/lib/manager/batect/readme.md @@ -0,0 +1,23 @@ +Extracts all Docker images from Batect configuration files. + +By default, the manager searches for files called `batect.yml` or `batect-bundle.yml`. + +If you keep your Batect configuration in other files, you'll need to tell Renovate where to find them. +This includes files included into your main configuration file with `include`. + +You do this by creating a `"batect"` object in your `renovate.json` file. +This object should contain a `fileMatch` array with regular expressions that match the configuration file names. + +For example: + +```json +{ + "batect": { + "fileMatch": [ + "(^|/)batect(-bundle)?\\.yml$", + "(^|/)my-other-batect-file\\.yml$", + "^a-directory/[^/]*\\.yml$" + ] + } +} +``` diff --git a/lib/manager/batect/types.ts b/lib/manager/batect/types.ts new file mode 100644 index 0000000000000000000000000000000000000000..b62adbc14fa56ebade41b3a6daa7b636d0677f7a --- /dev/null +++ b/lib/manager/batect/types.ts @@ -0,0 +1,7 @@ +export interface BatectConfig { + containers?: Record<string, BatectContainer>; +} + +export interface BatectContainer { + image?: string; +}