From 2ee3e78b7811ab01ac7b10731ded0620a01f8c04 Mon Sep 17 00:00:00 2001 From: Pavel Busko Date: Wed, 14 Aug 2024 10:24:32 +0000 Subject: [PATCH] feat: Cloud Native Buildpacks project descriptor manager --- lib/config/options/index.ts | 1 + lib/constants/category.ts | 1 + lib/modules/manager/api.ts | 2 + .../__fixtures__/project.toml | 14 ++++ .../__snapshots__/extract.spec.ts.snap | 30 +++++++ .../cnb-project-descriptor/extract.spec.ts | 21 +++++ .../manager/cnb-project-descriptor/extract.ts | 79 +++++++++++++++++++ .../manager/cnb-project-descriptor/index.ts | 12 +++ .../manager/cnb-project-descriptor/readme.md | 21 +++++ .../manager/cnb-project-descriptor/types.ts | 15 ++++ 10 files changed, 196 insertions(+) create mode 100644 lib/modules/manager/cnb-project-descriptor/__fixtures__/project.toml create mode 100644 lib/modules/manager/cnb-project-descriptor/__snapshots__/extract.spec.ts.snap create mode 100644 lib/modules/manager/cnb-project-descriptor/extract.spec.ts create mode 100644 lib/modules/manager/cnb-project-descriptor/extract.ts create mode 100644 lib/modules/manager/cnb-project-descriptor/index.ts create mode 100644 lib/modules/manager/cnb-project-descriptor/readme.md create mode 100644 lib/modules/manager/cnb-project-descriptor/types.ts diff --git a/lib/config/options/index.ts b/lib/config/options/index.ts index 2e1a8a4fe7cf83e..7eadd3f8282d675 100644 --- a/lib/config/options/index.ts +++ b/lib/config/options/index.ts @@ -1135,6 +1135,7 @@ const options: RenovateOptions[] = [ supportedManagers: [ 'ansible', 'bitbucket-pipelines', + 'cnb-project-descriptor', 'crossplane', 'devcontainer', 'docker-compose', diff --git a/lib/constants/category.ts b/lib/constants/category.ts index a92c54656eeb564..30b87e581d33aae 100644 --- a/lib/constants/category.ts +++ b/lib/constants/category.ts @@ -3,6 +3,7 @@ export const Categories = [ 'ansible', 'batect', 'bazel', + 'buildpacks', 'c', 'cd', 'ci', diff --git a/lib/modules/manager/api.ts b/lib/modules/manager/api.ts index db30f841bb4edc0..0ca479783e3c0b6 100644 --- a/lib/modules/manager/api.ts +++ b/lib/modules/manager/api.ts @@ -19,6 +19,7 @@ import * as cargo from './cargo'; import * as cdnurl from './cdnurl'; import * as circleci from './circleci'; import * as cloudbuild from './cloudbuild'; +import * as cnbProjectDesc from './cnb-project-descriptor'; import * as cocoapods from './cocoapods'; import * as composer from './composer'; import * as conan from './conan'; @@ -120,6 +121,7 @@ api.set('cargo', cargo); api.set('cdnurl', cdnurl); api.set('circleci', circleci); api.set('cloudbuild', cloudbuild); +api.set('cnb-project-descriptor', cnbProjectDesc); api.set('cocoapods', cocoapods); api.set('composer', composer); api.set('conan', conan); diff --git a/lib/modules/manager/cnb-project-descriptor/__fixtures__/project.toml b/lib/modules/manager/cnb-project-descriptor/__fixtures__/project.toml new file mode 100644 index 000000000000000..dab37b59d2d3757 --- /dev/null +++ b/lib/modules/manager/cnb-project-descriptor/__fixtures__/project.toml @@ -0,0 +1,14 @@ +[_] +schema-version = "0.2" + +[io.buildpacks] +builder = "registry.corp/builder/noble:1.1.1" + +[[io.buildpacks.group]] +uri = "docker://buildpacks/java:2.2.2" + +[[io.buildpacks.group]] +uri = "buildpacks/nodejs:3.3.3" + +[[io.buildpacks.group]] +uri = "file://local.oci" diff --git a/lib/modules/manager/cnb-project-descriptor/__snapshots__/extract.spec.ts.snap b/lib/modules/manager/cnb-project-descriptor/__snapshots__/extract.spec.ts.snap new file mode 100644 index 000000000000000..2d407a715fd9442 --- /dev/null +++ b/lib/modules/manager/cnb-project-descriptor/__snapshots__/extract.spec.ts.snap @@ -0,0 +1,30 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`modules/manager/cnb-project-descriptor/extract extractPackageFile() extracts builder and buildpack images 1`] = ` +[ + { + "autoReplaceStringTemplate": "{{depName}}{{#if newValue}}:{{newValue}}{{/if}}{{#if newDigest}}@{{newDigest}}{{/if}}", + "currentDigest": undefined, + "currentValue": "1.1.1", + "datasource": "docker", + "depName": "registry.corp/builder/noble", + "replaceString": "registry.corp/builder/noble:1.1.1", + }, + { + "autoReplaceStringTemplate": "{{depName}}{{#if newValue}}:{{newValue}}{{/if}}{{#if newDigest}}@{{newDigest}}{{/if}}", + "currentDigest": undefined, + "currentValue": "2.2.2", + "datasource": "docker", + "depName": "buildpacks/java", + "replaceString": "buildpacks/java:2.2.2", + }, + { + "autoReplaceStringTemplate": "{{depName}}{{#if newValue}}:{{newValue}}{{/if}}{{#if newDigest}}@{{newDigest}}{{/if}}", + "currentDigest": undefined, + "currentValue": "3.3.3", + "datasource": "docker", + "depName": "buildpacks/nodejs", + "replaceString": "buildpacks/nodejs:3.3.3", + }, +] +`; diff --git a/lib/modules/manager/cnb-project-descriptor/extract.spec.ts b/lib/modules/manager/cnb-project-descriptor/extract.spec.ts new file mode 100644 index 000000000000000..911157bab12854a --- /dev/null +++ b/lib/modules/manager/cnb-project-descriptor/extract.spec.ts @@ -0,0 +1,21 @@ +import { Fixtures } from '../../../../test/fixtures'; + +import { extractPackageFile } from '.'; + +describe('modules/manager/cnb-project-descriptor/extract', () => { + describe('extractPackageFile()', () => { + it('returns null for empty', () => { + expect(extractPackageFile('not a project toml', '', {})).toBeNull(); + }); + + it('extracts builder and buildpack images', () => { + const res = extractPackageFile( + Fixtures.get('project.toml'), + 'project.toml', + {}, + ); + expect(res?.deps).toMatchSnapshot(); + expect(res?.deps).toHaveLength(3); + }); + }); +}); diff --git a/lib/modules/manager/cnb-project-descriptor/extract.ts b/lib/modules/manager/cnb-project-descriptor/extract.ts new file mode 100644 index 000000000000000..3f360899e9626df --- /dev/null +++ b/lib/modules/manager/cnb-project-descriptor/extract.ts @@ -0,0 +1,79 @@ +import is from '@sindresorhus/is'; +import { logger } from '../../../logger'; +import { parse as parseToml } from '../../../util/toml'; +import { getDep } from '../dockerfile/extract'; +import type { + ExtractConfig, + PackageDependency, + PackageFileContent, +} from '../types'; +import type { ProjectDescriptor } from './types'; + +const dockerPrefix = /^docker:\/\//; + +export function extractPackageFile( + content: string, + packageFile: string, + config: ExtractConfig, +): PackageFileContent | null { + const deps: PackageDependency[] = []; + try { + const descriptor = parseToml(content) as ProjectDescriptor; + if ( + descriptor.io.buildpacks?.builder && + !descriptor.io.buildpacks.builder.startsWith('file://') + ) { + const dep = getDep( + descriptor.io.buildpacks.builder.replace(dockerPrefix, ''), + true, + config.registryAliases, + ); + logger.trace( + { + depName: dep.depName, + currentValue: dep.currentValue, + currentDigest: dep.currentDigest, + }, + 'Cloud Native Buildpacks builder', + ); + + deps.push(dep); + } + + if ( + descriptor.io.buildpacks?.group && + is.array(descriptor.io.buildpacks.group) + ) { + for (const group of descriptor.io.buildpacks.group) { + if (group.uri && !group.uri.startsWith('file://')) { + const dep = getDep( + group.uri.replace(dockerPrefix, ''), + true, + config.registryAliases, + ); + logger.trace( + { + depName: dep.depName, + currentValue: dep.currentValue, + currentDigest: dep.currentDigest, + }, + 'Cloud Native Buildpack', + ); + + deps.push(dep); + } + } + } + } catch (err) { + logger.debug( + { err, packageFile }, + 'Failed to parse buildpacks project descriptor TOML', + ); + return null; + } + + if (!deps.length) { + return null; + } + return { deps }; +} diff --git a/lib/modules/manager/cnb-project-descriptor/index.ts b/lib/modules/manager/cnb-project-descriptor/index.ts new file mode 100644 index 000000000000000..13840cdfdb4b79d --- /dev/null +++ b/lib/modules/manager/cnb-project-descriptor/index.ts @@ -0,0 +1,12 @@ +import type { Category } from '../../../constants'; +import { DockerDatasource } from '../../datasource/docker'; +export { extractPackageFile } from './extract'; + +export const defaultConfig = { + commitMessageTopic: 'buildpacks project descriptor {{depName}}', + fileMatch: ['(^|/)project\\.toml$'], + pinDigests: false, +}; + +export const categories: Category[] = ['docker', 'buildpacks']; +export const supportedDatasources = [DockerDatasource.id]; diff --git a/lib/modules/manager/cnb-project-descriptor/readme.md b/lib/modules/manager/cnb-project-descriptor/readme.md new file mode 100644 index 000000000000000..636f3641cad9a96 --- /dev/null +++ b/lib/modules/manager/cnb-project-descriptor/readme.md @@ -0,0 +1,21 @@ +The `cnb-project-descriptor` manager updates Cloud Native Buildpacks project descriptor file (`project.toml`), which references builder and/or buildpack images by URIs. +Updates are performed if the `project.toml` file is found and it conforms to the [project descriptor](https://github.com/buildpacks/spec/blob/main/extensions/project-descriptor.md) specification. + +__Note__: buildpacks in the `io.buildpacks.group` array must be configured with the docker reference (`uri`) for this manager to work. + +```toml +[_] +schema-version = "0.2" + +[io.buildpacks] +builder = "registry.corp/builder/noble:1.1.1" + +[[io.buildpacks.group]] +uri = "docker://buildpacks/java:2.2.2" + +[[io.buildpacks.group]] +uri = "buildpacks/nodejs:3.3.3" + +[[io.buildpacks.group]] +uri = "file://local.oci" # will be ignored +``` diff --git a/lib/modules/manager/cnb-project-descriptor/types.ts b/lib/modules/manager/cnb-project-descriptor/types.ts new file mode 100644 index 000000000000000..1c2c580b1799017 --- /dev/null +++ b/lib/modules/manager/cnb-project-descriptor/types.ts @@ -0,0 +1,15 @@ +export interface BuildpackGroup { + uri?: string; +} + +export interface IoBuildpacks { + builder?: string; + group?: BuildpackGroup[]; +} + +export interface ProjectDescriptor { + 'schema-version'?: string; + io: { + buildpacks?: IoBuildpacks; + }; +}