From 22d356b9b175f3af7a8fe0b9114ec06c8d6da2bb Mon Sep 17 00:00:00 2001 From: Tobias Schlatter Date: Thu, 5 Dec 2024 17:26:35 +0100 Subject: [PATCH] feat(bazel-module): add support for oci.pull (#32453) Co-authored-by: Rhys Arkins --- .../manager/bazel-module/extract.spec.ts | 55 +++++++++++++++++++ lib/modules/manager/bazel-module/extract.ts | 6 ++ lib/modules/manager/bazel-module/index.ts | 2 + .../manager/bazel-module/parser/index.spec.ts | 29 ++++++++++ .../manager/bazel-module/parser/index.ts | 3 +- .../manager/bazel-module/parser/oci.ts | 41 ++++++++++++++ lib/modules/manager/bazel-module/readme.md | 20 +++++++ 7 files changed, 155 insertions(+), 1 deletion(-) create mode 100644 lib/modules/manager/bazel-module/parser/oci.ts diff --git a/lib/modules/manager/bazel-module/extract.spec.ts b/lib/modules/manager/bazel-module/extract.spec.ts index 5e82c5682c35ab..336d6172a3ba13 100644 --- a/lib/modules/manager/bazel-module/extract.spec.ts +++ b/lib/modules/manager/bazel-module/extract.spec.ts @@ -4,6 +4,7 @@ import { Fixtures } from '../../../../test/fixtures'; import { GlobalConfig } from '../../../config/global'; import type { RepoGlobalConfig } from '../../../config/types'; import { BazelDatasource } from '../../datasource/bazel'; +import { DockerDatasource } from '../../datasource/docker'; import { GithubTagsDatasource } from '../../datasource/github-tags'; import { MavenDatasource } from '../../datasource/maven'; import * as parser from './parser'; @@ -290,6 +291,60 @@ describe('modules/manager/bazel-module/extract', () => { ]); }); + it('returns oci.pull dependencies', async () => { + const input = codeBlock` + oci.pull( + name = "nginx_image", + digest = "sha256:287ff321f9e3cde74b600cc26197424404157a72043226cbbf07ee8304a2c720", + image = "index.docker.io/library/nginx", + platforms = ["linux/amd64"], + tag = "1.27.1", + ) + `; + + const result = await extractPackageFile(input, 'MODULE.bazel'); + if (!result) { + throw new Error('Expected a result.'); + } + expect(result.deps).toEqual([ + { + datasource: DockerDatasource.id, + depType: 'oci_pull', + depName: 'nginx_image', + packageName: 'index.docker.io/library/nginx', + currentValue: '1.27.1', + currentDigest: + 'sha256:287ff321f9e3cde74b600cc26197424404157a72043226cbbf07ee8304a2c720', + }, + ]); + }); + + it('returns oci.pull dependencies without tags', async () => { + const input = codeBlock` + oci.pull( + name = "nginx_image", + digest = "sha256:287ff321f9e3cde74b600cc26197424404157a72043226cbbf07ee8304a2c720", + image = "index.docker.io/library/nginx", + platforms = ["linux/amd64"], + ) + `; + + const result = await extractPackageFile(input, 'MODULE.bazel'); + if (!result) { + throw new Error('Expected a result.'); + } + expect(result.deps).toEqual([ + { + datasource: DockerDatasource.id, + depType: 'oci_pull', + depName: 'nginx_image', + packageName: 'index.docker.io/library/nginx', + currentDigest: + 'sha256:287ff321f9e3cde74b600cc26197424404157a72043226cbbf07ee8304a2c720', + }, + ]); + }); + it('returns maven.install and bazel_dep dependencies together', async () => { const input = codeBlock` bazel_dep(name = "bazel_jar_jar", version = "0.1.0") diff --git a/lib/modules/manager/bazel-module/extract.ts b/lib/modules/manager/bazel-module/extract.ts index 399d471ca2b3fb..b62bcbccdd983e 100644 --- a/lib/modules/manager/bazel-module/extract.ts +++ b/lib/modules/manager/bazel-module/extract.ts @@ -7,6 +7,7 @@ import * as bazelrc from './bazelrc'; import type { RecordFragment } from './fragments'; import { parse } from './parser'; import { RuleToMavenPackageDep, fillRegistryUrls } from './parser/maven'; +import { RuleToDockerPackageDep } from './parser/oci'; import { RuleToBazelModulePackageDep } from './rules'; import * as rules from './rules'; @@ -18,11 +19,16 @@ export async function extractPackageFile( const records = parse(content); const pfc = await extractBazelPfc(records, packageFile); const mavenDeps = extractMavenDeps(records); + const dockerDeps = LooseArray(RuleToDockerPackageDep).parse(records); if (mavenDeps.length) { pfc.deps.push(...mavenDeps); } + if (dockerDeps.length) { + pfc.deps.push(...dockerDeps); + } + return pfc.deps.length ? pfc : null; } catch (err) { logger.debug({ err, packageFile }, 'Failed to parse bazel module file.'); diff --git a/lib/modules/manager/bazel-module/index.ts b/lib/modules/manager/bazel-module/index.ts index 4ad477b36f0690..fe9461b7f8cceb 100644 --- a/lib/modules/manager/bazel-module/index.ts +++ b/lib/modules/manager/bazel-module/index.ts @@ -1,5 +1,6 @@ import type { Category } from '../../../constants'; import { BazelDatasource } from '../../datasource/bazel'; +import { DockerDatasource } from '../../datasource/docker'; import { GithubTagsDatasource } from '../../datasource/github-tags'; import { MavenDatasource } from '../../datasource/maven'; import { extractPackageFile } from './extract'; @@ -15,6 +16,7 @@ export const defaultConfig = { export const supportedDatasources = [ BazelDatasource.id, + DockerDatasource.id, GithubTagsDatasource.id, MavenDatasource.id, ]; diff --git a/lib/modules/manager/bazel-module/parser/index.spec.ts b/lib/modules/manager/bazel-module/parser/index.spec.ts index aa2311d2c9ee93..26ce0ae5c45e37 100644 --- a/lib/modules/manager/bazel-module/parser/index.spec.ts +++ b/lib/modules/manager/bazel-module/parser/index.spec.ts @@ -286,5 +286,34 @@ describe('modules/manager/bazel-module/parser/index', () => { ), ]); }); + + it('finds oci.pull', () => { + const input = codeBlock` + oci.pull( + name = "nginx_image", + digest = "sha256:287ff321f9e3cde74b600cc26197424404157a72043226cbbf07ee8304a2c720", + image = "index.docker.io/library/nginx", + platforms = ["linux/amd64"], + tag = "1.27.1", + ) + `; + + const res = parse(input); + expect(res).toEqual([ + fragments.record( + { + rule: fragments.string('oci_pull'), + name: fragments.string('nginx_image'), + digest: fragments.string( + 'sha256:287ff321f9e3cde74b600cc26197424404157a72043226cbbf07ee8304a2c720', + ), + image: fragments.string('index.docker.io/library/nginx'), + platforms: fragments.array([fragments.string('linux/amd64')], true), + tag: fragments.string('1.27.1'), + }, + true, + ), + ]); + }); }); }); diff --git a/lib/modules/manager/bazel-module/parser/index.ts b/lib/modules/manager/bazel-module/parser/index.ts index 0757a63687557b..b53bce22412edb 100644 --- a/lib/modules/manager/bazel-module/parser/index.ts +++ b/lib/modules/manager/bazel-module/parser/index.ts @@ -3,8 +3,9 @@ import { Ctx } from '../context'; import type { RecordFragment } from '../fragments'; import { mavenRules } from './maven'; import { moduleRules } from './module'; +import { ociRules } from './oci'; -const rule = q.alt(moduleRules, mavenRules); +const rule = q.alt(moduleRules, mavenRules, ociRules); const query = q.tree({ type: 'root-tree', diff --git a/lib/modules/manager/bazel-module/parser/oci.ts b/lib/modules/manager/bazel-module/parser/oci.ts new file mode 100644 index 00000000000000..60b4e556a2e131 --- /dev/null +++ b/lib/modules/manager/bazel-module/parser/oci.ts @@ -0,0 +1,41 @@ +import { query as q } from 'good-enough-parser'; +import { z } from 'zod'; +import { DockerDatasource } from '../../../datasource/docker'; +import type { PackageDependency } from '../../types'; +import type { Ctx } from '../context'; +import { RecordFragmentSchema, StringFragmentSchema } from '../fragments'; +import { kvParams } from './common'; + +export const RuleToDockerPackageDep = RecordFragmentSchema.extend({ + children: z.object({ + rule: StringFragmentSchema.extend({ + value: z.literal('oci_pull'), + }), + name: StringFragmentSchema, + image: StringFragmentSchema, + tag: StringFragmentSchema.optional(), + digest: StringFragmentSchema.optional(), + }), +}).transform( + ({ children: { rule, name, image, tag, digest } }): PackageDependency => ({ + datasource: DockerDatasource.id, + depType: rule.value, + depName: name.value, + packageName: image.value, + currentValue: tag?.value, + currentDigest: digest?.value, + }), +); + +export const ociRules = q + .sym('oci') + .op('.') + .sym('pull', (ctx, token) => ctx.startRule('oci_pull')) + .join( + q.tree({ + type: 'wrapped-tree', + maxDepth: 1, + search: kvParams, + postHandler: (ctx) => ctx.endRule(), + }), + ); diff --git a/lib/modules/manager/bazel-module/readme.md b/lib/modules/manager/bazel-module/readme.md index b5b439ba9f0c36..59a56bd8678441 100644 --- a/lib/modules/manager/bazel-module/readme.md +++ b/lib/modules/manager/bazel-module/readme.md @@ -1,5 +1,7 @@ The `bazel-module` manager can update [Bazel module (bzlmod)](https://bazel.build/external/module) enabled workspaces. +### Maven + It also takes care about maven artifacts initalized with [bzlmod](https://github.com/bazelbuild/rules_jvm_external/blob/master/docs/bzlmod.md). For simplicity the name of extension variable is limited to `maven*`. E.g.: ``` @@ -26,3 +28,21 @@ maven.artifact( version = "1.11.1", ) ``` + +### Docker + +Similarly, it updates Docker / OCI images pulled with [oci_pull](https://github.com/bazel-contrib/rules_oci/blob/main/docs/pull.md). + +Note that the extension must be called `oci`: + +``` +oci = use_extension("@rules_oci//oci:extensions.bzl", "oci") + +oci.pull( + name = "nginx_image", + digest = "sha256:287ff321f9e3cde74b600cc26197424404157a72043226cbbf07ee8304a2c720", + image = "index.docker.io/library/nginx", + platforms = ["linux/amd64"], + tag = "1.27.1", +) +```