diff --git a/docs/usage/bazel.md b/docs/usage/bazel.md index 8eec08f4e1de588bfd49b3db28b14e4aafede5d6..9aa7c6d9f0b59b4e15aab24c6f36b743c09b8c0b 100644 --- a/docs/usage/bazel.md +++ b/docs/usage/bazel.md @@ -154,14 +154,18 @@ Renovate ignores [`multiple_version_override`](https://bazel.build/rules/lib/glo ## Legacy `WORKSPACE` files -Renovate extracts dependencies from: +Renovate extracts dependencies from the following repository rules: - `container_pull` - `oci_pull` - `git_repository` - `go_repository` - `maven_install` -- `http_archive` or `http_file` declarations +- `http_archive` +- `http_file` + +It also recognizes when these repository rule names are prefixed with an +underscore. For example, `_http_archive` is treated the same as `http_archive`. ### `git_repository` diff --git a/lib/modules/manager/bazel/__fixtures__/WORKSPACE2 b/lib/modules/manager/bazel/__fixtures__/WORKSPACE2 index 0d82bf137895532af90ed6bbe0d8d668d9756dea..40417b4837d04e9b714598b960c4481f0b4fc847 100644 --- a/lib/modules/manager/bazel/__fixtures__/WORKSPACE2 +++ b/lib/modules/manager/bazel/__fixtures__/WORKSPACE2 @@ -1,14 +1,14 @@ -load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") +load("@bazel_tools//tools/build_defs/repo:http.bzl", _http_archive = "http_archive") load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") -http_archive( +_http_archive( name = "GBDeviceInfo", url = "https://github.com/lmirosevic/GBDeviceInfo/archive/6.3.0.tar.gz", sha256 = "d7666275dff039407ea467c3083b83e24934101777c8b55b6b1b3b7e9a9e220b", strip_prefix = "GBDeviceInfo-6.3.0/GBDeviceInfo" ) -http_archive( +_http_archive( name = "com_github_nelhage_rules_boost", url = "https://github.com/nelhage/rules_boost/archive/135d46b4c9423ee7d494c78a21ff621bc73c12f3.tar.gz", sha256 = "3651f5dda0f7296e4cecafacc7f9d1f274be0fd64e30bebd74e28ffba28fe77f", @@ -17,14 +17,14 @@ http_archive( load("@com_github_nelhage_rules_boost//:boost/boost.bzl", "boost_deps") boost_deps() -http_archive( +_http_archive( name = "GBDeviceInfo-zip", url = "https://github.com/lmirosevic/GBDeviceInfo/archive/6.3.0.zip", sha256 = "4ef4320c4880fd64cfb7f42132f4b02fa626bccf1ba3e1a71dfbfcb50735f141", strip_prefix = "GBDeviceInfo-6.3.0/GBDeviceInfo" ) -http_archive( +_http_archive( name = "com_github_nelhage_rules_boost-zip", url = "https://github.com/nelhage/rules_boost/archive/135d46b4c9423ee7d494c78a21ff621bc73c12f3.zip", sha256 = "de8aac034cabe4a9ba5f7a33b9523862bf76c245a6c554c0e737f591bb7c7aeb", @@ -32,7 +32,7 @@ http_archive( ) maybe( - http_archive, + _http_archive, name = "io_bazel_rules_go", sha256 = "2b1641428dff9018f9e85c0384f03ec6c10660d935b750e3fa1492a281a53b0f", url = "https://github.com/bazelbuild/rules_go/releases/download/v0.29.0/rules_go-v0.29.0.zip", diff --git a/lib/modules/manager/bazel/artifacts.spec.ts b/lib/modules/manager/bazel/artifacts.spec.ts index 71ab62644fd4d4ad3b8e766ef9e1f3ce75619449..e70c4f0499b68fcfd3ced96c2ecfa6998547f2de 100644 --- a/lib/modules/manager/bazel/artifacts.spec.ts +++ b/lib/modules/manager/bazel/artifacts.spec.ts @@ -276,6 +276,61 @@ describe('modules/manager/bazel/artifacts', () => { expect(res).toBeNull(); }); + it('errors for _http_archive without urls', async () => { + const input = codeBlock` + _http_archive( + name = "bazel_skylib", + sha256 = "b5f6abe419da897b7901f90cbab08af958b97a8f3575b0d3dd062ac7ce78541f", + strip_prefix = "bazel-skylib-0.5.0", + ) + `; + + const upgrade = { + depName: 'bazel_skylib', + depType: 'http_archive', + repo: 'bazelbuild/bazel-skylib', + managerData: { idx: 0 }, + currentValue: '0.5.0', + newValue: '0.6.2', + }; + const res = await updateArtifacts( + partial<UpdateArtifact>({ + packageFileName: 'WORKSPACE', + updatedDeps: [upgrade], + newPackageFileContent: input, + }) + ); + expect(res).toBeNull(); + }); + + it('errors for maybe(_http_archive) without urls', async () => { + const input = codeBlock` + maybe( + _http_archive, + name = "bazel_skylib", + sha256 = "b5f6abe419da897b7901f90cbab08af958b97a8f3575b0d3dd062ac7ce78541f", + strip_prefix = "bazel-skylib-0.5.0", + ) + `; + + const upgrade = { + depName: 'bazel_skylib', + depType: 'http_archive', + repo: 'bazelbuild/bazel-skylib', + managerData: { idx: 0 }, + currentValue: '0.5.0', + newValue: '0.6.2', + }; + const res = await updateArtifacts( + partial<UpdateArtifact>({ + packageFileName: 'WORKSPACE', + updatedDeps: [upgrade], + newPackageFileContent: input, + }) + ); + expect(res).toBeNull(); + }); + it('updates http_archive with urls array', async () => { const inputHash = 'b5f6abe419da897b7901f90cbab08af958b97a8f3575b0d3dd062ac7ce78541f'; @@ -410,6 +465,140 @@ describe('modules/manager/bazel/artifacts', () => { ]); }); + it('updates _http_archive with urls array', async () => { + const inputHash = + 'b5f6abe419da897b7901f90cbab08af958b97a8f3575b0d3dd062ac7ce78541f'; + const input = codeBlock` + _http_archive( + name = "bazel_skylib", + sha256 = "${inputHash}", + strip_prefix = "bazel-skylib-0.5.0", + urls = [ + "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/archive/0.5.0.tar.gz", + "https://github.com/bazelbuild/bazel-skylib/archive/0.5.0.tar.gz", + ], + ) + `; + + const currentValue = '0.5.0'; + const newValue = '0.6.2'; + const upgrade = { + depName: 'bazel_skylib', + depType: 'http_archive', + repo: 'bazelbuild/bazel-skylib', + managerData: { idx: 0 }, + currentValue, + newValue, + }; + + const tarContent = Buffer.from('foo'); + const outputHash = crypto + .createHash('sha256') + .update(tarContent) + .digest('hex'); + + const output = input + .replace(currentValue, newValue) + .replace(currentValue, newValue) + .replace(currentValue, newValue) + .replace(inputHash, outputHash); + + httpMock + .scope('https://github.com') + .get('/bazelbuild/bazel-skylib/archive/0.6.2.tar.gz') + .reply(200, tarContent); + + httpMock + .scope('https://mirror.bazel.build') + .get('/github.com/bazelbuild/bazel-skylib/archive/0.6.2.tar.gz') + .reply(200, tarContent); + + const res = await updateArtifacts( + partial<UpdateArtifact>({ + packageFileName: 'WORKSPACE', + updatedDeps: [upgrade], + newPackageFileContent: input, + }) + ); + + expect(res).toEqual([ + { + file: { + contents: output, + path: 'WORKSPACE', + type: 'addition', + }, + }, + ]); + }); + + it('updates maybe(_http_archive) with urls array', async () => { + const inputHash = + 'b5f6abe419da897b7901f90cbab08af958b97a8f3575b0d3dd062ac7ce78541f'; + const input = codeBlock` + maybe( + _http_archive, + name = "bazel_skylib", + sha256 = "${inputHash}", + strip_prefix = "bazel-skylib-0.5.0", + urls = [ + "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/archive/0.5.0.tar.gz", + "https://github.com/bazelbuild/bazel-skylib/archive/0.5.0.tar.gz", + ], + ) + `; + + const currentValue = '0.5.0'; + const newValue = '0.6.2'; + const upgrade = { + depName: 'bazel_skylib', + depType: 'http_archive', + repo: 'bazelbuild/bazel-skylib', + managerData: { idx: 0 }, + currentValue, + newValue, + }; + + const tarContent = Buffer.from('foo'); + const outputHash = crypto + .createHash('sha256') + .update(tarContent) + .digest('hex'); + + const output = input + .replace(currentValue, newValue) + .replace(currentValue, newValue) + .replace(currentValue, newValue) + .replace(inputHash, outputHash); + + httpMock + .scope('https://github.com') + .get('/bazelbuild/bazel-skylib/archive/0.6.2.tar.gz') + .reply(200, tarContent); + httpMock + .scope('https://mirror.bazel.build') + .get('/github.com/bazelbuild/bazel-skylib/archive/0.6.2.tar.gz') + .reply(200, tarContent); + + const res = await updateArtifacts( + partial<UpdateArtifact>({ + packageFileName: 'WORKSPACE', + updatedDeps: [upgrade], + newPackageFileContent: input, + }) + ); + + expect(res).toEqual([ + { + file: { + contents: output, + path: 'WORKSPACE', + type: 'addition', + }, + }, + ]); + }); + it('updates one http_archive alongside others', async () => { const inputHash = '5aef09ed3279aa01d5c928e3beb248f9ad32dde6aafe6373a8c994c3ce643064'; diff --git a/lib/modules/manager/bazel/extract.ts b/lib/modules/manager/bazel/extract.ts index 3eee87916a904ad39d7ca544bf78da49e54d1fb3..78349672af895f3493a2c5099e8fd3b6f9d47f8d 100644 --- a/lib/modules/manager/bazel/extract.ts +++ b/lib/modules/manager/bazel/extract.ts @@ -1,6 +1,10 @@ import type { PackageDependency, PackageFileContent } from '../types'; import { parse } from './parser'; import { extractDepsFromFragment } from './rules'; +import { dockerRules } from './rules/docker'; +import { gitRules } from './rules/git'; +import { goRules } from './rules/go'; +import { ociRules } from './rules/oci'; import type { RecordFragment } from './types'; export function extractPackageFile( @@ -19,15 +23,11 @@ export function extractPackageFile( for (const dep of extractDepsFromFragment(fragment)) { dep.managerData = { idx }; - // Selectively provide `replaceString` in order - // to auto-replace functionality work correctly. + // Selectively provide `replaceString` in order to make auto-replace + // functionality work correctly. + const rules = [...dockerRules, ...ociRules, ...gitRules, ...goRules]; const replaceString = fragment.value; - if ( - replaceString.startsWith('container_pull') || - replaceString.startsWith('oci_pull') || - replaceString.startsWith('git_repository') || - replaceString.startsWith('go_repository') - ) { + if (rules.some((rule) => replaceString.startsWith(rule))) { if (dep.currentValue && dep.currentDigest) { dep.replaceString = replaceString; } diff --git a/lib/modules/manager/bazel/parser.spec.ts b/lib/modules/manager/bazel/parser.spec.ts index 7bdccab9167eb3dcedcf9956925b4150d39ef738..2b56cad88f0e0b09b8a60db212a96fd85a08f307 100644 --- a/lib/modules/manager/bazel/parser.spec.ts +++ b/lib/modules/manager/bazel/parser.spec.ts @@ -7,6 +7,8 @@ describe('modules/manager/bazel/parser', () => { const input = codeBlock` go_repository(name = "foo") maybe(go_repository, name = "bar", deps = ["baz", "qux"]) + _go_repository(name = "quux") + maybe(_go_repository, name = "corge", deps = ["grault", "garply"]) `; const res = parse(input); @@ -39,10 +41,40 @@ describe('modules/manager/bazel/parser', () => { }, }, }, + { + type: 'record', + value: '_go_repository(name = "quux")', + offset: 86, + children: { + rule: { type: 'string', value: '_go_repository', offset: 86 }, + name: { type: 'string', value: 'quux', offset: 109 }, + }, + }, + { + type: 'record', + value: + 'maybe(_go_repository, name = "corge", deps = ["grault", "garply"])', + offset: 116, + children: { + rule: { type: 'string', value: '_go_repository', offset: 122 }, + name: { type: 'string', value: 'corge', offset: 146 }, + deps: { + type: 'array', + value: '["grault", "garply"]', + offset: 161, + children: [ + { type: 'string', value: 'grault', offset: 163 }, + { type: 'string', value: 'garply', offset: 173 }, + ], + }, + }, + }, ]); expect(res?.map(extract)).toMatchObject([ { rule: 'go_repository', name: 'foo' }, { rule: 'go_repository', name: 'bar', deps: ['baz', 'qux'] }, + { rule: '_go_repository', name: 'quux' }, + { rule: '_go_repository', name: 'corge', deps: ['grault', 'garply'] }, ]); }); diff --git a/lib/modules/manager/bazel/parser.ts b/lib/modules/manager/bazel/parser.ts index 42905bb072490db3260bcac8145623e53df7663b..996a05c65e1cfe84d5cfd3ce50cac0335b4e3e3a 100644 --- a/lib/modules/manager/bazel/parser.ts +++ b/lib/modules/manager/bazel/parser.ts @@ -261,7 +261,7 @@ function ruleNameHandler(ctx: Ctx, { value, offset }: lexer.Token): Ctx { /** * Matches regular rules: * - `git_repository(...)` - * - `go_repository(...)` + * - `_go_repository(...)` */ const regularRule = q .sym<Ctx>(supportedRulesRegex, (ctx, token) => @@ -272,7 +272,7 @@ const regularRule = q /** * Matches "maybe"-form rules: * - `maybe(git_repository, ...)` - * - `maybe(go_repository, ...)` + * - `maybe(_go_repository, ...)` */ const maybeRule = q .sym<Ctx>('maybe', recordStartHandler) diff --git a/lib/modules/manager/bazel/rules/docker.ts b/lib/modules/manager/bazel/rules/docker.ts index 4ccc83d62694c69d1e1312cc988d7068d5720147..6d75313c5ae35e5a5f194106ea8b0300e7f8c68e 100644 --- a/lib/modules/manager/bazel/rules/docker.ts +++ b/lib/modules/manager/bazel/rules/docker.ts @@ -3,7 +3,7 @@ import { DockerDatasource } from '../../../datasource/docker'; import { id as dockerVersioning } from '../../../versioning/docker'; import type { PackageDependency } from '../../types'; -export const dockerRules = ['container_pull'] as const; +export const dockerRules = ['container_pull', '_container_pull'] as const; export const DockerTarget = z .object({ diff --git a/lib/modules/manager/bazel/rules/git.ts b/lib/modules/manager/bazel/rules/git.ts index 1cafda692c18857ab20abe91d325c38e62319f56..d63f5956b761ed615a1d7af55cd4f21aaad43486 100644 --- a/lib/modules/manager/bazel/rules/git.ts +++ b/lib/modules/manager/bazel/rules/git.ts @@ -17,7 +17,7 @@ function githubPackageName(input: string): string | undefined { return parseGithubUrl(input)?.match(githubUrlRegex)?.groups?.packageName; } -export const gitRules = ['git_repository'] as const; +export const gitRules = ['git_repository', '_git_repository'] as const; export const GitTarget = z .object({ diff --git a/lib/modules/manager/bazel/rules/go.ts b/lib/modules/manager/bazel/rules/go.ts index 5bbf35302e2896163ceab2391f876978298dd69f..1512001e04a5419f17df3620f28040a1a5f1c8a8 100644 --- a/lib/modules/manager/bazel/rules/go.ts +++ b/lib/modules/manager/bazel/rules/go.ts @@ -3,7 +3,7 @@ import { regEx } from '../../../../util/regex'; import { GoDatasource } from '../../../datasource/go'; import type { PackageDependency } from '../../types'; -export const goRules = ['go_repository'] as const; +export const goRules = ['go_repository', '_go_repository'] as const; export const GoTarget = z .object({ diff --git a/lib/modules/manager/bazel/rules/http.ts b/lib/modules/manager/bazel/rules/http.ts index 16c87fe5a6fc3108a7c11e73538a53db6f95c141..fe4b166172d22b151414c48f924020769e3f07e8 100644 --- a/lib/modules/manager/bazel/rules/http.ts +++ b/lib/modules/manager/bazel/rules/http.ts @@ -119,7 +119,12 @@ export function parseArchiveUrl( return null; } -export const httpRules = ['http_archive', 'http_file'] as const; +export const httpRules = [ + 'http_archive', + '_http_archive', + 'http_file', + '_http_file', +] as const; export const HttpTarget = z .object({ diff --git a/lib/modules/manager/bazel/rules/index.ts b/lib/modules/manager/bazel/rules/index.ts index ca016579171f61054c9de00471721ae6a894841c..30d275fe0f0fc8b3ed5a81bf255da8e5da7ab6a3 100644 --- a/lib/modules/manager/bazel/rules/index.ts +++ b/lib/modules/manager/bazel/rules/index.ts @@ -30,7 +30,7 @@ const supportedRules = [ ...httpRules, ...mavenRules, ]; -export const supportedRulesRegex = regEx(`^${supportedRules.join('|')}$`); +export const supportedRulesRegex = regEx(`^(?:${supportedRules.join('|')})$`); export function extractDepsFromFragmentData( fragmentData: FragmentData diff --git a/lib/modules/manager/bazel/rules/maven.ts b/lib/modules/manager/bazel/rules/maven.ts index 5107ea1ad499051d342f88e8b132f194a719b9ee..7622bb7b374ea0c9681954c2eb23f87366fffb27 100644 --- a/lib/modules/manager/bazel/rules/maven.ts +++ b/lib/modules/manager/bazel/rules/maven.ts @@ -4,7 +4,7 @@ import { MavenDatasource } from '../../../datasource/maven'; import { id as versioning } from '../../../versioning/gradle'; import type { PackageDependency } from '../../types'; -export const mavenRules = ['maven_install'] as const; +export const mavenRules = ['maven_install', '_maven_install'] as const; const ArtifactSpec = z.union([ z.object({ diff --git a/lib/modules/manager/bazel/rules/oci.ts b/lib/modules/manager/bazel/rules/oci.ts index 5e49791e88671b8b8cfa087833f9fc00d9921c19..e441db4ef53f784a1ac91cf6d96c1b0140e453df 100644 --- a/lib/modules/manager/bazel/rules/oci.ts +++ b/lib/modules/manager/bazel/rules/oci.ts @@ -3,7 +3,7 @@ import { DockerDatasource } from '../../../datasource/docker'; import { id as dockerVersioning } from '../../../versioning/docker'; import type { PackageDependency } from '../../types'; -export const ociRules = ['oci_pull'] as const; +export const ociRules = ['oci_pull', '_oci_pull'] as const; export const OciTarget = z .object({