renovate 44.59.3 → 44.60.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -98,10 +98,11 @@ var GoProxyDatasource = class GoProxyDatasource extends Datasource {
98
98
  }
99
99
  } catch (err) {
100
100
  const statusCode = (err instanceof ExternalHostError ? err.err : err)?.response?.statusCode;
101
- const canFallback = fallback === "|" ? true : statusCode === 404 || statusCode === 410;
102
- const msg = canFallback ? "Goproxy error: trying next URL provided with GOPROXY" : "Goproxy error: skipping other URLs provided with GOPROXY";
103
- logger.debug({ err }, msg);
104
- if (!canFallback) break;
101
+ if (!(fallback === "|" ? true : statusCode === 404 || statusCode === 410)) {
102
+ logger.debug({ err }, "Goproxy error: not falling back to other URLs provided with GOPROXY, rethrowing");
103
+ this.handleGenericErrors(err);
104
+ }
105
+ logger.debug({ err }, "Goproxy error: trying next URL provided with GOPROXY");
105
106
  }
106
107
  if (result && !result.sourceUrl) try {
107
108
  const datasource = await BaseGoDatasource.getDatasource(packageName);
@@ -1 +1 @@
1
- {"version":3,"file":"releases-goproxy.js","names":["p.map","HttpError","goVersioning"],"sources":["../../../../lib/modules/datasource/go/releases-goproxy.ts"],"sourcesContent":["import {\n isNonEmptyString,\n isNonEmptyStringAndNotWhitespace,\n isTruthy,\n} from '@sindresorhus/is';\nimport type { ConstraintsFilter } from '../../../config/types.ts';\nimport { logger } from '../../../logger/index.ts';\nimport { ExternalHostError } from '../../../types/errors/external-host-error.ts';\nimport { withCache } from '../../../util/cache/package/with-cache.ts';\nimport { detectPlatform } from '../../../util/common.ts';\nimport { getEnv } from '../../../util/env.ts';\nimport { filterMap } from '../../../util/filter-map.ts';\nimport { queryReleases } from '../../../util/github/graphql/index.ts';\nimport { GithubHttp } from '../../../util/http/github.ts';\nimport { HttpError } from '../../../util/http/index.ts';\nimport * as p from '../../../util/promises.ts';\nimport { newlineRegex, regEx } from '../../../util/regex.ts';\nimport type { Timestamp } from '../../../util/timestamp.ts';\nimport { asTimestamp } from '../../../util/timestamp.ts';\nimport {\n joinUrlParts,\n parseUrl,\n trimLeadingSlash,\n trimTrailingSlash,\n} from '../../../util/url.ts';\nimport goVersioning from '../../versioning/go-mod-directive/index.ts';\nimport { Datasource } from '../datasource.ts';\nimport { GithubReleasesDatasource } from '../github-releases/index.ts';\nimport type { GetReleasesConfig, Release, ReleaseResult } from '../types.ts';\nimport { BaseGoDatasource } from './base.ts';\nimport { getSourceUrl } from './common.ts';\nimport { parseGoproxy, parseNoproxy } from './goproxy-parser.ts';\nimport { GoDirectDatasource } from './releases-direct.ts';\nimport { VersionInfo } from './schema.ts';\n\n/** TODO #42566 */\nconst goVersionRegex = regEx(/^\\s*go\\s+(?<version>[^\\s]+)\\s*$/);\n\nconst modRegex = regEx(/^(?<baseMod>.*?)(?:[./]v(?<majorVersion>\\d+))?$/);\n\nconst versionTagRegex = regEx(/^v\\d/);\n\n/**\n * Modules with a major version of v2 or above which have no `go.mod` are reported by a Go proxy with a `+incompatible` suffix, which is absent from the tag they were published as.\n *\n * @see https://go.dev/ref/mod#non-module-compat\n */\nconst incompatibleSuffixRegex = regEx(/\\+incompatible$/);\n\n/**\n * @see https://go.dev/ref/mod#pseudo-versions\n */\nconst pseudoVersionRegex = regEx(\n /v\\d+\\.\\d+\\.\\d+-(?:\\w+\\.)?(?:0\\.)?(?<timestamp>\\d{14})-(?<digest>[a-f0-9]{12})/i,\n);\n\nexport function pseudoVersionToRelease(pseudoVersion: string): Release | null {\n const match = pseudoVersion.match(pseudoVersionRegex)?.groups;\n if (!match) {\n return null;\n }\n\n const { digest: newDigest, timestamp } = match;\n const releaseTimestamp = asTimestamp(timestamp);\n\n return {\n version: pseudoVersion,\n newDigest,\n releaseTimestamp,\n };\n}\n\n/**\n * A Go module which lives in a subdirectory of its repository is tagged with that subdirectory as a prefix, so `github.com/aws/aws-sdk-go-v2/service/s3` publishes `v1.2.3` as the tag `service/s3/v1.2.3`.\n *\n * @see https://go.dev/ref/mod#vcs-version\n *\n * Because we can't tell from the module path alone where the repository ends and the subdirectory begins, we try each candidate prefix - longest first - against the tags we actually found, in the same way as `filterByPrefix` does for direct lookups.\n */\nexport function getTagPrefix(goModule: string, tags: Iterable<string>): string {\n const nameParts = goModule\n .replace(regEx(/\\/v\\d+$/), '')\n .split('/')\n .slice(1);\n const tagNames = [...tags];\n\n while (nameParts.length) {\n const prefix = `${nameParts.join('/')}/`;\n\n const isUsed = tagNames.some(\n (tag) =>\n tag.startsWith(prefix) &&\n versionTagRegex.test(tag.slice(prefix.length)),\n );\n if (isUsed) {\n return prefix;\n }\n\n nameParts.shift();\n }\n\n return '';\n}\n\nexport class GoProxyDatasource extends Datasource {\n static readonly id = 'go-proxy';\n\n constructor() {\n super(GoProxyDatasource.id);\n }\n\n readonly direct = new GoDirectDatasource();\n\n private readonly githubHttp = new GithubHttp(GithubReleasesDatasource.id);\n\n private async _getReleases(\n config: GetReleasesConfig,\n ): Promise<ReleaseResult | null> {\n const { packageName } = config;\n logger.trace(`goproxy.getReleases(${packageName})`);\n const goproxy = getEnv().GOPROXY ?? 'https://proxy.golang.org,direct';\n if (goproxy === 'direct') {\n return this.direct.getReleases(config);\n }\n const proxyList = parseGoproxy(goproxy);\n const noproxy = parseNoproxy();\n\n let result: ReleaseResult | null = null;\n let servedByProxy = false;\n\n if (noproxy?.test(packageName)) {\n logger.debug(`Fetching ${packageName} via GONOPROXY match`);\n result = await this.direct.getReleases(config);\n return result;\n }\n\n for (const { url, fallback } of proxyList) {\n try {\n if (url === 'off') {\n break;\n } else if (url === 'direct') {\n result = await this.direct.getReleases(config);\n break;\n }\n\n const res = await this.getVersionsWithInfo(\n url,\n packageName,\n config.constraintsFiltering,\n );\n if (res.releases.length) {\n result = res;\n servedByProxy = true;\n break;\n }\n } catch (err) {\n const potentialHttpError =\n err instanceof ExternalHostError ? err.err : err;\n const statusCode = potentialHttpError?.response?.statusCode;\n const canFallback =\n fallback === '|' ? true : statusCode === 404 || statusCode === 410;\n const msg = canFallback\n ? 'Goproxy error: trying next URL provided with GOPROXY'\n : 'Goproxy error: skipping other URLs provided with GOPROXY';\n logger.debug({ err }, msg);\n if (!canFallback) {\n break;\n }\n }\n }\n\n if (result && !result.sourceUrl) {\n try {\n const datasource = await BaseGoDatasource.getDatasource(packageName);\n const sourceUrl = getSourceUrl(datasource);\n if (sourceUrl) {\n result.sourceUrl = sourceUrl;\n }\n } catch (err) {\n logger.trace({ err }, `Can't get datasource for ${packageName}`);\n }\n }\n\n if (result?.sourceUrl && servedByProxy) {\n await this.addGithubReleaseTimestamps(\n packageName,\n result.sourceUrl,\n result.releases,\n );\n }\n\n return result;\n }\n\n /**\n * A Go proxy reports the commit time of the tagged commit as a version's `Time`, which can be much earlier than the point at which that version was released.\n *\n * When the module is hosted on GitHub and the version has a GitHub Release, the Release's publication time is a better indicator of when the version became available.\n */\n async addGithubReleaseTimestamps(\n packageName: string,\n sourceUrl: string,\n releases: Release[],\n ): Promise<void> {\n if (detectPlatform(sourceUrl) !== 'github') {\n return;\n }\n\n const parsedUrl = parseUrl(sourceUrl);\n /* v8 ignore next -- detectPlatform only returns a platform for parseable URLs */\n if (!parsedUrl) {\n return;\n }\n\n const repository = trimTrailingSlash(\n trimLeadingSlash(parsedUrl.pathname),\n ).replace(regEx(/\\.git$/), '');\n\n try {\n const githubReleases = await queryReleases(\n {\n packageName: repository,\n registryUrl: parsedUrl.origin,\n },\n this.githubHttp,\n );\n\n const timestamps = new Map<string, Timestamp>();\n for (const { version, releaseTimestamp } of githubReleases) {\n timestamps.set(version, releaseTimestamp);\n }\n\n const tagPrefix = getTagPrefix(packageName, timestamps.keys());\n for (const release of releases) {\n const version = release.version.replace(incompatibleSuffixRegex, '');\n const releaseTimestamp = timestamps.get(`${tagPrefix}${version}`);\n if (\n releaseTimestamp &&\n (!release.releaseTimestamp ||\n releaseTimestamp > release.releaseTimestamp)\n ) {\n release.releaseTimestamp = releaseTimestamp;\n }\n }\n } catch (err) {\n logger.debug(\n { err, packageName },\n 'Error fetching GitHub Releases for Go module',\n );\n }\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n return withCache(\n {\n namespace: `datasource-${GoProxyDatasource.id}`,\n key: GoProxyDatasource.getCacheKey(config),\n fallback: true,\n },\n () => this._getReleases(config),\n );\n }\n\n /**\n * Avoid ambiguity when serving from case-insensitive file systems.\n *\n * @see https://golang.org/ref/mod#goproxy-protocol\n */\n encodeCase(input: string): string {\n return input.replace(regEx(/([A-Z])/g), (x) => `!${x.toLowerCase()}`);\n }\n\n async listVersions(baseUrl: string, packageName: string): Promise<Release[]> {\n const url = joinUrlParts(\n baseUrl,\n this.encodeCase(packageName),\n '@v',\n 'list',\n );\n const { body } = await this.http.getText(url);\n return filterMap(body.split(newlineRegex), (str) => {\n if (!isNonEmptyStringAndNotWhitespace(str)) {\n return null;\n }\n\n const [version, timestamp] = str.trim().split(regEx(/\\s+/));\n const release: Release = pseudoVersionToRelease(version) ?? { version };\n\n const releaseTimestamp = asTimestamp(timestamp);\n if (releaseTimestamp) {\n release.releaseTimestamp = releaseTimestamp;\n }\n\n return release;\n });\n }\n\n async versionInfo(\n baseUrl: string,\n packageName: string,\n version: string,\n ): Promise<Release> {\n const url = joinUrlParts(\n baseUrl,\n this.encodeCase(packageName),\n '@v',\n `${version}.info`,\n );\n const res = await this.http.getJson(url, VersionInfo);\n\n const result: Release = {\n version: res.body.Version,\n };\n\n const releaseTimestamp = asTimestamp(res.body.Time);\n if (releaseTimestamp) {\n result.releaseTimestamp = releaseTimestamp;\n }\n\n return result;\n }\n\n /**\n * Retrieve the `go` directive for a given Go Module.\n *\n * NOTE that this means the `go` directive, not the `toolchain` directive.\n */\n async retrieveGoDirectiveForModule(\n baseUrl: string,\n packageName: string,\n version: string,\n ): Promise<string | undefined> {\n return withCache(\n {\n namespace: `datasource-${GoProxyDatasource.id}`,\n key: GoProxyDatasource.getVersionedCacheKey(packageName, version),\n // a module's `go.mod` should /never/ change after it's published. If going via the Go Proxy and the Go Checksum Database, a change in this value will result in build failures.\n ttlMinutes: 100 * 24 * 60,\n },\n () => this._retrieveGoDirectiveForModule(baseUrl, packageName, version),\n );\n }\n\n async _retrieveGoDirectiveForModule(\n baseUrl: string,\n packageName: string,\n version: string,\n ): Promise<string | undefined> {\n const url = joinUrlParts(\n baseUrl,\n this.encodeCase(packageName),\n '@v',\n `${version}.mod`,\n );\n const res = await this.http.getText(url);\n\n let goDirective: string | undefined = undefined;\n\n for (const line of res.body.split('\\n')) {\n const goVersionMatches = goVersionRegex.exec(line)?.groups;\n if (goVersionMatches) {\n goDirective = goVersionMatches.version;\n break;\n }\n }\n\n if (!goDirective) {\n return goDirective;\n }\n\n // always return it in full SemVer format, which can then be matched on using `semver` or `semver-coerced`\n const parts = goDirective.split('.');\n if (parts.length === 1) {\n return `${parts[0]}.0.0`;\n }\n if (parts.length === 2) {\n return `${parts[0]}.${parts[1]}.0`;\n }\n return `${parts[0]}.${parts[1]}.${parts[2]}`;\n }\n\n async getLatestVersion(\n baseUrl: string,\n packageName: string,\n ): Promise<{ version: string; sourceUrl?: string } | null> {\n try {\n const url = joinUrlParts(\n baseUrl,\n this.encodeCase(packageName),\n '@latest',\n );\n const res = await this.http.getJson(url, VersionInfo);\n const { Version: version, Origin: origin } = res.body;\n // Extract sourceUrl from GOPROXY Origin when present, avoiding go-get to\n // vanity hosts (https://github.com/renovatebot/renovate/discussions/44898)\n const sourceUrl =\n origin?.VCS === 'git' && isNonEmptyString(origin.URL)\n ? origin.URL.replace(regEx(/\\.git$/), '')\n : undefined;\n return { version, sourceUrl };\n } catch (err) {\n logger.trace({ err }, 'Failed to get latest version');\n return null;\n }\n }\n\n async getVersionsWithInfo(\n baseUrl: string,\n packageName: string,\n constraintsFiltering: ConstraintsFilter | undefined,\n ): Promise<ReleaseResult> {\n const isGopkgin = packageName.startsWith('gopkg.in/');\n const majorSuffixSeparator = isGopkgin ? '.' : '/';\n const modParts = packageName.match(modRegex)?.groups;\n const baseMod =\n modParts?.baseMod ??\n /* v8 ignore next -- defensive: modRegex matches any non-empty package name, so baseMod is always set */ packageName;\n const packageMajor = parseInt(modParts?.majorVersion ?? '0', 10);\n\n const result: ReleaseResult = { releases: [] };\n for (let major = packageMajor; ; major += 1) {\n let pkg = `${baseMod}${majorSuffixSeparator}v${major}`;\n if (!isGopkgin && major < 2) {\n pkg = baseMod;\n major += 1; // v0 and v1 are the same module\n }\n\n let releases: Release[] = [];\n\n try {\n const res = await this.listVersions(baseUrl, pkg);\n\n // Artifactory returns all versions in any major (past and future),\n // so starting from v2, we filter them in order to avoid the infinite loop\n const filteredReleases = res.filter(({ version }) => {\n if (major < 2) {\n return true;\n }\n\n return (\n version.split(regEx(/[^\\d]+/)).find(isTruthy) === major.toString()\n );\n });\n\n releases = await p.map(filteredReleases, async (versionInfo) => {\n const { version, newDigest, releaseTimestamp } = versionInfo;\n\n if (releaseTimestamp) {\n return { version, newDigest, releaseTimestamp };\n }\n\n try {\n return await this.versionInfo(baseUrl, pkg, version);\n } catch (err) {\n logger.trace({ err }, `Can't obtain data from ${baseUrl}`);\n return { version };\n }\n });\n\n if (constraintsFiltering === 'strict') {\n releases = await p.map(releases, async (rel) => {\n try {\n const goDirective = await this.retrieveGoDirectiveForModule(\n baseUrl,\n pkg,\n rel.version,\n );\n if (goDirective) {\n rel.constraints ??= {};\n rel.constraints['%goMod'] ??= [];\n rel.constraints['%goMod'].push(goDirective);\n }\n } catch (err) {\n logger.trace(\n { err },\n `Can't obtain \\`go\\` directive from ${baseUrl}`,\n );\n }\n\n return rel;\n });\n }\n\n result.releases.push(...releases);\n } catch (err) {\n const potentialHttpError =\n err instanceof ExternalHostError ? err.err : err;\n const status = potentialHttpError.response?.statusCode;\n if (\n potentialHttpError instanceof HttpError &&\n (status === 404 || status === 403) &&\n major !== packageMajor\n ) {\n break;\n }\n\n throw err;\n }\n\n const latest = await this.getLatestVersion(baseUrl, pkg);\n if (latest) {\n const { version: latestVersion, sourceUrl } = latest;\n result.tags ??= {};\n result.tags.latest ??= latestVersion;\n if (goVersioning.isGreaterThan(latestVersion, result.tags.latest)) {\n result.tags.latest = latestVersion;\n }\n if (sourceUrl) {\n result.sourceUrl ??= sourceUrl;\n }\n if (!result.releases.length) {\n const releaseFromLatest = pseudoVersionToRelease(latestVersion);\n if (releaseFromLatest) {\n result.releases.push(releaseFromLatest);\n }\n }\n }\n\n if (!releases.length) {\n break;\n }\n }\n\n return result;\n }\n\n static getCacheKey({\n packageName,\n constraintsFiltering,\n }: GetReleasesConfig): string {\n const goproxy = getEnv().GOPROXY;\n const noproxy = parseNoproxy();\n const constraintsFilteringKey =\n constraintsFiltering && constraintsFiltering !== 'none'\n ? `@@${constraintsFiltering}`\n : '';\n // TODO: types (#22198)\n return `${packageName}@@${goproxy}@@${noproxy?.toString()}${constraintsFilteringKey}`;\n }\n\n static getVersionedCacheKey(packageName: string, version: string): string {\n const goproxy = getEnv().GOPROXY;\n const noproxy = parseNoproxy();\n // TODO: types (#22198)\n return `${packageName}@@${version}@@${goproxy}@@${noproxy?.toString()}`;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"releases-goproxy.js","names":["p.map","HttpError","goVersioning"],"sources":["../../../../lib/modules/datasource/go/releases-goproxy.ts"],"sourcesContent":["import {\n isNonEmptyString,\n isNonEmptyStringAndNotWhitespace,\n isTruthy,\n} from '@sindresorhus/is';\nimport type { ConstraintsFilter } from '../../../config/types.ts';\nimport { logger } from '../../../logger/index.ts';\nimport { ExternalHostError } from '../../../types/errors/external-host-error.ts';\nimport { withCache } from '../../../util/cache/package/with-cache.ts';\nimport { detectPlatform } from '../../../util/common.ts';\nimport { getEnv } from '../../../util/env.ts';\nimport { filterMap } from '../../../util/filter-map.ts';\nimport { queryReleases } from '../../../util/github/graphql/index.ts';\nimport { GithubHttp } from '../../../util/http/github.ts';\nimport { HttpError } from '../../../util/http/index.ts';\nimport * as p from '../../../util/promises.ts';\nimport { newlineRegex, regEx } from '../../../util/regex.ts';\nimport type { Timestamp } from '../../../util/timestamp.ts';\nimport { asTimestamp } from '../../../util/timestamp.ts';\nimport {\n joinUrlParts,\n parseUrl,\n trimLeadingSlash,\n trimTrailingSlash,\n} from '../../../util/url.ts';\nimport goVersioning from '../../versioning/go-mod-directive/index.ts';\nimport { Datasource } from '../datasource.ts';\nimport { GithubReleasesDatasource } from '../github-releases/index.ts';\nimport type { GetReleasesConfig, Release, ReleaseResult } from '../types.ts';\nimport { BaseGoDatasource } from './base.ts';\nimport { getSourceUrl } from './common.ts';\nimport { parseGoproxy, parseNoproxy } from './goproxy-parser.ts';\nimport { GoDirectDatasource } from './releases-direct.ts';\nimport { VersionInfo } from './schema.ts';\n\n/** TODO #42566 */\nconst goVersionRegex = regEx(/^\\s*go\\s+(?<version>[^\\s]+)\\s*$/);\n\nconst modRegex = regEx(/^(?<baseMod>.*?)(?:[./]v(?<majorVersion>\\d+))?$/);\n\nconst versionTagRegex = regEx(/^v\\d/);\n\n/**\n * Modules with a major version of v2 or above which have no `go.mod` are reported by a Go proxy with a `+incompatible` suffix, which is absent from the tag they were published as.\n *\n * @see https://go.dev/ref/mod#non-module-compat\n */\nconst incompatibleSuffixRegex = regEx(/\\+incompatible$/);\n\n/**\n * @see https://go.dev/ref/mod#pseudo-versions\n */\nconst pseudoVersionRegex = regEx(\n /v\\d+\\.\\d+\\.\\d+-(?:\\w+\\.)?(?:0\\.)?(?<timestamp>\\d{14})-(?<digest>[a-f0-9]{12})/i,\n);\n\nexport function pseudoVersionToRelease(pseudoVersion: string): Release | null {\n const match = pseudoVersion.match(pseudoVersionRegex)?.groups;\n if (!match) {\n return null;\n }\n\n const { digest: newDigest, timestamp } = match;\n const releaseTimestamp = asTimestamp(timestamp);\n\n return {\n version: pseudoVersion,\n newDigest,\n releaseTimestamp,\n };\n}\n\n/**\n * A Go module which lives in a subdirectory of its repository is tagged with that subdirectory as a prefix, so `github.com/aws/aws-sdk-go-v2/service/s3` publishes `v1.2.3` as the tag `service/s3/v1.2.3`.\n *\n * @see https://go.dev/ref/mod#vcs-version\n *\n * Because we can't tell from the module path alone where the repository ends and the subdirectory begins, we try each candidate prefix - longest first - against the tags we actually found, in the same way as `filterByPrefix` does for direct lookups.\n */\nexport function getTagPrefix(goModule: string, tags: Iterable<string>): string {\n const nameParts = goModule\n .replace(regEx(/\\/v\\d+$/), '')\n .split('/')\n .slice(1);\n const tagNames = [...tags];\n\n while (nameParts.length) {\n const prefix = `${nameParts.join('/')}/`;\n\n const isUsed = tagNames.some(\n (tag) =>\n tag.startsWith(prefix) &&\n versionTagRegex.test(tag.slice(prefix.length)),\n );\n if (isUsed) {\n return prefix;\n }\n\n nameParts.shift();\n }\n\n return '';\n}\n\nexport class GoProxyDatasource extends Datasource {\n static readonly id = 'go-proxy';\n\n constructor() {\n super(GoProxyDatasource.id);\n }\n\n readonly direct = new GoDirectDatasource();\n\n private readonly githubHttp = new GithubHttp(GithubReleasesDatasource.id);\n\n private async _getReleases(\n config: GetReleasesConfig,\n ): Promise<ReleaseResult | null> {\n const { packageName } = config;\n logger.trace(`goproxy.getReleases(${packageName})`);\n const goproxy = getEnv().GOPROXY ?? 'https://proxy.golang.org,direct';\n if (goproxy === 'direct') {\n return this.direct.getReleases(config);\n }\n const proxyList = parseGoproxy(goproxy);\n const noproxy = parseNoproxy();\n\n let result: ReleaseResult | null = null;\n let servedByProxy = false;\n\n if (noproxy?.test(packageName)) {\n logger.debug(`Fetching ${packageName} via GONOPROXY match`);\n result = await this.direct.getReleases(config);\n return result;\n }\n\n for (const { url, fallback } of proxyList) {\n try {\n if (url === 'off') {\n break;\n } else if (url === 'direct') {\n result = await this.direct.getReleases(config);\n break;\n }\n\n const res = await this.getVersionsWithInfo(\n url,\n packageName,\n config.constraintsFiltering,\n );\n if (res.releases.length) {\n result = res;\n servedByProxy = true;\n break;\n }\n } catch (err) {\n const potentialHttpError =\n err instanceof ExternalHostError ? err.err : err;\n const statusCode = potentialHttpError?.response?.statusCode;\n const canFallback =\n fallback === '|' ? true : statusCode === 404 || statusCode === 410;\n if (!canFallback) {\n logger.debug(\n { err },\n 'Goproxy error: not falling back to other URLs provided with GOPROXY, rethrowing',\n );\n this.handleGenericErrors(err);\n }\n logger.debug(\n { err },\n 'Goproxy error: trying next URL provided with GOPROXY',\n );\n }\n }\n\n if (result && !result.sourceUrl) {\n try {\n const datasource = await BaseGoDatasource.getDatasource(packageName);\n const sourceUrl = getSourceUrl(datasource);\n if (sourceUrl) {\n result.sourceUrl = sourceUrl;\n }\n } catch (err) {\n logger.trace({ err }, `Can't get datasource for ${packageName}`);\n }\n }\n\n if (result?.sourceUrl && servedByProxy) {\n await this.addGithubReleaseTimestamps(\n packageName,\n result.sourceUrl,\n result.releases,\n );\n }\n\n return result;\n }\n\n /**\n * A Go proxy reports the commit time of the tagged commit as a version's `Time`, which can be much earlier than the point at which that version was released.\n *\n * When the module is hosted on GitHub and the version has a GitHub Release, the Release's publication time is a better indicator of when the version became available.\n */\n async addGithubReleaseTimestamps(\n packageName: string,\n sourceUrl: string,\n releases: Release[],\n ): Promise<void> {\n if (detectPlatform(sourceUrl) !== 'github') {\n return;\n }\n\n const parsedUrl = parseUrl(sourceUrl);\n /* v8 ignore next -- detectPlatform only returns a platform for parseable URLs */\n if (!parsedUrl) {\n return;\n }\n\n const repository = trimTrailingSlash(\n trimLeadingSlash(parsedUrl.pathname),\n ).replace(regEx(/\\.git$/), '');\n\n try {\n const githubReleases = await queryReleases(\n {\n packageName: repository,\n registryUrl: parsedUrl.origin,\n },\n this.githubHttp,\n );\n\n const timestamps = new Map<string, Timestamp>();\n for (const { version, releaseTimestamp } of githubReleases) {\n timestamps.set(version, releaseTimestamp);\n }\n\n const tagPrefix = getTagPrefix(packageName, timestamps.keys());\n for (const release of releases) {\n const version = release.version.replace(incompatibleSuffixRegex, '');\n const releaseTimestamp = timestamps.get(`${tagPrefix}${version}`);\n if (\n releaseTimestamp &&\n (!release.releaseTimestamp ||\n releaseTimestamp > release.releaseTimestamp)\n ) {\n release.releaseTimestamp = releaseTimestamp;\n }\n }\n } catch (err) {\n logger.debug(\n { err, packageName },\n 'Error fetching GitHub Releases for Go module',\n );\n }\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n return withCache(\n {\n namespace: `datasource-${GoProxyDatasource.id}`,\n key: GoProxyDatasource.getCacheKey(config),\n fallback: true,\n },\n () => this._getReleases(config),\n );\n }\n\n /**\n * Avoid ambiguity when serving from case-insensitive file systems.\n *\n * @see https://golang.org/ref/mod#goproxy-protocol\n */\n encodeCase(input: string): string {\n return input.replace(regEx(/([A-Z])/g), (x) => `!${x.toLowerCase()}`);\n }\n\n async listVersions(baseUrl: string, packageName: string): Promise<Release[]> {\n const url = joinUrlParts(\n baseUrl,\n this.encodeCase(packageName),\n '@v',\n 'list',\n );\n const { body } = await this.http.getText(url);\n return filterMap(body.split(newlineRegex), (str) => {\n if (!isNonEmptyStringAndNotWhitespace(str)) {\n return null;\n }\n\n const [version, timestamp] = str.trim().split(regEx(/\\s+/));\n const release: Release = pseudoVersionToRelease(version) ?? { version };\n\n const releaseTimestamp = asTimestamp(timestamp);\n if (releaseTimestamp) {\n release.releaseTimestamp = releaseTimestamp;\n }\n\n return release;\n });\n }\n\n async versionInfo(\n baseUrl: string,\n packageName: string,\n version: string,\n ): Promise<Release> {\n const url = joinUrlParts(\n baseUrl,\n this.encodeCase(packageName),\n '@v',\n `${version}.info`,\n );\n const res = await this.http.getJson(url, VersionInfo);\n\n const result: Release = {\n version: res.body.Version,\n };\n\n const releaseTimestamp = asTimestamp(res.body.Time);\n if (releaseTimestamp) {\n result.releaseTimestamp = releaseTimestamp;\n }\n\n return result;\n }\n\n /**\n * Retrieve the `go` directive for a given Go Module.\n *\n * NOTE that this means the `go` directive, not the `toolchain` directive.\n */\n async retrieveGoDirectiveForModule(\n baseUrl: string,\n packageName: string,\n version: string,\n ): Promise<string | undefined> {\n return withCache(\n {\n namespace: `datasource-${GoProxyDatasource.id}`,\n key: GoProxyDatasource.getVersionedCacheKey(packageName, version),\n // a module's `go.mod` should /never/ change after it's published. If going via the Go Proxy and the Go Checksum Database, a change in this value will result in build failures.\n ttlMinutes: 100 * 24 * 60,\n },\n () => this._retrieveGoDirectiveForModule(baseUrl, packageName, version),\n );\n }\n\n async _retrieveGoDirectiveForModule(\n baseUrl: string,\n packageName: string,\n version: string,\n ): Promise<string | undefined> {\n const url = joinUrlParts(\n baseUrl,\n this.encodeCase(packageName),\n '@v',\n `${version}.mod`,\n );\n const res = await this.http.getText(url);\n\n let goDirective: string | undefined = undefined;\n\n for (const line of res.body.split('\\n')) {\n const goVersionMatches = goVersionRegex.exec(line)?.groups;\n if (goVersionMatches) {\n goDirective = goVersionMatches.version;\n break;\n }\n }\n\n if (!goDirective) {\n return goDirective;\n }\n\n // always return it in full SemVer format, which can then be matched on using `semver` or `semver-coerced`\n const parts = goDirective.split('.');\n if (parts.length === 1) {\n return `${parts[0]}.0.0`;\n }\n if (parts.length === 2) {\n return `${parts[0]}.${parts[1]}.0`;\n }\n return `${parts[0]}.${parts[1]}.${parts[2]}`;\n }\n\n async getLatestVersion(\n baseUrl: string,\n packageName: string,\n ): Promise<{ version: string; sourceUrl?: string } | null> {\n try {\n const url = joinUrlParts(\n baseUrl,\n this.encodeCase(packageName),\n '@latest',\n );\n const res = await this.http.getJson(url, VersionInfo);\n const { Version: version, Origin: origin } = res.body;\n // Extract sourceUrl from GOPROXY Origin when present, avoiding go-get to\n // vanity hosts (https://github.com/renovatebot/renovate/discussions/44898)\n const sourceUrl =\n origin?.VCS === 'git' && isNonEmptyString(origin.URL)\n ? origin.URL.replace(regEx(/\\.git$/), '')\n : undefined;\n return { version, sourceUrl };\n } catch (err) {\n logger.trace({ err }, 'Failed to get latest version');\n return null;\n }\n }\n\n async getVersionsWithInfo(\n baseUrl: string,\n packageName: string,\n constraintsFiltering: ConstraintsFilter | undefined,\n ): Promise<ReleaseResult> {\n const isGopkgin = packageName.startsWith('gopkg.in/');\n const majorSuffixSeparator = isGopkgin ? '.' : '/';\n const modParts = packageName.match(modRegex)?.groups;\n const baseMod =\n modParts?.baseMod ??\n /* v8 ignore next -- defensive: modRegex matches any non-empty package name, so baseMod is always set */ packageName;\n const packageMajor = parseInt(modParts?.majorVersion ?? '0', 10);\n\n const result: ReleaseResult = { releases: [] };\n for (let major = packageMajor; ; major += 1) {\n let pkg = `${baseMod}${majorSuffixSeparator}v${major}`;\n if (!isGopkgin && major < 2) {\n pkg = baseMod;\n major += 1; // v0 and v1 are the same module\n }\n\n let releases: Release[] = [];\n\n try {\n const res = await this.listVersions(baseUrl, pkg);\n\n // Artifactory returns all versions in any major (past and future),\n // so starting from v2, we filter them in order to avoid the infinite loop\n const filteredReleases = res.filter(({ version }) => {\n if (major < 2) {\n return true;\n }\n\n return (\n version.split(regEx(/[^\\d]+/)).find(isTruthy) === major.toString()\n );\n });\n\n releases = await p.map(filteredReleases, async (versionInfo) => {\n const { version, newDigest, releaseTimestamp } = versionInfo;\n\n if (releaseTimestamp) {\n return { version, newDigest, releaseTimestamp };\n }\n\n try {\n return await this.versionInfo(baseUrl, pkg, version);\n } catch (err) {\n logger.trace({ err }, `Can't obtain data from ${baseUrl}`);\n return { version };\n }\n });\n\n if (constraintsFiltering === 'strict') {\n releases = await p.map(releases, async (rel) => {\n try {\n const goDirective = await this.retrieveGoDirectiveForModule(\n baseUrl,\n pkg,\n rel.version,\n );\n if (goDirective) {\n rel.constraints ??= {};\n rel.constraints['%goMod'] ??= [];\n rel.constraints['%goMod'].push(goDirective);\n }\n } catch (err) {\n logger.trace(\n { err },\n `Can't obtain \\`go\\` directive from ${baseUrl}`,\n );\n }\n\n return rel;\n });\n }\n\n result.releases.push(...releases);\n } catch (err) {\n const potentialHttpError =\n err instanceof ExternalHostError ? err.err : err;\n const status = potentialHttpError.response?.statusCode;\n if (\n potentialHttpError instanceof HttpError &&\n (status === 404 || status === 403) &&\n major !== packageMajor\n ) {\n break;\n }\n\n throw err;\n }\n\n const latest = await this.getLatestVersion(baseUrl, pkg);\n if (latest) {\n const { version: latestVersion, sourceUrl } = latest;\n result.tags ??= {};\n result.tags.latest ??= latestVersion;\n if (goVersioning.isGreaterThan(latestVersion, result.tags.latest)) {\n result.tags.latest = latestVersion;\n }\n if (sourceUrl) {\n result.sourceUrl ??= sourceUrl;\n }\n if (!result.releases.length) {\n const releaseFromLatest = pseudoVersionToRelease(latestVersion);\n if (releaseFromLatest) {\n result.releases.push(releaseFromLatest);\n }\n }\n }\n\n if (!releases.length) {\n break;\n }\n }\n\n return result;\n }\n\n static getCacheKey({\n packageName,\n constraintsFiltering,\n }: GetReleasesConfig): string {\n const goproxy = getEnv().GOPROXY;\n const noproxy = parseNoproxy();\n const constraintsFilteringKey =\n constraintsFiltering && constraintsFiltering !== 'none'\n ? `@@${constraintsFiltering}`\n : '';\n // TODO: types (#22198)\n return `${packageName}@@${goproxy}@@${noproxy?.toString()}${constraintsFilteringKey}`;\n }\n\n static getVersionedCacheKey(packageName: string, version: string): string {\n const goproxy = getEnv().GOPROXY;\n const noproxy = parseNoproxy();\n // TODO: types (#22198)\n return `${packageName}@@${version}@@${goproxy}@@${noproxy?.toString()}`;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAoCA,MAAM,iBAAiB,MAAM,iCAAiC;AAE9D,MAAM,WAAW,MAAM,iDAAiD;AAExE,MAAM,kBAAkB,MAAM,MAAM;;;;;;AAOpC,MAAM,0BAA0B,MAAM,iBAAiB;;;;AAKvD,MAAM,qBAAqB,MACzB,gFACF;AAEA,SAAgB,uBAAuB,eAAuC;CAC5E,MAAM,QAAQ,cAAc,MAAM,kBAAkB,CAAC,EAAE;CACvD,IAAI,CAAC,OACH,OAAO;CAGT,MAAM,EAAE,QAAQ,WAAW,cAAc;CAGzC,OAAO;EACL,SAAS;EACT;EACA,kBALuB,YAAY,SAKpB;CACjB;AACF;;;;;;;;AASA,SAAgB,aAAa,UAAkB,MAAgC;CAC7E,MAAM,YAAY,SACf,QAAQ,MAAM,SAAS,GAAG,EAAE,CAAC,CAC7B,MAAM,GAAG,CAAC,CACV,MAAM,CAAC;CACV,MAAM,WAAW,CAAC,GAAG,IAAI;CAEzB,OAAO,UAAU,QAAQ;EACvB,MAAM,SAAS,GAAG,UAAU,KAAK,GAAG,EAAE;EAOtC,IALe,SAAS,MACrB,QACC,IAAI,WAAW,MAAM,KACrB,gBAAgB,KAAK,IAAI,MAAM,OAAO,MAAM,CAAC,CAExC,GACP,OAAO;EAGT,UAAU,MAAM;CAClB;CAEA,OAAO;AACT;AAEA,IAAa,oBAAb,MAAa,0BAA0B,WAAW;CAChD,OAAgB,KAAK;CAErB,cAAc;EACZ,MAAM,kBAAkB,EAAE;CAC5B;CAEA,SAAkB,IAAI,mBAAmB;CAEzC,aAA8B,IAAI,WAAW,yBAAyB,EAAE;CAExE,MAAc,aACZ,QAC+B;EAC/B,MAAM,EAAE,gBAAgB;EACxB,OAAO,MAAM,uBAAuB,YAAY,EAAE;EAClD,MAAM,UAAU,OAAO,CAAC,CAAC,WAAW;EACpC,IAAI,YAAY,UACd,OAAO,KAAK,OAAO,YAAY,MAAM;EAEvC,MAAM,YAAY,aAAa,OAAO;EACtC,MAAM,UAAU,aAAa;EAE7B,IAAI,SAA+B;EACnC,IAAI,gBAAgB;EAEpB,IAAI,SAAS,KAAK,WAAW,GAAG;GAC9B,OAAO,MAAM,YAAY,YAAY,qBAAqB;GAC1D,SAAS,MAAM,KAAK,OAAO,YAAY,MAAM;GAC7C,OAAO;EACT;EAEA,KAAK,MAAM,EAAE,KAAK,cAAc,WAC9B,IAAI;GACF,IAAI,QAAQ,OACV;QACK,IAAI,QAAQ,UAAU;IAC3B,SAAS,MAAM,KAAK,OAAO,YAAY,MAAM;IAC7C;GACF;GAEA,MAAM,MAAM,MAAM,KAAK,oBACrB,KACA,aACA,OAAO,oBACT;GACA,IAAI,IAAI,SAAS,QAAQ;IACvB,SAAS;IACT,gBAAgB;IAChB;GACF;EACF,SAAS,KAAK;GAGZ,MAAM,cADJ,eAAe,oBAAoB,IAAI,MAAM,IAAA,EACR,UAAU;GAGjD,IAAI,EADF,aAAa,MAAM,OAAO,eAAe,OAAO,eAAe,MAC/C;IAChB,OAAO,MACL,EAAE,IAAI,GACN,iFACF;IACA,KAAK,oBAAoB,GAAG;GAC9B;GACA,OAAO,MACL,EAAE,IAAI,GACN,sDACF;EACF;EAGF,IAAI,UAAU,CAAC,OAAO,WACpB,IAAI;GACF,MAAM,aAAa,MAAM,iBAAiB,cAAc,WAAW;GACnE,MAAM,YAAY,aAAa,UAAU;GACzC,IAAI,WACF,OAAO,YAAY;EAEvB,SAAS,KAAK;GACZ,OAAO,MAAM,EAAE,IAAI,GAAG,4BAA4B,aAAa;EACjE;EAGF,IAAI,QAAQ,aAAa,eACvB,MAAM,KAAK,2BACT,aACA,OAAO,WACP,OAAO,QACT;EAGF,OAAO;CACT;;;;;;CAOA,MAAM,2BACJ,aACA,WACA,UACe;EACf,IAAI,eAAe,SAAS,MAAM,UAChC;EAGF,MAAM,YAAY,SAAS,SAAS;;EAEpC,IAAI,CAAC,WACH;EAGF,MAAM,aAAa,kBACjB,iBAAiB,UAAU,QAAQ,CACrC,CAAC,CAAC,QAAQ,MAAM,QAAQ,GAAG,EAAE;EAE7B,IAAI;GACF,MAAM,iBAAiB,MAAM,cAC3B;IACE,aAAa;IACb,aAAa,UAAU;GACzB,GACA,KAAK,UACP;GAEA,MAAM,6BAAa,IAAI,IAAuB;GAC9C,KAAK,MAAM,EAAE,SAAS,sBAAsB,gBAC1C,WAAW,IAAI,SAAS,gBAAgB;GAG1C,MAAM,YAAY,aAAa,aAAa,WAAW,KAAK,CAAC;GAC7D,KAAK,MAAM,WAAW,UAAU;IAC9B,MAAM,UAAU,QAAQ,QAAQ,QAAQ,yBAAyB,EAAE;IACnE,MAAM,mBAAmB,WAAW,IAAI,GAAG,YAAY,SAAS;IAChE,IACE,qBACC,CAAC,QAAQ,oBACR,mBAAmB,QAAQ,mBAE7B,QAAQ,mBAAmB;GAE/B;EACF,SAAS,KAAK;GACZ,OAAO,MACL;IAAE;IAAK;GAAY,GACnB,8CACF;EACF;CACF;CAEA,YAAY,QAA0D;EACpE,OAAO,UACL;GACE,WAAW,cAAc,kBAAkB;GAC3C,KAAK,kBAAkB,YAAY,MAAM;GACzC,UAAU;EACZ,SACM,KAAK,aAAa,MAAM,CAChC;CACF;;;;;;CAOA,WAAW,OAAuB;EAChC,OAAO,MAAM,QAAQ,MAAM,UAAU,IAAI,MAAM,IAAI,EAAE,YAAY,GAAG;CACtE;CAEA,MAAM,aAAa,SAAiB,aAAyC;EAC3E,MAAM,MAAM,aACV,SACA,KAAK,WAAW,WAAW,GAC3B,MACA,MACF;EACA,MAAM,EAAE,SAAS,MAAM,KAAK,KAAK,QAAQ,GAAG;EAC5C,OAAO,UAAU,KAAK,MAAM,YAAY,IAAI,QAAQ;GAClD,IAAI,CAAC,iCAAiC,GAAG,GACvC,OAAO;GAGT,MAAM,CAAC,SAAS,aAAa,IAAI,KAAK,CAAC,CAAC,MAAM,MAAM,KAAK,CAAC;GAC1D,MAAM,UAAmB,uBAAuB,OAAO,KAAK,EAAE,QAAQ;GAEtE,MAAM,mBAAmB,YAAY,SAAS;GAC9C,IAAI,kBACF,QAAQ,mBAAmB;GAG7B,OAAO;EACT,CAAC;CACH;CAEA,MAAM,YACJ,SACA,aACA,SACkB;EAClB,MAAM,MAAM,aACV,SACA,KAAK,WAAW,WAAW,GAC3B,MACA,GAAG,QAAQ,MACb;EACA,MAAM,MAAM,MAAM,KAAK,KAAK,QAAQ,KAAK,WAAW;EAEpD,MAAM,SAAkB,EACtB,SAAS,IAAI,KAAK,QACpB;EAEA,MAAM,mBAAmB,YAAY,IAAI,KAAK,IAAI;EAClD,IAAI,kBACF,OAAO,mBAAmB;EAG5B,OAAO;CACT;;;;;;CAOA,MAAM,6BACJ,SACA,aACA,SAC6B;EAC7B,OAAO,UACL;GACE,WAAW,cAAc,kBAAkB;GAC3C,KAAK,kBAAkB,qBAAqB,aAAa,OAAO;GAEhE,YAAY;EACd,SACM,KAAK,8BAA8B,SAAS,aAAa,OAAO,CACxE;CACF;CAEA,MAAM,8BACJ,SACA,aACA,SAC6B;EAC7B,MAAM,MAAM,aACV,SACA,KAAK,WAAW,WAAW,GAC3B,MACA,GAAG,QAAQ,KACb;EACA,MAAM,MAAM,MAAM,KAAK,KAAK,QAAQ,GAAG;EAEvC,IAAI,cAAkC,KAAA;EAEtC,KAAK,MAAM,QAAQ,IAAI,KAAK,MAAM,IAAI,GAAG;GACvC,MAAM,mBAAmB,eAAe,KAAK,IAAI,CAAC,EAAE;GACpD,IAAI,kBAAkB;IACpB,cAAc,iBAAiB;IAC/B;GACF;EACF;EAEA,IAAI,CAAC,aACH,OAAO;EAIT,MAAM,QAAQ,YAAY,MAAM,GAAG;EACnC,IAAI,MAAM,WAAW,GACnB,OAAO,GAAG,MAAM,GAAG;EAErB,IAAI,MAAM,WAAW,GACnB,OAAO,GAAG,MAAM,GAAG,GAAG,MAAM,GAAG;EAEjC,OAAO,GAAG,MAAM,GAAG,GAAG,MAAM,GAAG,GAAG,MAAM;CAC1C;CAEA,MAAM,iBACJ,SACA,aACyD;EACzD,IAAI;GACF,MAAM,MAAM,aACV,SACA,KAAK,WAAW,WAAW,GAC3B,SACF;GAEA,MAAM,EAAE,SAAS,SAAS,QAAQ,YAAW,MAD3B,KAAK,KAAK,QAAQ,KAAK,WAAW,EAAA,CACH;GAOjD,OAAO;IAAE;IAAS,WAHhB,QAAQ,QAAQ,SAAS,iBAAiB,OAAO,GAAG,IAChD,OAAO,IAAI,QAAQ,MAAM,QAAQ,GAAG,EAAE,IACtC,KAAA;GACsB;EAC9B,SAAS,KAAK;GACZ,OAAO,MAAM,EAAE,IAAI,GAAG,8BAA8B;GACpD,OAAO;EACT;CACF;CAEA,MAAM,oBACJ,SACA,aACA,sBACwB;EACxB,MAAM,YAAY,YAAY,WAAW,WAAW;EACpD,MAAM,uBAAuB,YAAY,MAAM;EAC/C,MAAM,WAAW,YAAY,MAAM,QAAQ,CAAC,EAAE;EAC9C,MAAM,UACJ,UAAU;2GAC+F;EAC3G,MAAM,eAAe,SAAS,UAAU,gBAAgB,KAAK,EAAE;EAE/D,MAAM,SAAwB,EAAE,UAAU,CAAC,EAAE;EAC7C,KAAK,IAAI,QAAQ,eAAgB,SAAS,GAAG;GAC3C,IAAI,MAAM,GAAG,UAAU,qBAAqB,GAAG;GAC/C,IAAI,CAAC,aAAa,QAAQ,GAAG;IAC3B,MAAM;IACN,SAAS;GACX;GAEA,IAAI,WAAsB,CAAC;GAE3B,IAAI;IAKF,MAAM,oBAAmB,MAJP,KAAK,aAAa,SAAS,GAAG,EAAA,CAInB,QAAQ,EAAE,cAAc;KACnD,IAAI,QAAQ,GACV,OAAO;KAGT,OACE,QAAQ,MAAM,MAAM,QAAQ,CAAC,CAAC,CAAC,KAAK,QAAQ,MAAM,MAAM,SAAS;IAErE,CAAC;IAED,WAAW,MAAMA,IAAM,kBAAkB,OAAO,gBAAgB;KAC9D,MAAM,EAAE,SAAS,WAAW,qBAAqB;KAEjD,IAAI,kBACF,OAAO;MAAE;MAAS;MAAW;KAAiB;KAGhD,IAAI;MACF,OAAO,MAAM,KAAK,YAAY,SAAS,KAAK,OAAO;KACrD,SAAS,KAAK;MACZ,OAAO,MAAM,EAAE,IAAI,GAAG,0BAA0B,SAAS;MACzD,OAAO,EAAE,QAAQ;KACnB;IACF,CAAC;IAED,IAAI,yBAAyB,UAC3B,WAAW,MAAMA,IAAM,UAAU,OAAO,QAAQ;KAC9C,IAAI;MACF,MAAM,cAAc,MAAM,KAAK,6BAC7B,SACA,KACA,IAAI,OACN;MACA,IAAI,aAAa;OACf,IAAI,gBAAgB,CAAC;OACrB,IAAI,YAAY,cAAc,CAAC;OAC/B,IAAI,YAAY,SAAS,CAAC,KAAK,WAAW;MAC5C;KACF,SAAS,KAAK;MACZ,OAAO,MACL,EAAE,IAAI,GACN,sCAAsC,SACxC;KACF;KAEA,OAAO;IACT,CAAC;IAGH,OAAO,SAAS,KAAK,GAAG,QAAQ;GAClC,SAAS,KAAK;IACZ,MAAM,qBACJ,eAAe,oBAAoB,IAAI,MAAM;IAC/C,MAAM,SAAS,mBAAmB,UAAU;IAC5C,IACE,8BAA8BC,iBAC7B,WAAW,OAAO,WAAW,QAC9B,UAAU,cAEV;IAGF,MAAM;GACR;GAEA,MAAM,SAAS,MAAM,KAAK,iBAAiB,SAAS,GAAG;GACvD,IAAI,QAAQ;IACV,MAAM,EAAE,SAAS,eAAe,cAAc;IAC9C,OAAO,SAAS,CAAC;IACjB,OAAO,KAAK,WAAW;IACvB,IAAIC,IAAa,cAAc,eAAe,OAAO,KAAK,MAAM,GAC9D,OAAO,KAAK,SAAS;IAEvB,IAAI,WACF,OAAO,cAAc;IAEvB,IAAI,CAAC,OAAO,SAAS,QAAQ;KAC3B,MAAM,oBAAoB,uBAAuB,aAAa;KAC9D,IAAI,mBACF,OAAO,SAAS,KAAK,iBAAiB;IAE1C;GACF;GAEA,IAAI,CAAC,SAAS,QACZ;EAEJ;EAEA,OAAO;CACT;CAEA,OAAO,YAAY,EACjB,aACA,wBAC4B;EAC5B,MAAM,UAAU,OAAO,CAAC,CAAC;EACzB,MAAM,UAAU,aAAa;EAC7B,MAAM,0BACJ,wBAAwB,yBAAyB,SAC7C,KAAK,yBACL;EAEN,OAAO,GAAG,YAAY,IAAI,QAAQ,IAAI,SAAS,SAAS,IAAI;CAC9D;CAEA,OAAO,qBAAqB,aAAqB,SAAyB;EAIxE,OAAO,GAAG,YAAY,IAAI,QAAQ,IAHlB,OAAO,CAAC,CAAC,QAGqB,IAF9B,aAEwC,CAAC,EAAE,SAAS;CACtE;AACF"}
@@ -49,6 +49,14 @@ function incCountValue(key, incBy = 1) {
49
49
  const count = getCount(key);
50
50
  counts.set(key, count + incBy);
51
51
  }
52
+ /**
53
+ * Vulnerability alerts are counted separately from other updates, so that a
54
+ * limit set under `vulnerabilityAlerts` is a budget of its own rather than a
55
+ * share of the repository-wide one.
56
+ */
57
+ function vulnerabilityCountName(key) {
58
+ return key === "Branches" ? "VulnerabilityBranches" : "VulnerabilityConcurrentPRs";
59
+ }
52
60
  function handleConcurrentLimits(key, config) {
53
61
  if (key === "HourlyCommits") {
54
62
  const hourlyCommitLimit = calcLimit(config.upgrades, "commitHourlyLimit");
@@ -62,23 +70,26 @@ function handleConcurrentLimits(key, config) {
62
70
  }
63
71
  return false;
64
72
  }
65
- const hourlyPrLimit = calcLimit(config.upgrades, "prHourlyLimit");
66
- const hourlyPrCount = getCount("HourlyPRs");
67
- if (hourlyPrLimit && hourlyPrCount >= hourlyPrLimit) {
68
- logger.debug({
69
- hourlyPrCount,
70
- hourlyPrLimit
71
- }, "Hourly PRs limit reached");
72
- return true;
73
+ if (!config.isVulnerabilityAlert) {
74
+ const hourlyPrLimit = calcLimit(config.upgrades, "prHourlyLimit");
75
+ const hourlyPrCount = getCount("HourlyPRs");
76
+ if (hourlyPrLimit && hourlyPrCount >= hourlyPrLimit) {
77
+ logger.debug({
78
+ hourlyPrCount,
79
+ hourlyPrLimit
80
+ }, "Hourly PRs limit reached");
81
+ return true;
82
+ }
73
83
  }
74
84
  const limitKey = key === "Branches" ? "branchConcurrentLimit" : "prConcurrentLimit";
75
85
  const limitValue = calcLimit(config.upgrades, limitKey);
76
- const currentCount = getCount(key);
86
+ const countName = config.isVulnerabilityAlert ? vulnerabilityCountName(key) : key;
87
+ const currentCount = getCount(countName);
77
88
  if (limitValue && currentCount >= limitValue) {
78
89
  logger.debug({
79
90
  limitKey,
80
91
  currentCount
81
- }, `${key} limit reached`);
92
+ }, `${countName} limit reached`);
82
93
  return true;
83
94
  }
84
95
  return false;
@@ -1 +1 @@
1
- {"version":3,"file":"limits.js","names":[],"sources":["../../../lib/workers/global/limits.ts"],"sourcesContent":["import { isInteger, isNumber, isUndefined } from '@sindresorhus/is';\nimport { logger } from '../../logger/index.ts';\nimport type { BranchConfig, BranchUpgradeConfig } from '../types.ts';\n\nexport type Limit = 'Commits';\ninterface LimitValue {\n max: number | null;\n current: number;\n}\n\nconst limits = new Map<Limit, LimitValue>();\n\nexport function resetAllLimits(): void {\n limits.clear();\n}\n\nexport function setMaxLimit(key: Limit, val: unknown): void {\n const max = typeof val === 'number' ? Math.max(0, val) : null;\n limits.set(key, { current: 0, max });\n logger.debug(`${key} limit = ${max!}`);\n}\n\nexport function incLimitedValue(key: Limit, incBy = 1): void {\n const limit = limits.get(key) ?? { max: null, current: 0 };\n limits.set(key, {\n ...limit,\n current: limit.current + incBy,\n });\n}\n\nfunction handleCommitsLimit(): boolean {\n const limit = limits.get('Commits');\n // TODO: fix me?\n // oxlint-disable-next-line typescript/prefer-optional-chain\n if (!limit || limit.max === null) {\n return false;\n }\n const { max, current } = limit;\n const res = max - current <= 0;\n if (res) {\n logger.debug({ current, max }, 'Commits limit reached');\n }\n return res;\n}\n\nexport type CountName =\n | 'ConcurrentPRs'\n | 'HourlyPRs'\n | 'Branches'\n | 'HourlyCommits';\n\ntype BranchLimitName =\n | 'branchConcurrentLimit'\n | 'prConcurrentLimit'\n | 'commitHourlyLimit'\n | 'prHourlyLimit';\n\nexport const counts = new Map<CountName, number>();\n\nexport function getCount(key: CountName): number {\n const count = counts.get(key);\n // istanbul ignore if: should not happen\n if (!isInteger(count)) {\n logger.debug(`Could not compute the count of ${key}, returning zero.`);\n return 0;\n }\n return count;\n}\n\nexport function setCount(key: CountName, val: number): void {\n counts.set(key, val);\n logger.debug(`${key} count = ${val}`);\n}\n\nexport function incCountValue(key: CountName, incBy = 1): void {\n const count = getCount(key);\n counts.set(key, count + incBy);\n}\n\nfunction handleConcurrentLimits(\n key: Exclude<CountName, 'HourlyPRs'>,\n config: BranchConfig,\n): boolean {\n // Only check hourly commit limit when specifically checking HourlyCommits\n if (key === 'HourlyCommits') {\n // calculate the remaining hourly commit limit\n const hourlyCommitLimit = calcLimit(config.upgrades, 'commitHourlyLimit');\n const hourlyCommitCount = getCount('HourlyCommits');\n\n // if a limit is defined ( >0 ) and limit reached return true\n if (hourlyCommitLimit && hourlyCommitCount >= hourlyCommitLimit) {\n logger.debug(\n { hourlyCommitCount, hourlyCommitLimit },\n 'Hourly commits limit reached',\n );\n return true;\n }\n\n return false;\n }\n\n // calculate the remaining hourly PR limit\n const hourlyPrLimit = calcLimit(config.upgrades, 'prHourlyLimit');\n const hourlyPrCount = getCount('HourlyPRs');\n\n // if a limit is defined ( >0 ) and limit reached return true ie. limit has been reached\n if (hourlyPrLimit && hourlyPrCount >= hourlyPrLimit) {\n logger.debug({ hourlyPrCount, hourlyPrLimit }, 'Hourly PRs limit reached');\n return true;\n }\n\n // calculate the branch or PR concurrent limit\n const limitKey =\n key === 'Branches' ? 'branchConcurrentLimit' : 'prConcurrentLimit';\n const limitValue = calcLimit(config.upgrades, limitKey);\n const currentCount = getCount(key);\n\n if (limitValue && currentCount >= limitValue) {\n logger.debug({ limitKey, currentCount }, `${key} limit reached`);\n return true;\n }\n\n return false;\n}\n\nexport function calcLimit(\n upgrades: BranchUpgradeConfig[],\n limitName: BranchLimitName,\n): number {\n const uniqueUpgrades = new Map(upgrades.map((u) => [u.depName, u]));\n logger.debug(\n {\n limits: Array.from(uniqueUpgrades.values()).map((upg) => {\n return { depName: upg.depName, [limitName]: upg[limitName] };\n }),\n },\n `${limitName} of the upgrades present in this branch`,\n );\n\n if (hasMultipleLimits(upgrades, limitName)) {\n logger.once.debug(\n `Branch has multiple ${limitName} limits. The lowest among these will be selected.`,\n );\n }\n\n let lowestLimit = Number.MAX_SAFE_INTEGER;\n for (const upgrade of upgrades) {\n let limit = upgrade[limitName];\n\n // inherit prConcurrentLimit value incase branchConcurrentLimit is null\n if (!isNumber(limit) && limitName === 'branchConcurrentLimit') {\n limit = upgrade.prConcurrentLimit;\n }\n\n // istanbul ignore if: should never happen as all limits get a default value\n if (isUndefined(limit)) {\n limit = Number.MAX_SAFE_INTEGER;\n }\n\n // no limit\n if (limit === 0 || limit === null) {\n logger.debug(\n `${limitName} of this branch is unlimited, because at least one of the upgrade has it's ${limitName} set to \"No limit\" ie. 0 or null`,\n );\n return 0;\n }\n\n // limit is set\n lowestLimit = limit < lowestLimit ? limit : lowestLimit;\n }\n\n logger.debug(\n `Calculated lowest ${limitName} among the upgrades present in this branch is ${lowestLimit}.`,\n );\n return lowestLimit;\n}\n\nexport function hasMultipleLimits(\n upgrades: BranchUpgradeConfig[],\n limitName: BranchLimitName,\n): boolean {\n if (upgrades.length === 1) {\n return false;\n }\n\n const distinctLimits = new Set<number>();\n for (const upgrade of upgrades) {\n let limitValue = upgrade[limitName];\n\n // inherit prConcurrentLimit value incase branchConcurrentLimit is null\n if (limitName === 'branchConcurrentLimit' && !isNumber(limitValue)) {\n limitValue = upgrade.prConcurrentLimit;\n }\n\n // istanbul ignore if: should not happen as the limits are of type number\n if (limitValue === null) {\n limitValue = 0;\n }\n\n if (!isUndefined(limitValue) && !distinctLimits.has(limitValue)) {\n distinctLimits.add(limitValue);\n }\n }\n\n return distinctLimits.size > 1;\n}\n\nexport function isLimitReached(limit: 'Commits'): boolean;\nexport function isLimitReached(\n limit: 'Branches' | 'ConcurrentPRs' | 'HourlyCommits',\n config: BranchConfig,\n): boolean;\nexport function isLimitReached(\n limit: 'Commits' | 'Branches' | 'ConcurrentPRs' | 'HourlyCommits',\n config?: BranchConfig,\n): boolean {\n if (limit === 'Commits') {\n return handleCommitsLimit();\n }\n\n if (config) {\n return handleConcurrentLimits(limit, config);\n }\n\n // istanbul ignore next: should not happen\n throw new Error(\n 'Config is required for computing limits for Branches and PullRequests',\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"limits.js","names":[],"sources":["../../../lib/workers/global/limits.ts"],"sourcesContent":["import { isInteger, isNumber, isUndefined } from '@sindresorhus/is';\nimport { logger } from '../../logger/index.ts';\nimport type { BranchConfig, BranchUpgradeConfig } from '../types.ts';\n\nexport type Limit = 'Commits';\ninterface LimitValue {\n max: number | null;\n current: number;\n}\n\nconst limits = new Map<Limit, LimitValue>();\n\nexport function resetAllLimits(): void {\n limits.clear();\n counts.clear();\n}\n\nexport function setMaxLimit(key: Limit, val: unknown): void {\n const max = typeof val === 'number' ? Math.max(0, val) : null;\n limits.set(key, { current: 0, max });\n logger.debug(`${key} limit = ${max!}`);\n}\n\nexport function incLimitedValue(key: Limit, incBy = 1): void {\n const limit = limits.get(key) ?? { max: null, current: 0 };\n limits.set(key, {\n ...limit,\n current: limit.current + incBy,\n });\n}\n\nfunction handleCommitsLimit(): boolean {\n const limit = limits.get('Commits');\n // TODO: fix me?\n // oxlint-disable-next-line typescript/prefer-optional-chain\n if (!limit || limit.max === null) {\n return false;\n }\n const { max, current } = limit;\n const res = max - current <= 0;\n if (res) {\n logger.debug({ current, max }, 'Commits limit reached');\n }\n return res;\n}\n\nexport type CountName =\n | 'ConcurrentPRs'\n | 'HourlyPRs'\n | 'Branches'\n | 'HourlyCommits'\n | 'VulnerabilityConcurrentPRs'\n | 'VulnerabilityBranches';\n\n/**\n * The limits `isLimitReached()` evaluates against a single branch. `Commits` is\n * excluded because it is global and takes no config, `HourlyPRs` because it is\n * only ever read while checking one of these, and the `Vulnerability*` counts\n * because they are picked internally rather than passed in.\n */\ntype BranchLimitCountName = 'ConcurrentPRs' | 'Branches' | 'HourlyCommits';\n\ntype BranchLimitName =\n | 'branchConcurrentLimit'\n | 'prConcurrentLimit'\n | 'commitHourlyLimit'\n | 'prHourlyLimit';\n\nexport const counts = new Map<CountName, number>();\n\nexport function getCount(key: CountName): number {\n const count = counts.get(key);\n // istanbul ignore if: should not happen\n if (!isInteger(count)) {\n logger.debug(`Could not compute the count of ${key}, returning zero.`);\n return 0;\n }\n return count;\n}\n\nexport function setCount(key: CountName, val: number): void {\n counts.set(key, val);\n logger.debug(`${key} count = ${val}`);\n}\n\nexport function incCountValue(key: CountName, incBy = 1): void {\n const count = getCount(key);\n counts.set(key, count + incBy);\n}\n\n/**\n * Vulnerability alerts are counted separately from other updates, so that a\n * limit set under `vulnerabilityAlerts` is a budget of its own rather than a\n * share of the repository-wide one.\n */\nfunction vulnerabilityCountName(key: 'ConcurrentPRs' | 'Branches'): CountName {\n return key === 'Branches'\n ? 'VulnerabilityBranches'\n : 'VulnerabilityConcurrentPRs';\n}\n\nfunction handleConcurrentLimits(\n key: BranchLimitCountName,\n config: BranchConfig,\n): boolean {\n // Only check hourly commit limit when specifically checking HourlyCommits\n if (key === 'HourlyCommits') {\n // calculate the remaining hourly commit limit\n const hourlyCommitLimit = calcLimit(config.upgrades, 'commitHourlyLimit');\n const hourlyCommitCount = getCount('HourlyCommits');\n\n // if a limit is defined ( >0 ) and limit reached return true\n if (hourlyCommitLimit && hourlyCommitCount >= hourlyCommitLimit) {\n logger.debug(\n { hourlyCommitCount, hourlyCommitLimit },\n 'Hourly commits limit reached',\n );\n return true;\n }\n\n return false;\n }\n\n // vulnerability alerts have no hourly limit of their own, so they keep skipping this one\n if (!config.isVulnerabilityAlert) {\n // calculate the remaining hourly PR limit\n const hourlyPrLimit = calcLimit(config.upgrades, 'prHourlyLimit');\n const hourlyPrCount = getCount('HourlyPRs');\n\n // if a limit is defined ( >0 ) and limit reached return true ie. limit has been reached\n if (hourlyPrLimit && hourlyPrCount >= hourlyPrLimit) {\n logger.debug(\n { hourlyPrCount, hourlyPrLimit },\n 'Hourly PRs limit reached',\n );\n return true;\n }\n }\n\n // calculate the branch or PR concurrent limit\n const limitKey =\n key === 'Branches' ? 'branchConcurrentLimit' : 'prConcurrentLimit';\n const limitValue = calcLimit(config.upgrades, limitKey);\n const countName = config.isVulnerabilityAlert\n ? vulnerabilityCountName(key)\n : key;\n const currentCount = getCount(countName);\n\n if (limitValue && currentCount >= limitValue) {\n logger.debug({ limitKey, currentCount }, `${countName} limit reached`);\n return true;\n }\n\n return false;\n}\n\nexport function calcLimit(\n upgrades: BranchUpgradeConfig[],\n limitName: BranchLimitName,\n): number {\n const uniqueUpgrades = new Map(upgrades.map((u) => [u.depName, u]));\n logger.debug(\n {\n limits: Array.from(uniqueUpgrades.values()).map((upg) => {\n return { depName: upg.depName, [limitName]: upg[limitName] };\n }),\n },\n `${limitName} of the upgrades present in this branch`,\n );\n\n if (hasMultipleLimits(upgrades, limitName)) {\n logger.once.debug(\n `Branch has multiple ${limitName} limits. The lowest among these will be selected.`,\n );\n }\n\n let lowestLimit = Number.MAX_SAFE_INTEGER;\n for (const upgrade of upgrades) {\n let limit = upgrade[limitName];\n\n // inherit prConcurrentLimit value incase branchConcurrentLimit is null\n if (!isNumber(limit) && limitName === 'branchConcurrentLimit') {\n limit = upgrade.prConcurrentLimit;\n }\n\n // istanbul ignore if: should never happen as all limits get a default value\n if (isUndefined(limit)) {\n limit = Number.MAX_SAFE_INTEGER;\n }\n\n // no limit\n if (limit === 0 || limit === null) {\n logger.debug(\n `${limitName} of this branch is unlimited, because at least one of the upgrade has it's ${limitName} set to \"No limit\" ie. 0 or null`,\n );\n return 0;\n }\n\n // limit is set\n lowestLimit = limit < lowestLimit ? limit : lowestLimit;\n }\n\n logger.debug(\n `Calculated lowest ${limitName} among the upgrades present in this branch is ${lowestLimit}.`,\n );\n return lowestLimit;\n}\n\nexport function hasMultipleLimits(\n upgrades: BranchUpgradeConfig[],\n limitName: BranchLimitName,\n): boolean {\n if (upgrades.length === 1) {\n return false;\n }\n\n const distinctLimits = new Set<number>();\n for (const upgrade of upgrades) {\n let limitValue = upgrade[limitName];\n\n // inherit prConcurrentLimit value incase branchConcurrentLimit is null\n if (limitName === 'branchConcurrentLimit' && !isNumber(limitValue)) {\n limitValue = upgrade.prConcurrentLimit;\n }\n\n // istanbul ignore if: should not happen as the limits are of type number\n if (limitValue === null) {\n limitValue = 0;\n }\n\n if (!isUndefined(limitValue) && !distinctLimits.has(limitValue)) {\n distinctLimits.add(limitValue);\n }\n }\n\n return distinctLimits.size > 1;\n}\n\nexport function isLimitReached(limit: 'Commits'): boolean;\nexport function isLimitReached(\n limit: BranchLimitCountName,\n config: BranchConfig,\n): boolean;\nexport function isLimitReached(\n limit: 'Commits' | BranchLimitCountName,\n config?: BranchConfig,\n): boolean {\n if (limit === 'Commits') {\n return handleCommitsLimit();\n }\n\n if (config) {\n return handleConcurrentLimits(limit, config);\n }\n\n // istanbul ignore next: should not happen\n throw new Error(\n 'Config is required for computing limits for Branches and PullRequests',\n );\n}\n"],"mappings":"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"}
@@ -79,10 +79,12 @@ async function syncBranchState(branchName, baseBranch) {
79
79
  async function writeUpdates(config, allBranches) {
80
80
  const branches = allBranches;
81
81
  logger.debug(`Processing ${branches.length} branch${branches.length === 1 ? "" : "es"}: ${branches.map((b) => b.branchName).sort().join(", ")}`);
82
- const concurrentPrsCount = await getConcurrentPrsCount(config, branches);
83
- setCount("ConcurrentPRs", concurrentPrsCount);
84
- const concurrentBranchesCount = await getConcurrentBranchesCount(branches);
85
- setCount("Branches", concurrentBranchesCount);
82
+ const vulnerabilityBranches = branches.filter((b) => b.isVulnerabilityAlert);
83
+ const otherBranches = branches.filter((b) => !b.isVulnerabilityAlert);
84
+ setCount("ConcurrentPRs", await getConcurrentPrsCount(config, otherBranches));
85
+ setCount("VulnerabilityConcurrentPRs", await getConcurrentPrsCount(config, vulnerabilityBranches));
86
+ setCount("Branches", await getConcurrentBranchesCount(otherBranches));
87
+ setCount("VulnerabilityBranches", await getConcurrentBranchesCount(vulnerabilityBranches));
86
88
  const prsThisHourCount = await getPrHourlyCount(config);
87
89
  setCount("HourlyPRs", prsThisHourCount);
88
90
  const commitsThisHourCount = await getCommitsHourlyCount(branches);
@@ -111,7 +113,7 @@ async function writeUpdates(config, allBranches) {
111
113
  setBranchNewCommit(branchName, baseBranch, res.commitSha, commitDate);
112
114
  }
113
115
  if (branch.result === "automerged" && branch.automergeType !== "pr-comment") return "automerged";
114
- if (!branchExisted && await scm.branchExists(branch.branchName)) incCountValue("Branches");
116
+ if (!branchExisted && await scm.branchExists(branch.branchName)) incCountValue(branch.isVulnerabilityAlert ? "VulnerabilityBranches" : "Branches");
115
117
  }, { attributes: {
116
118
  [ATTR_VCS_REF_TYPE]: "branch",
117
119
  [ATTR_VCS_REF_BASE_TYPE]: "branch",
@@ -1 +1 @@
1
- {"version":3,"file":"write.js","names":[],"sources":["../../../../lib/workers/repository/process/write.ts"],"sourcesContent":["import {\n ATTR_VCS_REF_BASE_TYPE,\n ATTR_VCS_REF_HEAD_NAME,\n ATTR_VCS_REF_TYPE,\n} from '@opentelemetry/semantic-conventions/incubating';\nimport { isString } from '@sindresorhus/is';\nimport type { RenovateConfig } from '../../../config/types.ts';\nimport { instrument } from '../../../instrumentation/index.ts';\nimport { addMeta, logger, removeMeta } from '../../../logger/index.ts';\nimport { hashMap } from '../../../modules/manager/index.ts';\nimport { scm } from '../../../modules/platform/scm.ts';\nimport { getCache } from '../../../util/cache/repository/index.ts';\nimport type { BranchCache } from '../../../util/cache/repository/types.ts';\nimport { fingerprint } from '../../../util/fingerprint.ts';\nimport { setBranchNewCommit } from '../../../util/git/set-branch-commit.ts';\nimport { incCountValue, setCount } from '../../global/limits.ts';\nimport type {\n BranchConfig,\n CacheFingerprintMatchResult,\n UpgradeFingerprintConfig,\n} from '../../types.ts';\nimport { processBranch } from '../update/branch/index.ts';\nimport { upgradeFingerprintFields } from './fingerprint-fields.ts';\nimport {\n getCommitsHourlyCount,\n getConcurrentBranchesCount,\n getConcurrentPrsCount,\n getPrHourlyCount,\n} from './limits.ts';\n\nexport type WriteUpdateResult = 'done' | 'automerged';\n\nexport function generateCommitFingerprintConfig(\n branch: BranchConfig,\n): UpgradeFingerprintConfig[] {\n const res = branch.upgrades.map((upgrade) => {\n const filteredUpgrade = {} as UpgradeFingerprintConfig;\n for (const field of upgradeFingerprintFields) {\n // TS cannot narrow the type here\n filteredUpgrade[field] = upgrade[field]!;\n }\n return filteredUpgrade;\n });\n\n return res;\n}\n\nexport function compareCacheFingerprint(\n branchState: BranchCache,\n commitFingerprint: string,\n): CacheFingerprintMatchResult {\n if (!branchState.commitFingerprint) {\n logger.trace('branch.isUpToDate(): no fingerprint');\n return 'no-fingerprint';\n }\n\n if (commitFingerprint !== branchState.commitFingerprint) {\n logger.debug('branch.isUpToDate(): needs recalculation');\n return 'no-match';\n }\n\n logger.debug('branch.isUpToDate(): using cached result \"true\"');\n return 'matched';\n}\n\nexport async function syncBranchState(\n branchName: string,\n baseBranch: string,\n): Promise<BranchCache> {\n logger.debug('syncBranchState()');\n const branchSha = await scm.getBranchCommit(branchName);\n const baseBranchSha = await scm.getBranchCommit(baseBranch);\n\n const cache = getCache();\n cache.branches ??= [];\n const { branches: cachedBranches } = cache;\n let branchState = cachedBranches.find((br) => br.branchName === branchName);\n if (!branchState) {\n logger.debug(\n 'syncBranchState(): Branch cache not found, creating minimal branchState',\n );\n // create a minimal branch state\n branchState = {\n branchName,\n sha: branchSha,\n baseBranch,\n baseBranchSha,\n } as BranchCache;\n cachedBranches.push(branchState);\n }\n\n // if base branch name has changed invalidate cached isModified state\n if (baseBranch !== branchState.baseBranch) {\n logger.debug('syncBranchState(): update baseBranch name');\n branchState.baseBranch = baseBranch;\n delete branchState.isModified;\n branchState.pristine = false;\n }\n\n // if base branch sha has changed invalidate cached isBehindBase state\n if (baseBranchSha !== branchState.baseBranchSha) {\n logger.debug('syncBranchState(): update baseBranchSha');\n delete branchState.isBehindBase;\n delete branchState.isConflicted;\n\n // update cached branchSha\n branchState.baseBranchSha = baseBranchSha;\n branchState.pristine = false;\n }\n\n // if branch sha has changed invalidate all cached states\n if (branchSha !== branchState.sha) {\n logger.debug('syncBranchState(): update branchSha');\n delete branchState.isBehindBase;\n delete branchState.isConflicted;\n delete branchState.isModified;\n delete branchState.commitFingerprint;\n\n // Update commit timestamp when SHA changes\n const commitDate = await scm.getBranchUpdateDate(branchName);\n if (commitDate) {\n branchState.commitTimestamp = commitDate.toISO()!;\n }\n\n // update cached branchSha\n branchState.sha = branchSha;\n branchState.pristine = false;\n }\n\n return branchState;\n}\n\nexport async function writeUpdates(\n config: RenovateConfig,\n allBranches: BranchConfig[],\n): Promise<WriteUpdateResult> {\n const branches = allBranches;\n logger.debug(\n `Processing ${branches.length} branch${\n branches.length === 1 ? '' : 'es'\n }: ${branches\n .map((b) => b.branchName)\n .sort()\n .join(', ')}`,\n );\n\n const concurrentPrsCount = await getConcurrentPrsCount(config, branches);\n setCount('ConcurrentPRs', concurrentPrsCount);\n\n const concurrentBranchesCount = await getConcurrentBranchesCount(branches);\n setCount('Branches', concurrentBranchesCount);\n\n const prsThisHourCount = await getPrHourlyCount(config);\n setCount('HourlyPRs', prsThisHourCount);\n\n const commitsThisHourCount = await getCommitsHourlyCount(branches);\n setCount('HourlyCommits', commitsThisHourCount);\n\n for (const branch of branches) {\n const { baseBranch, branchName } = branch;\n const res = await instrument(\n branchName,\n async () => {\n const meta: Record<string, string> = { branch: branchName };\n if (config.baseBranchPatterns?.length && baseBranch) {\n meta.baseBranch = baseBranch;\n }\n addMeta(meta);\n const branchExisted = await scm.branchExists(branchName);\n const branchState = await syncBranchState(branchName, baseBranch);\n\n const managers = [\n ...new Set(\n branch.upgrades\n .map((upgrade) => hashMap.get(upgrade.manager) ?? upgrade.manager)\n .filter(isString),\n ),\n ].sort();\n const commitFingerprint = fingerprint({\n commitFingerprintConfig: generateCommitFingerprintConfig(branch),\n managers,\n });\n branch.cacheFingerprintMatch = compareCacheFingerprint(\n branchState,\n commitFingerprint,\n );\n\n const res = await processBranch(branch);\n branch.prBlockedBy = res?.prBlockedBy;\n branch.prNo = res?.prNo;\n branch.result = res?.result;\n branch.commitFingerprint = res?.updatesVerified\n ? commitFingerprint\n : branchState.commitFingerprint;\n\n if (res?.commitSha) {\n // Get the commit timestamp for the new commit\n const commitDate = await scm.getBranchUpdateDate(branchName);\n setBranchNewCommit(branchName, baseBranch, res.commitSha, commitDate);\n }\n if (\n branch.result === 'automerged' &&\n branch.automergeType !== 'pr-comment'\n ) {\n // Stop processing other branches because base branch has been changed\n return 'automerged';\n }\n if (!branchExisted && (await scm.branchExists(branch.branchName))) {\n incCountValue('Branches');\n }\n },\n {\n attributes: {\n [ATTR_VCS_REF_TYPE]: 'branch',\n [ATTR_VCS_REF_BASE_TYPE]: 'branch',\n [ATTR_VCS_REF_HEAD_NAME]: branchName,\n },\n },\n );\n\n if (res !== undefined) {\n return res;\n }\n }\n removeMeta(['branch', 'baseBranch']);\n return 'done';\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"write.js","names":[],"sources":["../../../../lib/workers/repository/process/write.ts"],"sourcesContent":["import {\n ATTR_VCS_REF_BASE_TYPE,\n ATTR_VCS_REF_HEAD_NAME,\n ATTR_VCS_REF_TYPE,\n} from '@opentelemetry/semantic-conventions/incubating';\nimport { isString } from '@sindresorhus/is';\nimport type { RenovateConfig } from '../../../config/types.ts';\nimport { instrument } from '../../../instrumentation/index.ts';\nimport { addMeta, logger, removeMeta } from '../../../logger/index.ts';\nimport { hashMap } from '../../../modules/manager/index.ts';\nimport { scm } from '../../../modules/platform/scm.ts';\nimport { getCache } from '../../../util/cache/repository/index.ts';\nimport type { BranchCache } from '../../../util/cache/repository/types.ts';\nimport { fingerprint } from '../../../util/fingerprint.ts';\nimport { setBranchNewCommit } from '../../../util/git/set-branch-commit.ts';\nimport { incCountValue, setCount } from '../../global/limits.ts';\nimport type {\n BranchConfig,\n CacheFingerprintMatchResult,\n UpgradeFingerprintConfig,\n} from '../../types.ts';\nimport { processBranch } from '../update/branch/index.ts';\nimport { upgradeFingerprintFields } from './fingerprint-fields.ts';\nimport {\n getCommitsHourlyCount,\n getConcurrentBranchesCount,\n getConcurrentPrsCount,\n getPrHourlyCount,\n} from './limits.ts';\n\nexport type WriteUpdateResult = 'done' | 'automerged';\n\nexport function generateCommitFingerprintConfig(\n branch: BranchConfig,\n): UpgradeFingerprintConfig[] {\n const res = branch.upgrades.map((upgrade) => {\n const filteredUpgrade = {} as UpgradeFingerprintConfig;\n for (const field of upgradeFingerprintFields) {\n // TS cannot narrow the type here\n filteredUpgrade[field] = upgrade[field]!;\n }\n return filteredUpgrade;\n });\n\n return res;\n}\n\nexport function compareCacheFingerprint(\n branchState: BranchCache,\n commitFingerprint: string,\n): CacheFingerprintMatchResult {\n if (!branchState.commitFingerprint) {\n logger.trace('branch.isUpToDate(): no fingerprint');\n return 'no-fingerprint';\n }\n\n if (commitFingerprint !== branchState.commitFingerprint) {\n logger.debug('branch.isUpToDate(): needs recalculation');\n return 'no-match';\n }\n\n logger.debug('branch.isUpToDate(): using cached result \"true\"');\n return 'matched';\n}\n\nexport async function syncBranchState(\n branchName: string,\n baseBranch: string,\n): Promise<BranchCache> {\n logger.debug('syncBranchState()');\n const branchSha = await scm.getBranchCommit(branchName);\n const baseBranchSha = await scm.getBranchCommit(baseBranch);\n\n const cache = getCache();\n cache.branches ??= [];\n const { branches: cachedBranches } = cache;\n let branchState = cachedBranches.find((br) => br.branchName === branchName);\n if (!branchState) {\n logger.debug(\n 'syncBranchState(): Branch cache not found, creating minimal branchState',\n );\n // create a minimal branch state\n branchState = {\n branchName,\n sha: branchSha,\n baseBranch,\n baseBranchSha,\n } as BranchCache;\n cachedBranches.push(branchState);\n }\n\n // if base branch name has changed invalidate cached isModified state\n if (baseBranch !== branchState.baseBranch) {\n logger.debug('syncBranchState(): update baseBranch name');\n branchState.baseBranch = baseBranch;\n delete branchState.isModified;\n branchState.pristine = false;\n }\n\n // if base branch sha has changed invalidate cached isBehindBase state\n if (baseBranchSha !== branchState.baseBranchSha) {\n logger.debug('syncBranchState(): update baseBranchSha');\n delete branchState.isBehindBase;\n delete branchState.isConflicted;\n\n // update cached branchSha\n branchState.baseBranchSha = baseBranchSha;\n branchState.pristine = false;\n }\n\n // if branch sha has changed invalidate all cached states\n if (branchSha !== branchState.sha) {\n logger.debug('syncBranchState(): update branchSha');\n delete branchState.isBehindBase;\n delete branchState.isConflicted;\n delete branchState.isModified;\n delete branchState.commitFingerprint;\n\n // Update commit timestamp when SHA changes\n const commitDate = await scm.getBranchUpdateDate(branchName);\n if (commitDate) {\n branchState.commitTimestamp = commitDate.toISO()!;\n }\n\n // update cached branchSha\n branchState.sha = branchSha;\n branchState.pristine = false;\n }\n\n return branchState;\n}\n\nexport async function writeUpdates(\n config: RenovateConfig,\n allBranches: BranchConfig[],\n): Promise<WriteUpdateResult> {\n const branches = allBranches;\n logger.debug(\n `Processing ${branches.length} branch${\n branches.length === 1 ? '' : 'es'\n }: ${branches\n .map((b) => b.branchName)\n .sort()\n .join(', ')}`,\n );\n\n // vulnerability alerts are counted separately so that they get their own limit budget\n const vulnerabilityBranches = branches.filter((b) => b.isVulnerabilityAlert);\n const otherBranches = branches.filter((b) => !b.isVulnerabilityAlert);\n\n setCount('ConcurrentPRs', await getConcurrentPrsCount(config, otherBranches));\n setCount(\n 'VulnerabilityConcurrentPRs',\n await getConcurrentPrsCount(config, vulnerabilityBranches),\n );\n\n setCount('Branches', await getConcurrentBranchesCount(otherBranches));\n setCount(\n 'VulnerabilityBranches',\n await getConcurrentBranchesCount(vulnerabilityBranches),\n );\n\n const prsThisHourCount = await getPrHourlyCount(config);\n setCount('HourlyPRs', prsThisHourCount);\n\n const commitsThisHourCount = await getCommitsHourlyCount(branches);\n setCount('HourlyCommits', commitsThisHourCount);\n\n for (const branch of branches) {\n const { baseBranch, branchName } = branch;\n const res = await instrument(\n branchName,\n async () => {\n const meta: Record<string, string> = { branch: branchName };\n if (config.baseBranchPatterns?.length && baseBranch) {\n meta.baseBranch = baseBranch;\n }\n addMeta(meta);\n const branchExisted = await scm.branchExists(branchName);\n const branchState = await syncBranchState(branchName, baseBranch);\n\n const managers = [\n ...new Set(\n branch.upgrades\n .map((upgrade) => hashMap.get(upgrade.manager) ?? upgrade.manager)\n .filter(isString),\n ),\n ].sort();\n const commitFingerprint = fingerprint({\n commitFingerprintConfig: generateCommitFingerprintConfig(branch),\n managers,\n });\n branch.cacheFingerprintMatch = compareCacheFingerprint(\n branchState,\n commitFingerprint,\n );\n\n const res = await processBranch(branch);\n branch.prBlockedBy = res?.prBlockedBy;\n branch.prNo = res?.prNo;\n branch.result = res?.result;\n branch.commitFingerprint = res?.updatesVerified\n ? commitFingerprint\n : branchState.commitFingerprint;\n\n if (res?.commitSha) {\n // Get the commit timestamp for the new commit\n const commitDate = await scm.getBranchUpdateDate(branchName);\n setBranchNewCommit(branchName, baseBranch, res.commitSha, commitDate);\n }\n if (\n branch.result === 'automerged' &&\n branch.automergeType !== 'pr-comment'\n ) {\n // Stop processing other branches because base branch has been changed\n return 'automerged';\n }\n if (!branchExisted && (await scm.branchExists(branch.branchName))) {\n incCountValue(\n branch.isVulnerabilityAlert ? 'VulnerabilityBranches' : 'Branches',\n );\n }\n },\n {\n attributes: {\n [ATTR_VCS_REF_TYPE]: 'branch',\n [ATTR_VCS_REF_BASE_TYPE]: 'branch',\n [ATTR_VCS_REF_HEAD_NAME]: branchName,\n },\n },\n );\n\n if (res !== undefined) {\n return res;\n }\n }\n removeMeta(['branch', 'baseBranch']);\n return 'done';\n}\n"],"mappings":"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"}
@@ -146,7 +146,7 @@ async function processBranch(branchConfig, forceRebase = false) {
146
146
  }
147
147
  }
148
148
  logger.debug(`Open PR Count: ${getCount("ConcurrentPRs")}, Existing Branch Count: ${getCount("Branches")}, Hourly PR Count: ${getCount("HourlyPRs")}, Hourly Commit Count: ${getCount("HourlyCommits")}`);
149
- if (!branchExists && isLimitReached("Branches", branchConfig) && !dependencyDashboardCheck && !config.isVulnerabilityAlert) {
149
+ if (!branchExists && isLimitReached("Branches", branchConfig) && !dependencyDashboardCheck) {
150
150
  logger.debug("Reached branch limit - skipping branch creation");
151
151
  return {
152
152
  branchExists,