diff --git a/lib/modules/manager/bazel/rules/http.ts b/lib/modules/manager/bazel/rules/http.ts index 9f1ba2b35008cae0bf5be3b0d067d008f2b8798f..f2dbc0f805ad61197747c2227ebcd937b1254ebd 100644 --- a/lib/modules/manager/bazel/rules/http.ts +++ b/lib/modules/manager/bazel/rules/http.ts @@ -1,52 +1,94 @@ import is from '@sindresorhus/is'; import { z } from 'zod'; -import { regEx } from '../../../../util/regex'; +import { escapeRegExp, regEx } from '../../../../util/regex'; import { parseUrl } from '../../../../util/url'; import { GithubReleasesDatasource } from '../../../datasource/github-releases'; import { GithubTagsDatasource } from '../../../datasource/github-tags'; import type { PackageDependency } from '../../types'; -import type { UrlParsedResult } from '../types'; -export function parseArchiveUrl( - urlString: string | undefined | null -): UrlParsedResult | null { - if (!urlString) { - return null; - } - const url = parseUrl(urlString); - if (!url || url.host !== 'github.com' || !url.pathname) { - return null; - } - const [p0, p1, p2, p3, p4, p5] = url.pathname.split('/').slice(1); - const repo = p0 + '/' + p1; +// Source: https://bazel.build/rules/lib/repo/http +const archives = [ + '.zip', + '.tar', + '.jar', + '.war', + '.aar', + '.ar', + '.deb', + + '.gz', + '.tar.gz', + '.tgz', + + '.bz2', + '.tar.bz2', + '.tbz2', + + '.xz', + '.tar.xz', + '.txz', + + '.zst', + '.tar.zst', + '.tzst', +]; + +const archiveSuffixRegex = regEx( + `(?:${archives.map(escapeRegExp).join('|')})$` +); + +function stripArchiveSuffix(value: string): string { + return value.replace(archiveSuffixRegex, ''); +} + +function isHash(value: unknown): value is string { + return is.string(value) && regEx(/[0-9a-z]{40}/i).test(value); +} + +export function parseGithubPath( + pathname: string +): Partial<PackageDependency> | null { + const [p0, p1, p2, p3, p4, p5] = pathname.split('/').slice(1); + const packageName = p0 + '/' + p1; let datasource = ''; - let currentValue: string | null = null; + let value: string | null = null; if (p2 === 'releases' && p3 === 'download') { // https://github.com/foo/bar/releases/download/1.2.3/bar-1.2.3.tar.gz datasource = GithubReleasesDatasource.id; - currentValue = p4; + value = p4; } else if (p2 === 'archive' && p3 === 'refs' && p4 === 'tags') { // https://github.com/foo/bar/archive/refs/tags/v1.2.3.tar.gz datasource = GithubTagsDatasource.id; - currentValue = p5; + value = p5; } else if (p2 === 'archive') { // https://github.com/foo/bar/archive/1.2.3.tar.gz datasource = GithubTagsDatasource.id; - currentValue = p3; + value = p3; } - if (currentValue) { - // Strip archive extension to get hash or tag. - // Tolerates formats produced by Git(Hub|Lab) and allowed by http_archive - // Note: Order matters in suffix list to strip, e.g. .tar.gz. - for (const extension of ['.gz', '.bz2', '.xz', '.tar', '.tgz', '.zip']) { - if (currentValue.endsWith(extension)) { - currentValue = currentValue.slice(0, -extension.length); - } - } + if (!value) { + return null; + } + + value = stripArchiveSuffix(value); + return isHash(value) + ? { datasource, packageName, currentDigest: value } + : { datasource, packageName, currentValue: value }; +} + +export function parseArchiveUrl( + urlString: string | undefined | null +): Partial<PackageDependency> | null { + if (!urlString) { + return null; + } + + const url = parseUrl(urlString); - return { datasource, repo, currentValue }; + if (url?.host === 'github.com') { + return parseGithubPath(url.pathname); } + return null; } @@ -71,13 +113,22 @@ export const HttpTarget = z datasource: parsedUrl.datasource, depType: rule, depName: name, - packageName: parsedUrl.repo, + packageName: parsedUrl.packageName, }; - if (regEx(/^[a-f0-9]{40}$/i).test(parsedUrl.currentValue)) { - dep.currentDigest = parsedUrl.currentValue; - } else { + // We don't want to set both `currentValue` and `currentDigest`. + // + // What we want is to provide the first occurrence of `currentValue`, + // or, if it's not available, `currentDigest`. + // + // Auto-replace mechanism will replace this first occurrence, + // and artifact update function will do the rest. + // + // Hence, `if-else-if` is being used here. + if (parsedUrl.currentValue) { dep.currentValue = parsedUrl.currentValue; + } else if (parsedUrl.currentDigest) { + dep.currentDigest = parsedUrl.currentDigest; } return [dep]; diff --git a/lib/modules/manager/bazel/rules/index.spec.ts b/lib/modules/manager/bazel/rules/index.spec.ts index 761222f6eabfb484357ee3c3f1f752f9803d08fb..83963981517e1e7aba0c871f815d26fe06449b06 100644 --- a/lib/modules/manager/bazel/rules/index.spec.ts +++ b/lib/modules/manager/bazel/rules/index.spec.ts @@ -16,8 +16,8 @@ describe('modules/manager/bazel/rules/index', () => { ) ).toEqual({ datasource: 'github-tags', - repo: 'foo/bar', - currentValue: 'abcdef0123abcdef0123abcdef0123abcdef0123', + packageName: 'foo/bar', + currentDigest: 'abcdef0123abcdef0123abcdef0123abcdef0123', }); // Archive of a release @@ -27,7 +27,7 @@ describe('modules/manager/bazel/rules/index', () => { ) ).toEqual({ datasource: 'github-releases', - repo: 'foo/bar', + packageName: 'foo/bar', currentValue: '1.2.3', }); @@ -38,7 +38,7 @@ describe('modules/manager/bazel/rules/index', () => { ) ).toEqual({ datasource: 'github-tags', - repo: 'aspect-build/rules_js', + packageName: 'aspect-build/rules_js', currentValue: 'v1.1.2', }); }); diff --git a/lib/modules/manager/bazel/types.ts b/lib/modules/manager/bazel/types.ts index ea94a32848bf1551cec586c796406c33067cd6d0..4c27a6c4716e69ed4f637dbd388d1555c073fa3e 100644 --- a/lib/modules/manager/bazel/types.ts +++ b/lib/modules/manager/bazel/types.ts @@ -1,9 +1,3 @@ -export interface UrlParsedResult { - datasource: string; - repo: string; - currentValue: string; -} - export interface BazelManagerData { idx: number; }