renovate 43.133.0 → 43.134.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.
- package/dist/modules/datasource/aws-machine-image/index.js +1 -1
- package/dist/modules/datasource/aws-machine-image/index.js.map +1 -1
- package/dist/modules/datasource/docker/index.js +2 -1
- package/dist/modules/datasource/docker/index.js.map +1 -1
- package/dist/modules/datasource/go/index.js +1 -0
- package/dist/modules/datasource/go/index.js.map +1 -1
- package/dist/modules/datasource/go/releases-direct.js +1 -0
- package/dist/modules/datasource/go/releases-direct.js.map +1 -1
- package/dist/modules/datasource/maven/index.js +1 -1
- package/dist/modules/datasource/maven/index.js.map +1 -1
- package/dist/modules/datasource/nuget/v3.js +1 -1
- package/dist/modules/datasource/nuget/v3.js.map +1 -1
- package/dist/modules/datasource/packagist/index.js +1 -1
- package/dist/modules/datasource/packagist/index.js.map +1 -1
- package/dist/modules/manager/ansible-galaxy/collections.js +1 -1
- package/dist/modules/manager/ansible-galaxy/collections.js.map +1 -1
- package/dist/modules/manager/custom/regex/strategies.js +1 -1
- package/dist/modules/manager/custom/regex/strategies.js.map +1 -1
- package/dist/modules/manager/fingerprint.generated.js +1 -1
- package/dist/modules/manager/fingerprint.generated.js.map +1 -1
- package/dist/modules/manager/kustomize/artifacts.js +1 -0
- package/dist/modules/manager/kustomize/artifacts.js.map +1 -1
- package/dist/modules/manager/nuget/artifacts.js +3 -2
- package/dist/modules/manager/nuget/artifacts.js.map +1 -1
- package/dist/modules/manager/nuget/package-tree.js +7 -6
- package/dist/modules/manager/nuget/package-tree.js.map +1 -1
- package/dist/modules/manager/terragrunt/extract.js +2 -0
- package/dist/modules/manager/terragrunt/extract.js.map +1 -1
- package/dist/modules/platform/github/schema.d.ts +2 -2
- package/dist/modules/versioning/cargo/index.js +1 -1
- package/dist/modules/versioning/cargo/index.js.map +1 -1
- package/dist/modules/versioning/ruby/index.js +1 -0
- package/dist/modules/versioning/ruby/index.js.map +1 -1
- package/dist/modules/versioning/ruby/range.js +1 -0
- package/dist/modules/versioning/ruby/range.js.map +1 -1
- package/dist/modules/versioning/ruby/strategies/replace.js +1 -0
- package/dist/modules/versioning/ruby/strategies/replace.js.map +1 -1
- package/dist/workers/repository/process/lookup/filter.js +1 -1
- package/dist/workers/repository/process/lookup/filter.js.map +1 -1
- package/dist/workers/repository/process/lookup/index.js +2 -3
- package/dist/workers/repository/process/lookup/index.js.map +1 -1
- package/package.json +2 -2
- package/renovate-schema.json +2 -2
|
@@ -53,7 +53,7 @@ var AwsMachineImageDatasource = class AwsMachineImageDatasource extends Datasour
|
|
|
53
53
|
const matchingImages = await this.getEC2Client(clientConfig).send(amiFilterCmd);
|
|
54
54
|
matchingImages.Images = matchingImages.Images ?? [];
|
|
55
55
|
return matchingImages.Images.sort((image1, image2) => {
|
|
56
|
-
return (image1.CreationDate ? Date.parse(image1.CreationDate) : 0) - (image2.CreationDate ? Date.parse(image2.CreationDate) : 0);
|
|
56
|
+
return (image1.CreationDate ? Date.parse(image1.CreationDate) : /* v8 ignore next */ 0) - (image2.CreationDate ? Date.parse(image2.CreationDate) : /* v8 ignore next */ 0);
|
|
57
57
|
});
|
|
58
58
|
}
|
|
59
59
|
getSortedAwsMachineImages(serializedAmiFilter) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["amazonMachineImageVersioning.id"],"sources":["../../../../lib/modules/datasource/aws-machine-image/index.ts"],"sourcesContent":["import type { Filter, Image } from '@aws-sdk/client-ec2';\nimport { DescribeImagesCommand, EC2Client } from '@aws-sdk/client-ec2';\nimport { fromNodeProviderChain } from '@aws-sdk/credential-providers';\nimport { withCache } from '../../../util/cache/package/with-cache.ts';\nimport { asTimestamp } from '../../../util/timestamp.ts';\nimport * as amazonMachineImageVersioning from '../../versioning/aws-machine-image/index.ts';\nimport { Datasource } from '../datasource.ts';\nimport type { GetReleasesConfig, ReleaseResult } from '../types.ts';\nimport type { AwsClientConfig, ParsedConfig } from './types.ts';\n\nexport class AwsMachineImageDatasource extends Datasource {\n static readonly id = 'aws-machine-image';\n\n override readonly defaultVersioning = amazonMachineImageVersioning.id;\n\n override readonly caching = true;\n\n override readonly releaseTimestampSupport = true;\n override readonly releaseTimestampNote =\n 'The release timestamp is determined from the `CreationDate` field in the results.';\n\n override readonly defaultConfig = {\n // Because AMIs don't follow any versioning scheme, we override commitMessageExtra to remove the 'v'\n commitMessageExtra: 'to {{{newVersion}}}',\n prBodyColumns: ['Change', 'Image'],\n prBodyDefinitions: {\n Image: '```{{{newDigest}}}```',\n },\n digest: {\n // Because newDigestShort will allways be 'amazon-' we override to print the name of the AMI\n commitMessageExtra: 'to {{{newDigest}}}',\n prBodyColumns: ['Image'],\n prBodyDefinitions: {\n Image: '```{{{newDigest}}}```',\n },\n },\n };\n\n private readonly now: number;\n\n constructor() {\n super(AwsMachineImageDatasource.id);\n this.now = Date.now();\n }\n\n private isAmiFilter(config: Filter | AwsClientConfig): config is Filter {\n return 'Name' in config && 'Values' in config;\n }\n\n private getEC2Client(config: AwsClientConfig): EC2Client {\n const { profile, region } = config;\n return new EC2Client({\n region,\n credentials: fromNodeProviderChain({ profile }),\n });\n }\n\n private getAmiFilterCommand(filter: Filter[]): DescribeImagesCommand {\n return new DescribeImagesCommand({\n Filters: filter,\n });\n }\n\n loadConfig(serializedAmiFilter: string): [Filter[], AwsClientConfig] {\n const parsedConfig: ParsedConfig = JSON.parse(serializedAmiFilter);\n const filters = [];\n let config = {};\n for (const elem of parsedConfig) {\n if (this.isAmiFilter(elem)) {\n // Separate actual AMI filters from aws client config\n filters.push(elem);\n } else {\n // merge config objects if there are multiple\n config = Object.assign(config, elem);\n }\n }\n return [filters, config];\n }\n\n private async _getSortedAwsMachineImages(\n serializedAmiFilter: string,\n ): Promise<Image[]> {\n const [amiFilter, clientConfig] = this.loadConfig(serializedAmiFilter);\n const amiFilterCmd = this.getAmiFilterCommand(amiFilter);\n const ec2Client = this.getEC2Client(clientConfig);\n const matchingImages = await ec2Client.send(amiFilterCmd);\n matchingImages.Images = matchingImages.Images ?? [];\n return matchingImages.Images.sort((image1, image2) => {\n const ts1 = image1.CreationDate\n ? Date.parse(image1.CreationDate)\n : /* v8 ignore next */ 0; // TODO: add date coersion util\n\n const ts2 = image2.CreationDate\n ? Date.parse(image2.CreationDate)\n : /* v8 ignore next */ 0; // TODO: add date coersion util\n return ts1 - ts2;\n });\n }\n\n getSortedAwsMachineImages(serializedAmiFilter: string): Promise<Image[]> {\n return withCache(\n {\n namespace: `datasource-${AwsMachineImageDatasource.id}`,\n key: `getSortedAwsMachineImages:${serializedAmiFilter}`,\n },\n () => this._getSortedAwsMachineImages(serializedAmiFilter),\n );\n }\n\n private async _getDigest(\n { packageName: serializedAmiFilter }: GetReleasesConfig,\n newValue?: string,\n ): Promise<string | null> {\n const images = await this.getSortedAwsMachineImages(serializedAmiFilter);\n if (images.length < 1) {\n return null;\n }\n\n if (newValue) {\n const newValueMatchingImages = images.filter(\n (image) => image.ImageId === newValue,\n );\n if (\n newValueMatchingImages.length === 1 &&\n newValueMatchingImages[0].Name\n ) {\n return newValueMatchingImages[0].Name;\n }\n return null;\n }\n\n const res = await this.getReleases({ packageName: serializedAmiFilter });\n return res?.releases?.[0]?.newDigest ?? /* v8 ignore next */ null; // TODO: needs test\n }\n\n override getDigest(\n config: GetReleasesConfig,\n newValue?: string,\n ): Promise<string | null> {\n return withCache(\n {\n namespace: `datasource-${AwsMachineImageDatasource.id}`,\n key: `getDigest:${config.packageName}:${newValue ?? ''}`,\n fallback: true,\n },\n () => this._getDigest(config, newValue),\n );\n }\n\n private async _getReleases({\n packageName: serializedAmiFilter,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n const images = await this.getSortedAwsMachineImages(serializedAmiFilter);\n const latestImage = images[images.length - 1];\n if (!latestImage?.ImageId) {\n return null;\n }\n return {\n releases: [\n {\n version: latestImage.ImageId,\n releaseTimestamp: asTimestamp(latestImage.CreationDate),\n isDeprecated:\n Date.parse(latestImage.DeprecationTime ?? this.now.toString()) <\n this.now,\n newDigest: latestImage.Name,\n },\n ],\n };\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n return withCache(\n {\n namespace: `datasource-${AwsMachineImageDatasource.id}`,\n key: `getReleases:${config.packageName}`,\n fallback: true,\n },\n () => this._getReleases(config),\n );\n }\n}\n"],"mappings":";;;;;;;AAUA,IAAa,4BAAb,MAAa,kCAAkC,WAAW;CACxD,OAAgB,KAAK;CAErB,oBAAsCA;CAEtC,UAA4B;CAE5B,0BAA4C;CAC5C,uBACE;CAEF,gBAAkC;EAEhC,oBAAoB;EACpB,eAAe,CAAC,UAAU,QAAQ;EAClC,mBAAmB,EACjB,OAAO,yBACR;EACD,QAAQ;GAEN,oBAAoB;GACpB,eAAe,CAAC,QAAQ;GACxB,mBAAmB,EACjB,OAAO,yBACR;GACF;EACF;CAED;CAEA,cAAc;AACZ,QAAM,0BAA0B,GAAG;AACnC,OAAK,MAAM,KAAK,KAAK;;CAGvB,YAAoB,QAAoD;AACtE,SAAO,UAAU,UAAU,YAAY;;CAGzC,aAAqB,QAAoC;EACvD,MAAM,EAAE,SAAS,WAAW;AAC5B,SAAO,IAAI,UAAU;GACnB;GACA,aAAa,sBAAsB,EAAE,SAAS,CAAC;GAChD,CAAC;;CAGJ,oBAA4B,QAAyC;AACnE,SAAO,IAAI,sBAAsB,EAC/B,SAAS,QACV,CAAC;;CAGJ,WAAW,qBAA0D;EACnE,MAAM,eAA6B,KAAK,MAAM,oBAAoB;EAClE,MAAM,UAAU,EAAE;EAClB,IAAI,SAAS,EAAE;AACf,OAAK,MAAM,QAAQ,aACjB,KAAI,KAAK,YAAY,KAAK,CAExB,SAAQ,KAAK,KAAK;MAGlB,UAAS,OAAO,OAAO,QAAQ,KAAK;AAGxC,SAAO,CAAC,SAAS,OAAO;;CAG1B,MAAc,2BACZ,qBACkB;EAClB,MAAM,CAAC,WAAW,gBAAgB,KAAK,WAAW,oBAAoB;EACtE,MAAM,eAAe,KAAK,oBAAoB,UAAU;EAExD,MAAM,iBAAiB,MADL,KAAK,aAAa,aAAa,CACV,KAAK,aAAa;AACzD,iBAAe,SAAS,eAAe,UAAU,EAAE;AACnD,SAAO,eAAe,OAAO,MAAM,QAAQ,WAAW;AAQpD,WAPY,OAAO,eACf,KAAK,MAAM,OAAO,aAAa,
|
|
1
|
+
{"version":3,"file":"index.js","names":["amazonMachineImageVersioning.id"],"sources":["../../../../lib/modules/datasource/aws-machine-image/index.ts"],"sourcesContent":["import type { Filter, Image } from '@aws-sdk/client-ec2';\nimport { DescribeImagesCommand, EC2Client } from '@aws-sdk/client-ec2';\nimport { fromNodeProviderChain } from '@aws-sdk/credential-providers';\nimport { withCache } from '../../../util/cache/package/with-cache.ts';\nimport { asTimestamp } from '../../../util/timestamp.ts';\nimport * as amazonMachineImageVersioning from '../../versioning/aws-machine-image/index.ts';\nimport { Datasource } from '../datasource.ts';\nimport type { GetReleasesConfig, ReleaseResult } from '../types.ts';\nimport type { AwsClientConfig, ParsedConfig } from './types.ts';\n\nexport class AwsMachineImageDatasource extends Datasource {\n static readonly id = 'aws-machine-image';\n\n override readonly defaultVersioning = amazonMachineImageVersioning.id;\n\n override readonly caching = true;\n\n override readonly releaseTimestampSupport = true;\n override readonly releaseTimestampNote =\n 'The release timestamp is determined from the `CreationDate` field in the results.';\n\n override readonly defaultConfig = {\n // Because AMIs don't follow any versioning scheme, we override commitMessageExtra to remove the 'v'\n commitMessageExtra: 'to {{{newVersion}}}',\n prBodyColumns: ['Change', 'Image'],\n prBodyDefinitions: {\n Image: '```{{{newDigest}}}```',\n },\n digest: {\n // Because newDigestShort will allways be 'amazon-' we override to print the name of the AMI\n commitMessageExtra: 'to {{{newDigest}}}',\n prBodyColumns: ['Image'],\n prBodyDefinitions: {\n Image: '```{{{newDigest}}}```',\n },\n },\n };\n\n private readonly now: number;\n\n constructor() {\n super(AwsMachineImageDatasource.id);\n this.now = Date.now();\n }\n\n private isAmiFilter(config: Filter | AwsClientConfig): config is Filter {\n return 'Name' in config && 'Values' in config;\n }\n\n private getEC2Client(config: AwsClientConfig): EC2Client {\n const { profile, region } = config;\n return new EC2Client({\n region,\n credentials: fromNodeProviderChain({ profile }),\n });\n }\n\n private getAmiFilterCommand(filter: Filter[]): DescribeImagesCommand {\n return new DescribeImagesCommand({\n Filters: filter,\n });\n }\n\n loadConfig(serializedAmiFilter: string): [Filter[], AwsClientConfig] {\n const parsedConfig: ParsedConfig = JSON.parse(serializedAmiFilter);\n const filters = [];\n let config = {};\n for (const elem of parsedConfig) {\n if (this.isAmiFilter(elem)) {\n // Separate actual AMI filters from aws client config\n filters.push(elem);\n } else {\n // merge config objects if there are multiple\n config = Object.assign(config, elem);\n }\n }\n return [filters, config];\n }\n\n private async _getSortedAwsMachineImages(\n serializedAmiFilter: string,\n ): Promise<Image[]> {\n const [amiFilter, clientConfig] = this.loadConfig(serializedAmiFilter);\n const amiFilterCmd = this.getAmiFilterCommand(amiFilter);\n const ec2Client = this.getEC2Client(clientConfig);\n const matchingImages = await ec2Client.send(amiFilterCmd);\n matchingImages.Images = matchingImages.Images ?? [];\n return matchingImages.Images.sort((image1, image2) => {\n const ts1 = image1.CreationDate\n ? Date.parse(image1.CreationDate)\n : /* v8 ignore next */ 0; // TODO: add date coersion util\n\n const ts2 = image2.CreationDate\n ? Date.parse(image2.CreationDate)\n : /* v8 ignore next */ 0; // TODO: add date coersion util\n return ts1 - ts2;\n });\n }\n\n getSortedAwsMachineImages(serializedAmiFilter: string): Promise<Image[]> {\n return withCache(\n {\n namespace: `datasource-${AwsMachineImageDatasource.id}`,\n key: `getSortedAwsMachineImages:${serializedAmiFilter}`,\n },\n () => this._getSortedAwsMachineImages(serializedAmiFilter),\n );\n }\n\n private async _getDigest(\n { packageName: serializedAmiFilter }: GetReleasesConfig,\n newValue?: string,\n ): Promise<string | null> {\n const images = await this.getSortedAwsMachineImages(serializedAmiFilter);\n if (images.length < 1) {\n return null;\n }\n\n if (newValue) {\n const newValueMatchingImages = images.filter(\n (image) => image.ImageId === newValue,\n );\n if (\n newValueMatchingImages.length === 1 &&\n newValueMatchingImages[0].Name\n ) {\n return newValueMatchingImages[0].Name;\n }\n return null;\n }\n\n const res = await this.getReleases({ packageName: serializedAmiFilter });\n return res?.releases?.[0]?.newDigest ?? /* v8 ignore next */ null; // TODO: needs test\n }\n\n override getDigest(\n config: GetReleasesConfig,\n newValue?: string,\n ): Promise<string | null> {\n return withCache(\n {\n namespace: `datasource-${AwsMachineImageDatasource.id}`,\n key: `getDigest:${config.packageName}:${newValue ?? ''}`,\n fallback: true,\n },\n () => this._getDigest(config, newValue),\n );\n }\n\n private async _getReleases({\n packageName: serializedAmiFilter,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n const images = await this.getSortedAwsMachineImages(serializedAmiFilter);\n const latestImage = images[images.length - 1];\n if (!latestImage?.ImageId) {\n return null;\n }\n return {\n releases: [\n {\n version: latestImage.ImageId,\n releaseTimestamp: asTimestamp(latestImage.CreationDate),\n isDeprecated:\n Date.parse(latestImage.DeprecationTime ?? this.now.toString()) <\n this.now,\n newDigest: latestImage.Name,\n },\n ],\n };\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n return withCache(\n {\n namespace: `datasource-${AwsMachineImageDatasource.id}`,\n key: `getReleases:${config.packageName}`,\n fallback: true,\n },\n () => this._getReleases(config),\n );\n }\n}\n"],"mappings":"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"}
|
|
@@ -186,6 +186,7 @@ var DockerDatasource = class DockerDatasource extends Datasource {
|
|
|
186
186
|
tag
|
|
187
187
|
}, "Found manifest list, using first image");
|
|
188
188
|
return this.getManifest(registry, dockerRepository, manifest.manifests[0].digest);
|
|
189
|
+
// istanbul ignore next: can't happen
|
|
189
190
|
default: return null;
|
|
190
191
|
}
|
|
191
192
|
}
|
|
@@ -195,7 +196,7 @@ var DockerDatasource = class DockerDatasource extends Datasource {
|
|
|
195
196
|
try {
|
|
196
197
|
manifestResponse = await this.getManifestResponse(registryHost, dockerRepository, currentDigest, "head");
|
|
197
198
|
} catch (_err) {
|
|
198
|
-
const err = _err instanceof ExternalHostError ? _err.err : _err;
|
|
199
|
+
const err = _err instanceof ExternalHostError ? _err.err : /* istanbul ignore next: can never happen */ _err;
|
|
199
200
|
if (typeof err.statusCode === "number" && err.statusCode >= 500 && err.statusCode < 600) return null;
|
|
200
201
|
/* istanbul ignore next */
|
|
201
202
|
throw _err;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["dockerVersioningId","HttpError"],"sources":["../../../../lib/modules/datasource/docker/index.ts"],"sourcesContent":["import { isNonEmptyString } from '@sindresorhus/is';\nimport { GlobalConfig } from '../../../config/global.ts';\nimport { PAGE_NOT_FOUND_ERROR } from '../../../constants/error-messages.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 { getEnv } from '../../../util/env.ts';\nimport { memCacheProvider } from '../../../util/http/cache/memory-http-cache-provider.ts';\nimport { HttpError } from '../../../util/http/index.ts';\nimport type { HttpResponse } from '../../../util/http/types.ts';\nimport { hasKey } from '../../../util/object.ts';\nimport { type AsyncResult, Result } from '../../../util/result.ts';\nimport { isDockerDigest } from '../../../util/string-match.ts';\nimport { asTimestamp } from '../../../util/timestamp.ts';\nimport {\n ensurePathPrefix,\n joinUrlParts,\n parseLinkHeader,\n} from '../../../util/url.ts';\nimport { id as dockerVersioningId } from '../../versioning/docker/index.ts';\nimport { Datasource } from '../datasource.ts';\nimport type {\n DigestConfig,\n GetReleasesConfig,\n Release,\n ReleaseResult,\n} from '../types.ts';\nimport { isArtifactoryServer } from '../util.ts';\nimport {\n DOCKER_HUB,\n dockerDatasourceId,\n extractDigestFromResponseBody,\n findHelmSourceUrl,\n findLatestStable,\n getAuthHeaders,\n getRegistryRepository,\n gitRefLabel,\n imageUrlLabel,\n isDockerHost,\n sourceLabel,\n sourceLabels,\n} from './common.ts';\nimport { DockerHubCache } from './dockerhub-cache.ts';\nimport { ecrPublicRegex, ecrRegex, isECRMaxResultsError } from './ecr.ts';\nimport type { DistributionManifest, OciImageManifest } from './schema.ts';\nimport {\n DockerHubTagsPage,\n ManifestJson,\n OciHelmConfig,\n OciImageConfig,\n} from './schema.ts';\n\nconst defaultConfig = {\n commitMessageTopic: '{{{depName}}} Docker tag',\n commitMessageExtra:\n 'to {{#if isPinDigest}}{{{newDigestShort}}}{{else}}{{#if isMajor}}{{{prettyNewMajor}}}{{else}}{{{prettyNewVersion}}}{{/if}}{{/if}}',\n digest: {\n branchTopic: '{{{depNameSanitized}}}-{{{currentValue}}}',\n commitMessageExtra: 'to {{newDigestShort}}',\n commitMessageTopic:\n '{{{depName}}}{{#if currentValue}}:{{{currentValue}}}{{/if}} Docker digest',\n group: {\n commitMessageTopic: '{{{groupName}}}',\n commitMessageExtra: '',\n },\n },\n pin: {\n commitMessageExtra: '',\n groupName: 'Docker digests',\n group: {\n commitMessageTopic: '{{{groupName}}}',\n branchTopic: 'digests-pin',\n },\n },\n};\n\nexport class DockerDatasource extends Datasource {\n static readonly id = dockerDatasourceId;\n\n override readonly defaultVersioning = dockerVersioningId;\n\n override readonly defaultRegistryUrls = [DOCKER_HUB];\n\n override readonly defaultConfig = defaultConfig;\n\n override readonly releaseTimestampSupport = true;\n override readonly releaseTimestampNote =\n 'Only supported on Docker Hub: The release timestamp is determined from the `tag_last_pushed` field in the results. **NOTE**: Currently, digests will receive the same release timestamp as the `tag_last_pushed`, which means that digests may appear newer than they are - see https://github.com/renovatebot/renovate/issues/38659';\n override readonly sourceUrlSupport = 'package';\n override readonly sourceUrlNote =\n 'The source URL is determined from the `org.opencontainers.image.source` and `org.label-schema.vcs-url` labels present in the metadata of the **latest stable** image found on the Docker registry.';\n\n constructor() {\n super(DockerDatasource.id);\n }\n\n // TODO: debug why quay throws errors (#9612)\n private async getManifestResponse(\n registryHost: string,\n dockerRepository: string,\n tag: string,\n mode: 'head' | 'getText' = 'getText',\n ): Promise<HttpResponse | null> {\n logger.debug(\n `getManifestResponse(${registryHost}, ${dockerRepository}, ${tag}, ${mode})`,\n );\n try {\n const headers = await getAuthHeaders(\n this.http,\n registryHost,\n dockerRepository,\n );\n if (!headers) {\n logger.warn('No docker auth found - returning');\n return null;\n }\n headers.accept = [\n 'application/vnd.docker.distribution.manifest.list.v2+json',\n 'application/vnd.docker.distribution.manifest.v2+json',\n 'application/vnd.oci.image.manifest.v1+json',\n 'application/vnd.oci.image.index.v1+json',\n ].join(', ');\n const url = `${registryHost}/v2/${dockerRepository}/manifests/${tag}`;\n const manifestResponse = await this.http[mode](url, {\n headers,\n noAuth: true,\n cacheProvider: memCacheProvider,\n });\n return manifestResponse;\n } catch (err) /* istanbul ignore next */ {\n if (err instanceof ExternalHostError) {\n throw err;\n }\n if (err.statusCode === 401) {\n logger.debug(\n { registryHost, dockerRepository },\n 'Unauthorized docker lookup',\n );\n logger.debug({ err });\n return null;\n }\n if (err.statusCode === 404) {\n logger.debug(\n {\n err,\n registryHost,\n dockerRepository,\n tag,\n },\n 'Docker Manifest is unknown',\n );\n return null;\n }\n if (err.statusCode === 429 && isDockerHost(registryHost)) {\n throw new ExternalHostError(err);\n }\n if (err.statusCode >= 500 && err.statusCode < 600) {\n throw new ExternalHostError(err);\n }\n if (err.code === 'ETIMEDOUT') {\n logger.debug(\n { registryHost },\n 'Timeout when attempting to connect to docker registry',\n );\n logger.debug({ err });\n return null;\n }\n logger.debug(\n {\n err,\n registryHost,\n dockerRepository,\n tag,\n },\n 'Unknown Error looking up docker manifest',\n );\n return null;\n }\n }\n\n private async _getImageConfig(\n registryHost: string,\n dockerRepository: string,\n configDigest: string,\n ): Promise<HttpResponse<OciImageConfig> | undefined> {\n logger.trace(\n `getImageConfig(${registryHost}, ${dockerRepository}, ${configDigest})`,\n );\n\n const headers = await getAuthHeaders(\n this.http,\n registryHost,\n dockerRepository,\n );\n /* v8 ignore next 4 -- should never happen */\n if (!headers) {\n logger.warn('No docker auth found - returning');\n return undefined;\n }\n const url = joinUrlParts(\n registryHost,\n 'v2',\n dockerRepository,\n 'blobs',\n configDigest,\n );\n return await this.http.getJson(\n url,\n {\n headers,\n noAuth: true,\n },\n OciImageConfig,\n );\n }\n\n getImageConfig(\n registryHost: string,\n dockerRepository: string,\n configDigest: string,\n ): Promise<HttpResponse<OciImageConfig> | undefined> {\n return withCache(\n {\n namespace: 'datasource-docker-imageconfig',\n key: `${registryHost}:${dockerRepository}@${configDigest}`,\n ttlMinutes: 1440 * 28,\n },\n () => this._getImageConfig(registryHost, dockerRepository, configDigest),\n );\n }\n\n private async _getHelmConfig(\n registryHost: string,\n dockerRepository: string,\n configDigest: string,\n ): Promise<HttpResponse<OciHelmConfig> | undefined> {\n logger.trace(\n `getImageConfig(${registryHost}, ${dockerRepository}, ${configDigest})`,\n );\n\n const headers = await getAuthHeaders(\n this.http,\n registryHost,\n dockerRepository,\n );\n /* v8 ignore next 4 -- should never happen */\n if (!headers) {\n logger.warn('No docker auth found - returning');\n return undefined;\n }\n const url = joinUrlParts(\n registryHost,\n 'v2',\n dockerRepository,\n 'blobs',\n configDigest,\n );\n return await this.http.getJson(\n url,\n {\n headers,\n noAuth: true,\n },\n OciHelmConfig,\n );\n }\n\n getHelmConfig(\n registryHost: string,\n dockerRepository: string,\n configDigest: string,\n ): Promise<HttpResponse<OciHelmConfig> | undefined> {\n return withCache(\n {\n namespace: 'datasource-docker-imageconfig',\n key: `${registryHost}:${dockerRepository}@${configDigest}`,\n ttlMinutes: 1440 * 28,\n },\n () => this._getHelmConfig(registryHost, dockerRepository, configDigest),\n );\n }\n\n private async getConfigDigest(\n registry: string,\n dockerRepository: string,\n tag: string,\n ): Promise<string | null> {\n return (\n (await this.getManifest(registry, dockerRepository, tag))?.config\n ?.digest ?? null\n );\n }\n\n private async getManifest(\n registry: string,\n dockerRepository: string,\n tag: string,\n ): Promise<OciImageManifest | DistributionManifest | null> {\n const manifestResponse = await this.getManifestResponse(\n registry,\n dockerRepository,\n tag,\n );\n\n // If getting the manifest fails here, then abort\n // This means that the latest tag doesn't have a manifest, which shouldn't\n // be possible\n /* v8 ignore next 3 -- should never happen */\n if (!manifestResponse) {\n return null;\n }\n\n // Softfail on invalid manifests\n const parsed = ManifestJson.safeParse(manifestResponse.body);\n if (!parsed.success) {\n logger.debug(\n {\n registry,\n dockerRepository,\n tag,\n body: manifestResponse.body,\n headers: manifestResponse.headers,\n err: parsed.error,\n },\n 'Invalid manifest response',\n );\n return null;\n }\n\n const manifest = parsed.data;\n\n switch (manifest.mediaType) {\n case 'application/vnd.docker.distribution.manifest.v2+json':\n case 'application/vnd.oci.image.manifest.v1+json':\n return manifest;\n case 'application/vnd.docker.distribution.manifest.list.v2+json':\n case 'application/vnd.oci.image.index.v1+json':\n if (!manifest.manifests.length) {\n logger.debug(\n { manifest },\n 'Invalid manifest list with no manifests - returning',\n );\n return null;\n }\n logger.trace(\n { registry, dockerRepository, tag },\n 'Found manifest list, using first image',\n );\n return this.getManifest(\n registry,\n dockerRepository,\n manifest.manifests[0].digest,\n );\n // istanbul ignore next: can't happen\n default:\n return null;\n }\n }\n\n private async _getImageArchitecture(\n registryHost: string,\n dockerRepository: string,\n currentDigest: string,\n ): Promise<string | null | undefined> {\n try {\n let manifestResponse: HttpResponse<string> | null;\n\n try {\n manifestResponse = await this.getManifestResponse(\n registryHost,\n dockerRepository,\n currentDigest,\n 'head',\n );\n } catch (_err) {\n const err =\n _err instanceof ExternalHostError\n ? _err.err\n : /* istanbul ignore next: can never happen */ _err;\n\n if (\n typeof err.statusCode === 'number' &&\n err.statusCode >= 500 &&\n err.statusCode < 600\n ) {\n // querying the digest manifest for a non existent image leads to a 500 statusCode\n return null;\n }\n\n /* istanbul ignore next */\n throw _err;\n }\n\n if (\n manifestResponse?.headers['content-type'] !==\n 'application/vnd.docker.distribution.manifest.v2+json' &&\n manifestResponse?.headers['content-type'] !==\n 'application/vnd.oci.image.manifest.v1+json'\n ) {\n return null;\n }\n\n const configDigest = await this.getConfigDigest(\n registryHost,\n dockerRepository,\n currentDigest,\n );\n if (!configDigest) {\n return null;\n }\n\n const configResponse = await this.getImageConfig(\n registryHost,\n dockerRepository,\n configDigest,\n );\n\n // TODO: fix me, architecture is required in spec\n if (\n configResponse &&\n ('config' in configResponse.body ||\n 'architecture' in configResponse.body)\n ) {\n const architecture = configResponse.body.architecture ?? null;\n logger.debug(\n `Current digest ${currentDigest} relates to architecture ${\n architecture ?? 'null'\n }`,\n );\n\n return architecture;\n }\n } catch (err) /* istanbul ignore next */ {\n if (err.statusCode !== 404 || err.message === PAGE_NOT_FOUND_ERROR) {\n throw err;\n }\n logger.debug(\n { registryHost, dockerRepository, currentDigest, err },\n 'Unknown error getting image architecture',\n );\n }\n\n return undefined;\n }\n\n getImageArchitecture(\n registryHost: string,\n dockerRepository: string,\n currentDigest: string,\n ): Promise<string | null | undefined> {\n return withCache(\n {\n namespace: 'datasource-docker-architecture',\n key: `${registryHost}:${dockerRepository}@${currentDigest}`,\n ttlMinutes: 1440 * 28,\n },\n () =>\n this._getImageArchitecture(\n registryHost,\n dockerRepository,\n currentDigest,\n ),\n );\n }\n\n /*\n * docker.getLabels\n *\n * This function will:\n * - Return the labels for the requested image\n */\n private async _getLabels(\n registryHost: string,\n dockerRepository: string,\n tag: string,\n ): Promise<Record<string, string> | undefined> {\n logger.debug(`getLabels(${registryHost}, ${dockerRepository}, ${tag})`);\n // Skip Docker Hub image if RENOVATE_X_DOCKER_HUB_DISABLE_LABEL_LOOKUP is set\n if (\n getEnv().RENOVATE_X_DOCKER_HUB_DISABLE_LABEL_LOOKUP &&\n registryHost === 'https://index.docker.io'\n ) {\n logger.debug(\n 'Docker Hub image - skipping label lookup due to RENOVATE_X_DOCKER_HUB_DISABLE_LABEL_LOOKUP',\n );\n return {};\n }\n // Docker Hub library images don't have labels we need\n if (\n registryHost === 'https://index.docker.io' &&\n dockerRepository.startsWith('library/')\n ) {\n logger.debug('Docker Hub library image - skipping label lookup');\n return {};\n }\n try {\n let labels: Record<string, string> | undefined = {};\n const manifest = await this.getManifest(\n registryHost,\n dockerRepository,\n tag,\n );\n\n if (!manifest) {\n logger.debug(\n { registryHost, dockerRepository, tag },\n 'No manifest found',\n );\n return undefined;\n }\n\n if ('annotations' in manifest && manifest.annotations) {\n labels = manifest.annotations;\n }\n\n switch (manifest.config.mediaType) {\n case 'application/vnd.cncf.helm.config.v1+json': {\n if (labels[sourceLabel]) {\n // we already have the source url, so no need to pull the config\n return labels;\n }\n const configResponse = await this.getHelmConfig(\n registryHost,\n dockerRepository,\n manifest.config.digest,\n );\n\n if (configResponse) {\n // Helm chart\n const url = findHelmSourceUrl(configResponse.body);\n if (url) {\n labels[sourceLabel] = url;\n }\n }\n break;\n }\n case 'application/vnd.oci.image.config.v1+json':\n case 'application/vnd.docker.container.image.v1+json': {\n if (labels[sourceLabel] && labels[gitRefLabel]) {\n // we already have the source url, so no need to pull the config\n return labels;\n }\n const configResponse = await this.getImageConfig(\n registryHost,\n dockerRepository,\n manifest.config.digest,\n );\n\n /* v8 ignore next 3 -- should never happen */\n if (!configResponse) {\n return labels;\n }\n\n const body = configResponse.body;\n if (body.config) {\n labels = { ...labels, ...body.config.Labels };\n } else {\n logger.debug(\n { headers: configResponse.headers, body },\n `manifest blob response body missing the \"config\" property`,\n );\n }\n break;\n }\n }\n\n if (labels) {\n logger.debug(\n {\n labels,\n },\n 'found labels in manifest',\n );\n }\n return labels;\n } catch (err) /* istanbul ignore next: should be tested in future */ {\n if (err instanceof ExternalHostError) {\n throw err;\n }\n if (err.statusCode === 400 || err.statusCode === 401) {\n logger.debug(\n { registryHost, dockerRepository, err },\n 'Unauthorized docker lookup',\n );\n } else if (err.statusCode === 404) {\n logger.warn(\n {\n err,\n registryHost,\n dockerRepository,\n tag,\n },\n 'Config Manifest is unknown',\n );\n } else if (err.statusCode === 429 && isDockerHost(registryHost)) {\n logger.warn({ err }, 'docker registry failure: too many requests');\n } else if (err.statusCode >= 500 && err.statusCode < 600) {\n logger.debug(\n {\n err,\n registryHost,\n dockerRepository,\n tag,\n },\n 'docker registry failure: internal error',\n );\n } else if (\n err.code === 'ERR_TLS_CERT_ALTNAME_INVALID' ||\n err.code === 'ETIMEDOUT'\n ) {\n logger.debug(\n { registryHost, err },\n 'Error connecting to docker registry',\n );\n } else if (registryHost === 'https://quay.io') {\n // istanbul ignore next\n logger.debug(\n 'Ignoring quay.io errors until they fully support v2 schema',\n );\n } else {\n logger.info(\n { registryHost, dockerRepository, tag, err },\n 'Unknown error getting Docker labels',\n );\n }\n return {};\n }\n }\n\n getLabels(\n registryHost: string,\n dockerRepository: string,\n tag: string,\n ): Promise<Record<string, string> | undefined> {\n return withCache(\n {\n namespace: 'datasource-docker-labels',\n key: `${registryHost}:${dockerRepository}:${tag}`,\n ttlMinutes: 24 * 60,\n },\n () => this._getLabels(registryHost, dockerRepository, tag),\n );\n }\n\n private async getTagsQuayRegistry(\n registry: string,\n repository: string,\n ): Promise<string[]> {\n let tags: string[] = [];\n const limit = 100;\n\n const pageUrl = (page: number): string =>\n `${registry}/api/v1/repository/${repository}/tag/?limit=${limit}&page=${page}&onlyActiveTags=true`;\n\n let page = 1;\n let url: string | null = pageUrl(page);\n while (url && page <= 20) {\n interface QuayRestDockerTags {\n tags: {\n name: string;\n }[];\n has_additional: boolean;\n }\n\n // typescript issue :-/\n // oxlint-disable typescript/no-unnecessary-type-assertion\n const res = (await this.http.getJsonUnchecked<QuayRestDockerTags>(\n url,\n )) as HttpResponse<QuayRestDockerTags>;\n // oxlint-enable typescript/no-unnecessary-type-assertion\n const pageTags = res.body.tags.map((tag) => tag.name);\n tags = tags.concat(pageTags);\n page += 1;\n url = res.body.has_additional ? pageUrl(page) : null;\n }\n return tags;\n }\n\n private async getDockerApiTags(\n registryHost: string,\n dockerRepository: string,\n ): Promise<string[] | null> {\n let tags: string[] = [];\n // AWS ECR limits the maximum number of results to 1000\n // See https://docs.aws.amazon.com/AmazonECR/latest/APIReference/API_DescribeRepositories.html#ECR-DescribeRepositories-request-maxResults\n // See https://docs.aws.amazon.com/AmazonECRPublic/latest/APIReference/API_DescribeRepositories.html#ecrpublic-DescribeRepositories-request-maxResults\n const limit =\n ecrRegex.test(registryHost) || ecrPublicRegex.test(registryHost)\n ? 1000\n : 10000;\n let url: string | null =\n `${registryHost}/${dockerRepository}/tags/list?n=${limit}`;\n url = ensurePathPrefix(url, '/v2');\n const headers = await getAuthHeaders(\n this.http,\n registryHost,\n dockerRepository,\n url,\n );\n if (!headers) {\n logger.debug('Failed to get authHeaders for getTags lookup');\n return null;\n }\n let page = 0;\n const hostsNeedingAllPages = [\n 'https://ghcr.io', // GHCR sorts from oldest to newest, so we need to get all pages\n 'https://quay.io', // Quay sorts from oldest to newest, so we need to get all pages\n ];\n const pages = hostsNeedingAllPages.includes(registryHost)\n ? 1000\n : GlobalConfig.get('dockerMaxPages', 20);\n logger.trace({ registryHost, dockerRepository, pages }, 'docker.getTags');\n let foundMaxResultsError = false;\n do {\n let res: HttpResponse<{ tags: string[] }>;\n try {\n res = await this.http.getJsonUnchecked<{ tags: string[] }>(url, {\n headers,\n noAuth: true,\n });\n } catch (err) {\n if (\n !foundMaxResultsError &&\n err instanceof HttpError &&\n isECRMaxResultsError(err)\n ) {\n const maxResults = 1000;\n url = `${registryHost}/${dockerRepository}/tags/list?n=${maxResults}`;\n url = ensurePathPrefix(url, '/v2');\n foundMaxResultsError = true;\n continue;\n }\n throw err;\n }\n tags = tags.concat(res.body.tags);\n const linkHeader = parseLinkHeader(res.headers.link);\n if (isArtifactoryServer(res)) {\n // Artifactory bug: next link comes back without virtual-repo prefix (RTFACT-18971)\n if (linkHeader?.next?.last) {\n // parse the current URL, strip any old \"last\" param, then set the new one\n const parsed: URL = new URL(url);\n parsed.searchParams.delete('last');\n parsed.searchParams.set('last', linkHeader.next.last);\n url = parsed.href;\n } else {\n url = null;\n }\n } else if (linkHeader?.next?.url) {\n // for the normal case we can still use URL to resolve relative-next\n url = new URL(linkHeader.next.url, url).href;\n } else {\n url = null;\n }\n page += 1;\n } while (url && page < pages);\n return tags;\n }\n\n private async _getTags(\n registryHost: string,\n dockerRepository: string,\n ): Promise<string[] | null> {\n try {\n const isQuay = registryHost === 'https://quay.io';\n let tags: string[] | null;\n if (isQuay) {\n try {\n // Due to pagination and sorting limits on Quay Docker v2 API implementation we try the Quay v1 API first\n tags = await this.getTagsQuayRegistry(registryHost, dockerRepository);\n } catch (err) {\n // If we get a 401 Unauthorized error (v1 API requires separate auth for private images), fall back to Docker v2 API\n if (err.statusCode === 401) {\n logger.debug(\n { registryHost, dockerRepository },\n 'Quay v1 API unauthorized, falling back to Docker v2 API',\n );\n tags = await this.getDockerApiTags(registryHost, dockerRepository);\n } else {\n throw err;\n }\n }\n } else {\n tags = await this.getDockerApiTags(registryHost, dockerRepository);\n }\n return tags;\n } catch (_err) /* istanbul ignore next */ {\n const err = _err instanceof ExternalHostError ? _err.err : _err;\n\n if (\n (err.statusCode === 404 || err.message === PAGE_NOT_FOUND_ERROR) &&\n !dockerRepository.includes('/')\n ) {\n logger.debug(\n `Retrying Tags for ${registryHost}/${dockerRepository} using library/ prefix`,\n );\n return this.getTags(registryHost, 'library/' + dockerRepository);\n }\n // JFrog Artifactory - Retry handling when resolving Docker Official Images\n // These follow the format of {{registryHost}}{{jFrogRepository}}/library/{{dockerRepository}}\n if (\n (err.statusCode === 404 || err.message === PAGE_NOT_FOUND_ERROR) &&\n isArtifactoryServer(err.response) &&\n dockerRepository.split('/').length === 2\n ) {\n logger.debug(\n `JFrog Artifactory: Retrying Tags for ${registryHost}/${dockerRepository} using library/ path between JFrog virtual repository and image`,\n );\n\n const dockerRepositoryParts = dockerRepository.split('/');\n const jfrogRepository = dockerRepositoryParts[0];\n const dockerImage = dockerRepositoryParts[1];\n\n return this.getTags(\n registryHost,\n jfrogRepository + '/library/' + dockerImage,\n );\n }\n if (err.statusCode === 429 && isDockerHost(registryHost)) {\n logger.warn(\n { registryHost, dockerRepository, err },\n 'docker registry failure: too many requests',\n );\n throw new ExternalHostError(err);\n }\n if (err.statusCode >= 500 && err.statusCode < 600) {\n logger.warn(\n { registryHost, dockerRepository, err },\n 'docker registry failure: internal error',\n );\n throw new ExternalHostError(err);\n }\n const errorCodes = ['ECONNRESET', 'ETIMEDOUT'];\n if (errorCodes.includes(err.code)) {\n logger.warn(\n { registryHost, dockerRepository, err },\n 'docker registry connection failure',\n );\n throw new ExternalHostError(err);\n }\n if (isDockerHost(registryHost)) {\n logger.info({ err }, 'Docker Hub lookup failure');\n }\n throw _err;\n }\n }\n\n getTags(\n registryHost: string,\n dockerRepository: string,\n ): Promise<string[] | null> {\n return withCache(\n {\n namespace: 'datasource-docker-tags',\n key: `${registryHost}:${dockerRepository}`,\n },\n () => this._getTags(registryHost, dockerRepository),\n );\n }\n\n /**\n * docker.getDigest\n *\n * The `newValue` supplied here should be a valid tag for the docker image.\n *\n * This function will:\n * - Look up a sha256 digest for a tag on its registry\n * - Return the digest as a string\n */\n private async _getDigest(\n { registryUrl, lookupName, packageName, currentDigest }: DigestConfig,\n newValue?: string,\n ): Promise<string | null> {\n let registryHost: string;\n let dockerRepository: string;\n if (registryUrl && lookupName) {\n // Reuse the resolved values from getReleases()\n registryHost = registryUrl;\n dockerRepository = lookupName;\n } else {\n // Resolve values independently\n ({ registryHost, dockerRepository } = getRegistryRepository(\n packageName,\n registryUrl!,\n ));\n }\n logger.debug(\n // TODO: types (#22198)\n `getDigest(${registryHost}, ${dockerRepository}, ${newValue})`,\n );\n const newTag = isNonEmptyString(newValue) ? newValue : 'latest';\n let digest: string | null = null;\n try {\n let architecture: string | null | undefined = null;\n if (currentDigest && isDockerDigest(currentDigest)) {\n architecture = await this.getImageArchitecture(\n registryHost,\n dockerRepository,\n currentDigest,\n );\n }\n\n let manifestResponse: HttpResponse | null = null;\n if (!architecture) {\n manifestResponse = await this.getManifestResponse(\n registryHost,\n dockerRepository,\n newTag,\n 'head',\n );\n\n if (\n manifestResponse &&\n hasKey('docker-content-digest', manifestResponse.headers)\n ) {\n digest =\n (manifestResponse.headers['docker-content-digest'] as string) ||\n null;\n }\n }\n\n if (\n isNonEmptyString(architecture) ||\n (manifestResponse &&\n !hasKey('docker-content-digest', manifestResponse.headers))\n ) {\n logger.debug(\n { registryHost, dockerRepository },\n 'Architecture-specific digest or missing docker-content-digest header - pulling full manifest',\n );\n manifestResponse = await this.getManifestResponse(\n registryHost,\n dockerRepository,\n newTag,\n );\n\n if (architecture && manifestResponse) {\n const parsed = ManifestJson.safeParse(manifestResponse.body);\n /* istanbul ignore else: hard to test */\n if (parsed.success) {\n const manifestList = parsed.data;\n if (\n manifestList.mediaType ===\n 'application/vnd.docker.distribution.manifest.list.v2+json' ||\n manifestList.mediaType ===\n 'application/vnd.oci.image.index.v1+json'\n ) {\n for (const manifest of manifestList.manifests) {\n if (manifest.platform?.architecture === architecture) {\n digest = manifest.digest;\n break;\n }\n }\n // TODO: return null if no matching architecture digest found\n // https://github.com/renovatebot/renovate/discussions/22639\n } else if (\n hasKey('docker-content-digest', manifestResponse.headers)\n ) {\n // TODO: return null if no matching architecture, requires to fetch the config manifest\n // https://github.com/renovatebot/renovate/discussions/22639\n digest = manifestResponse.headers[\n 'docker-content-digest'\n ] as string;\n }\n } else {\n logger.debug(\n {\n registryHost,\n dockerRepository,\n newTag,\n body: manifestResponse.body,\n headers: manifestResponse.headers,\n err: parsed.error,\n },\n 'Failed to parse manifest response',\n );\n }\n }\n\n if (!digest) {\n logger.debug(\n { registryHost, dockerRepository, newTag },\n 'Extraction digest from manifest response body is deprecated',\n );\n digest = extractDigestFromResponseBody(manifestResponse!);\n }\n }\n\n if (\n !manifestResponse &&\n !dockerRepository.includes('/') &&\n !packageName.includes('/')\n ) {\n logger.debug(\n `Retrying Digest for ${registryHost}/${dockerRepository} using library/ prefix`,\n );\n return this.getDigest(\n {\n registryUrl,\n packageName: 'library/' + packageName,\n currentDigest,\n },\n newValue,\n );\n }\n\n if (manifestResponse) {\n // TODO: fix types (#22198)\n logger.debug(`Got docker digest ${digest!}`);\n }\n } catch (err) /* istanbul ignore next */ {\n if (err instanceof ExternalHostError) {\n throw err;\n }\n logger.debug(\n {\n err,\n packageName,\n newTag,\n },\n 'Unknown Error looking up docker image digest',\n );\n }\n return digest;\n }\n\n override getDigest(\n config: DigestConfig,\n newValue?: string,\n ): Promise<string | null> {\n const newTag = newValue ?? 'latest';\n const { registryHost, dockerRepository } = getRegistryRepository(\n config.packageName,\n config.registryUrl!,\n );\n const digest = config.currentDigest ? `@${config.currentDigest}` : '';\n return withCache(\n {\n namespace: 'datasource-docker-digest',\n key: `${registryHost}:${dockerRepository}:${newTag}${digest}`,\n fallback: true,\n },\n () => this._getDigest(config, newValue),\n );\n }\n\n private async _getDockerHubTags(\n dockerRepository: string,\n ): Promise<Release[] | null> {\n let url = `https://hub.docker.com/v2/repositories/${dockerRepository}/tags?page_size=1000&ordering=last_updated`;\n\n const cache = await DockerHubCache.init(dockerRepository);\n const maxPages = GlobalConfig.get('dockerMaxPages', 20);\n let page = 0,\n needNextPage = true;\n while (needNextPage && page < maxPages) {\n const { val, err } = await this.http\n .getJsonSafe(url, DockerHubTagsPage)\n .unwrap();\n\n if (err) {\n logger.debug({ err }, `Docker: error fetching data from DockerHub`);\n return null;\n }\n page++;\n const { results, next, count } = val;\n\n needNextPage = cache.reconcile(results, count);\n\n if (!next) {\n break;\n }\n\n url = next;\n }\n\n await cache.save();\n\n const items = cache.getItems();\n return items.map(\n ({ name: version, tag_last_pushed, digest: newDigest }) => {\n const release: Release = { version };\n\n const releaseTimestamp = asTimestamp(tag_last_pushed);\n if (releaseTimestamp) {\n release.releaseTimestamp = releaseTimestamp;\n }\n\n if (newDigest) {\n logger.once.debug(\n {\n documentationUrl:\n 'https://github.com/renovatebot/renovate/issues/38659',\n },\n 'Using the `tag_last_pushed` to determine the age of a release from Docker Hub. If this is a digest update, it may lead to inconsistent behaviour, showing the digest as newer than it actually is',\n );\n release.newDigest = newDigest;\n }\n\n return release;\n },\n );\n }\n\n getDockerHubTags(dockerRepository: string): Promise<Release[] | null> {\n return withCache(\n {\n namespace: 'datasource-docker-hub-tags',\n key: `${dockerRepository}`,\n },\n () => this._getDockerHubTags(dockerRepository),\n );\n }\n\n /**\n * docker.getReleases\n *\n * A docker image usually looks something like this: somehost.io/owner/repo:8.1.0-alpine\n * In the above:\n * - 'somehost.io' is the registry\n * - 'owner/repo' is the package name\n * - '8.1.0-alpine' is the tag\n *\n * This function will filter only tags that contain a semver version\n */\n private async _getReleases({\n packageName,\n registryUrl,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n const { registryHost, dockerRepository } = getRegistryRepository(\n packageName,\n registryUrl!,\n );\n\n type TagsResultType = AsyncResult<\n Release[],\n NonNullable<Error | 'tags-error' | 'dockerhub-error'>\n >;\n\n const getTags = (): TagsResultType =>\n Result.wrapNullable(\n this.getTags(registryHost, dockerRepository),\n 'tags-error' as const,\n ).transform((tags) => tags.map((version) => ({ version })));\n\n const getDockerHubTags = (): TagsResultType =>\n Result.wrapNullable(\n this.getDockerHubTags(dockerRepository),\n 'dockerhub-error' as const,\n ).catch(getTags);\n\n const tagsResult =\n registryHost === 'https://index.docker.io' &&\n !getEnv().RENOVATE_X_DOCKER_HUB_TAGS_DISABLE\n ? getDockerHubTags()\n : getTags();\n\n const { val: releases, err } = await tagsResult.unwrap();\n if (err instanceof Error) {\n throw err;\n } else if (err) {\n return null;\n }\n\n const ret: ReleaseResult = {\n registryUrl: registryHost,\n releases,\n };\n if (dockerRepository !== packageName) {\n // This will be reused later if a getDigest() call is made\n ret.lookupName = dockerRepository;\n }\n\n const tags = releases.map((release) => release.version);\n const latestTag = tags.includes('latest')\n ? 'latest'\n : (findLatestStable(tags) ?? tags[tags.length - 1]);\n\n /* v8 ignore next 3 -- TODO: add test */\n if (!latestTag) {\n return ret;\n }\n const labels = await this.getLabels(\n registryHost,\n dockerRepository,\n latestTag,\n );\n if (labels) {\n if (isNonEmptyString(labels[gitRefLabel])) {\n ret.gitRef = labels[gitRefLabel];\n }\n for (const label of sourceLabels) {\n if (isNonEmptyString(labels[label])) {\n ret.sourceUrl = labels[label];\n break;\n }\n }\n if (isNonEmptyString(labels[imageUrlLabel])) {\n ret.homepage = labels[imageUrlLabel];\n }\n }\n return ret;\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n const { registryHost, dockerRepository } = getRegistryRepository(\n config.packageName,\n config.registryUrl!,\n );\n return withCache(\n {\n namespace: 'datasource-docker-releases-v2',\n key: `${registryHost}:${dockerRepository}`,\n cacheable: registryHost === 'https://index.docker.io',\n fallback: true,\n },\n () => this._getReleases(config),\n );\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["dockerVersioningId","HttpError"],"sources":["../../../../lib/modules/datasource/docker/index.ts"],"sourcesContent":["import { isNonEmptyString } from '@sindresorhus/is';\nimport { GlobalConfig } from '../../../config/global.ts';\nimport { PAGE_NOT_FOUND_ERROR } from '../../../constants/error-messages.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 { getEnv } from '../../../util/env.ts';\nimport { memCacheProvider } from '../../../util/http/cache/memory-http-cache-provider.ts';\nimport { HttpError } from '../../../util/http/index.ts';\nimport type { HttpResponse } from '../../../util/http/types.ts';\nimport { hasKey } from '../../../util/object.ts';\nimport { type AsyncResult, Result } from '../../../util/result.ts';\nimport { isDockerDigest } from '../../../util/string-match.ts';\nimport { asTimestamp } from '../../../util/timestamp.ts';\nimport {\n ensurePathPrefix,\n joinUrlParts,\n parseLinkHeader,\n} from '../../../util/url.ts';\nimport { id as dockerVersioningId } from '../../versioning/docker/index.ts';\nimport { Datasource } from '../datasource.ts';\nimport type {\n DigestConfig,\n GetReleasesConfig,\n Release,\n ReleaseResult,\n} from '../types.ts';\nimport { isArtifactoryServer } from '../util.ts';\nimport {\n DOCKER_HUB,\n dockerDatasourceId,\n extractDigestFromResponseBody,\n findHelmSourceUrl,\n findLatestStable,\n getAuthHeaders,\n getRegistryRepository,\n gitRefLabel,\n imageUrlLabel,\n isDockerHost,\n sourceLabel,\n sourceLabels,\n} from './common.ts';\nimport { DockerHubCache } from './dockerhub-cache.ts';\nimport { ecrPublicRegex, ecrRegex, isECRMaxResultsError } from './ecr.ts';\nimport type { DistributionManifest, OciImageManifest } from './schema.ts';\nimport {\n DockerHubTagsPage,\n ManifestJson,\n OciHelmConfig,\n OciImageConfig,\n} from './schema.ts';\n\nconst defaultConfig = {\n commitMessageTopic: '{{{depName}}} Docker tag',\n commitMessageExtra:\n 'to {{#if isPinDigest}}{{{newDigestShort}}}{{else}}{{#if isMajor}}{{{prettyNewMajor}}}{{else}}{{{prettyNewVersion}}}{{/if}}{{/if}}',\n digest: {\n branchTopic: '{{{depNameSanitized}}}-{{{currentValue}}}',\n commitMessageExtra: 'to {{newDigestShort}}',\n commitMessageTopic:\n '{{{depName}}}{{#if currentValue}}:{{{currentValue}}}{{/if}} Docker digest',\n group: {\n commitMessageTopic: '{{{groupName}}}',\n commitMessageExtra: '',\n },\n },\n pin: {\n commitMessageExtra: '',\n groupName: 'Docker digests',\n group: {\n commitMessageTopic: '{{{groupName}}}',\n branchTopic: 'digests-pin',\n },\n },\n};\n\nexport class DockerDatasource extends Datasource {\n static readonly id = dockerDatasourceId;\n\n override readonly defaultVersioning = dockerVersioningId;\n\n override readonly defaultRegistryUrls = [DOCKER_HUB];\n\n override readonly defaultConfig = defaultConfig;\n\n override readonly releaseTimestampSupport = true;\n override readonly releaseTimestampNote =\n 'Only supported on Docker Hub: The release timestamp is determined from the `tag_last_pushed` field in the results. **NOTE**: Currently, digests will receive the same release timestamp as the `tag_last_pushed`, which means that digests may appear newer than they are - see https://github.com/renovatebot/renovate/issues/38659';\n override readonly sourceUrlSupport = 'package';\n override readonly sourceUrlNote =\n 'The source URL is determined from the `org.opencontainers.image.source` and `org.label-schema.vcs-url` labels present in the metadata of the **latest stable** image found on the Docker registry.';\n\n constructor() {\n super(DockerDatasource.id);\n }\n\n // TODO: debug why quay throws errors (#9612)\n private async getManifestResponse(\n registryHost: string,\n dockerRepository: string,\n tag: string,\n mode: 'head' | 'getText' = 'getText',\n ): Promise<HttpResponse | null> {\n logger.debug(\n `getManifestResponse(${registryHost}, ${dockerRepository}, ${tag}, ${mode})`,\n );\n try {\n const headers = await getAuthHeaders(\n this.http,\n registryHost,\n dockerRepository,\n );\n if (!headers) {\n logger.warn('No docker auth found - returning');\n return null;\n }\n headers.accept = [\n 'application/vnd.docker.distribution.manifest.list.v2+json',\n 'application/vnd.docker.distribution.manifest.v2+json',\n 'application/vnd.oci.image.manifest.v1+json',\n 'application/vnd.oci.image.index.v1+json',\n ].join(', ');\n const url = `${registryHost}/v2/${dockerRepository}/manifests/${tag}`;\n const manifestResponse = await this.http[mode](url, {\n headers,\n noAuth: true,\n cacheProvider: memCacheProvider,\n });\n return manifestResponse;\n } catch (err) /* istanbul ignore next */ {\n if (err instanceof ExternalHostError) {\n throw err;\n }\n if (err.statusCode === 401) {\n logger.debug(\n { registryHost, dockerRepository },\n 'Unauthorized docker lookup',\n );\n logger.debug({ err });\n return null;\n }\n if (err.statusCode === 404) {\n logger.debug(\n {\n err,\n registryHost,\n dockerRepository,\n tag,\n },\n 'Docker Manifest is unknown',\n );\n return null;\n }\n if (err.statusCode === 429 && isDockerHost(registryHost)) {\n throw new ExternalHostError(err);\n }\n if (err.statusCode >= 500 && err.statusCode < 600) {\n throw new ExternalHostError(err);\n }\n if (err.code === 'ETIMEDOUT') {\n logger.debug(\n { registryHost },\n 'Timeout when attempting to connect to docker registry',\n );\n logger.debug({ err });\n return null;\n }\n logger.debug(\n {\n err,\n registryHost,\n dockerRepository,\n tag,\n },\n 'Unknown Error looking up docker manifest',\n );\n return null;\n }\n }\n\n private async _getImageConfig(\n registryHost: string,\n dockerRepository: string,\n configDigest: string,\n ): Promise<HttpResponse<OciImageConfig> | undefined> {\n logger.trace(\n `getImageConfig(${registryHost}, ${dockerRepository}, ${configDigest})`,\n );\n\n const headers = await getAuthHeaders(\n this.http,\n registryHost,\n dockerRepository,\n );\n /* v8 ignore next 4 -- should never happen */\n if (!headers) {\n logger.warn('No docker auth found - returning');\n return undefined;\n }\n const url = joinUrlParts(\n registryHost,\n 'v2',\n dockerRepository,\n 'blobs',\n configDigest,\n );\n return await this.http.getJson(\n url,\n {\n headers,\n noAuth: true,\n },\n OciImageConfig,\n );\n }\n\n getImageConfig(\n registryHost: string,\n dockerRepository: string,\n configDigest: string,\n ): Promise<HttpResponse<OciImageConfig> | undefined> {\n return withCache(\n {\n namespace: 'datasource-docker-imageconfig',\n key: `${registryHost}:${dockerRepository}@${configDigest}`,\n ttlMinutes: 1440 * 28,\n },\n () => this._getImageConfig(registryHost, dockerRepository, configDigest),\n );\n }\n\n private async _getHelmConfig(\n registryHost: string,\n dockerRepository: string,\n configDigest: string,\n ): Promise<HttpResponse<OciHelmConfig> | undefined> {\n logger.trace(\n `getImageConfig(${registryHost}, ${dockerRepository}, ${configDigest})`,\n );\n\n const headers = await getAuthHeaders(\n this.http,\n registryHost,\n dockerRepository,\n );\n /* v8 ignore next 4 -- should never happen */\n if (!headers) {\n logger.warn('No docker auth found - returning');\n return undefined;\n }\n const url = joinUrlParts(\n registryHost,\n 'v2',\n dockerRepository,\n 'blobs',\n configDigest,\n );\n return await this.http.getJson(\n url,\n {\n headers,\n noAuth: true,\n },\n OciHelmConfig,\n );\n }\n\n getHelmConfig(\n registryHost: string,\n dockerRepository: string,\n configDigest: string,\n ): Promise<HttpResponse<OciHelmConfig> | undefined> {\n return withCache(\n {\n namespace: 'datasource-docker-imageconfig',\n key: `${registryHost}:${dockerRepository}@${configDigest}`,\n ttlMinutes: 1440 * 28,\n },\n () => this._getHelmConfig(registryHost, dockerRepository, configDigest),\n );\n }\n\n private async getConfigDigest(\n registry: string,\n dockerRepository: string,\n tag: string,\n ): Promise<string | null> {\n return (\n (await this.getManifest(registry, dockerRepository, tag))?.config\n ?.digest ?? null\n );\n }\n\n private async getManifest(\n registry: string,\n dockerRepository: string,\n tag: string,\n ): Promise<OciImageManifest | DistributionManifest | null> {\n const manifestResponse = await this.getManifestResponse(\n registry,\n dockerRepository,\n tag,\n );\n\n // If getting the manifest fails here, then abort\n // This means that the latest tag doesn't have a manifest, which shouldn't\n // be possible\n /* v8 ignore next 3 -- should never happen */\n if (!manifestResponse) {\n return null;\n }\n\n // Softfail on invalid manifests\n const parsed = ManifestJson.safeParse(manifestResponse.body);\n if (!parsed.success) {\n logger.debug(\n {\n registry,\n dockerRepository,\n tag,\n body: manifestResponse.body,\n headers: manifestResponse.headers,\n err: parsed.error,\n },\n 'Invalid manifest response',\n );\n return null;\n }\n\n const manifest = parsed.data;\n\n switch (manifest.mediaType) {\n case 'application/vnd.docker.distribution.manifest.v2+json':\n case 'application/vnd.oci.image.manifest.v1+json':\n return manifest;\n case 'application/vnd.docker.distribution.manifest.list.v2+json':\n case 'application/vnd.oci.image.index.v1+json':\n if (!manifest.manifests.length) {\n logger.debug(\n { manifest },\n 'Invalid manifest list with no manifests - returning',\n );\n return null;\n }\n logger.trace(\n { registry, dockerRepository, tag },\n 'Found manifest list, using first image',\n );\n return this.getManifest(\n registry,\n dockerRepository,\n manifest.manifests[0].digest,\n );\n // istanbul ignore next: can't happen\n default:\n return null;\n }\n }\n\n private async _getImageArchitecture(\n registryHost: string,\n dockerRepository: string,\n currentDigest: string,\n ): Promise<string | null | undefined> {\n try {\n let manifestResponse: HttpResponse<string> | null;\n\n try {\n manifestResponse = await this.getManifestResponse(\n registryHost,\n dockerRepository,\n currentDigest,\n 'head',\n );\n } catch (_err) {\n const err =\n _err instanceof ExternalHostError\n ? _err.err\n : /* istanbul ignore next: can never happen */ _err;\n\n if (\n typeof err.statusCode === 'number' &&\n err.statusCode >= 500 &&\n err.statusCode < 600\n ) {\n // querying the digest manifest for a non existent image leads to a 500 statusCode\n return null;\n }\n\n /* istanbul ignore next */\n throw _err;\n }\n\n if (\n manifestResponse?.headers['content-type'] !==\n 'application/vnd.docker.distribution.manifest.v2+json' &&\n manifestResponse?.headers['content-type'] !==\n 'application/vnd.oci.image.manifest.v1+json'\n ) {\n return null;\n }\n\n const configDigest = await this.getConfigDigest(\n registryHost,\n dockerRepository,\n currentDigest,\n );\n if (!configDigest) {\n return null;\n }\n\n const configResponse = await this.getImageConfig(\n registryHost,\n dockerRepository,\n configDigest,\n );\n\n // TODO: fix me, architecture is required in spec\n if (\n configResponse &&\n ('config' in configResponse.body ||\n 'architecture' in configResponse.body)\n ) {\n const architecture = configResponse.body.architecture ?? null;\n logger.debug(\n `Current digest ${currentDigest} relates to architecture ${\n architecture ?? 'null'\n }`,\n );\n\n return architecture;\n }\n } catch (err) /* istanbul ignore next */ {\n if (err.statusCode !== 404 || err.message === PAGE_NOT_FOUND_ERROR) {\n throw err;\n }\n logger.debug(\n { registryHost, dockerRepository, currentDigest, err },\n 'Unknown error getting image architecture',\n );\n }\n\n return undefined;\n }\n\n getImageArchitecture(\n registryHost: string,\n dockerRepository: string,\n currentDigest: string,\n ): Promise<string | null | undefined> {\n return withCache(\n {\n namespace: 'datasource-docker-architecture',\n key: `${registryHost}:${dockerRepository}@${currentDigest}`,\n ttlMinutes: 1440 * 28,\n },\n () =>\n this._getImageArchitecture(\n registryHost,\n dockerRepository,\n currentDigest,\n ),\n );\n }\n\n /*\n * docker.getLabels\n *\n * This function will:\n * - Return the labels for the requested image\n */\n private async _getLabels(\n registryHost: string,\n dockerRepository: string,\n tag: string,\n ): Promise<Record<string, string> | undefined> {\n logger.debug(`getLabels(${registryHost}, ${dockerRepository}, ${tag})`);\n // Skip Docker Hub image if RENOVATE_X_DOCKER_HUB_DISABLE_LABEL_LOOKUP is set\n if (\n getEnv().RENOVATE_X_DOCKER_HUB_DISABLE_LABEL_LOOKUP &&\n registryHost === 'https://index.docker.io'\n ) {\n logger.debug(\n 'Docker Hub image - skipping label lookup due to RENOVATE_X_DOCKER_HUB_DISABLE_LABEL_LOOKUP',\n );\n return {};\n }\n // Docker Hub library images don't have labels we need\n if (\n registryHost === 'https://index.docker.io' &&\n dockerRepository.startsWith('library/')\n ) {\n logger.debug('Docker Hub library image - skipping label lookup');\n return {};\n }\n try {\n let labels: Record<string, string> | undefined = {};\n const manifest = await this.getManifest(\n registryHost,\n dockerRepository,\n tag,\n );\n\n if (!manifest) {\n logger.debug(\n { registryHost, dockerRepository, tag },\n 'No manifest found',\n );\n return undefined;\n }\n\n if ('annotations' in manifest && manifest.annotations) {\n labels = manifest.annotations;\n }\n\n switch (manifest.config.mediaType) {\n case 'application/vnd.cncf.helm.config.v1+json': {\n if (labels[sourceLabel]) {\n // we already have the source url, so no need to pull the config\n return labels;\n }\n const configResponse = await this.getHelmConfig(\n registryHost,\n dockerRepository,\n manifest.config.digest,\n );\n\n if (configResponse) {\n // Helm chart\n const url = findHelmSourceUrl(configResponse.body);\n if (url) {\n labels[sourceLabel] = url;\n }\n }\n break;\n }\n case 'application/vnd.oci.image.config.v1+json':\n case 'application/vnd.docker.container.image.v1+json': {\n if (labels[sourceLabel] && labels[gitRefLabel]) {\n // we already have the source url, so no need to pull the config\n return labels;\n }\n const configResponse = await this.getImageConfig(\n registryHost,\n dockerRepository,\n manifest.config.digest,\n );\n\n /* v8 ignore next 3 -- should never happen */\n if (!configResponse) {\n return labels;\n }\n\n const body = configResponse.body;\n if (body.config) {\n labels = { ...labels, ...body.config.Labels };\n } else {\n logger.debug(\n { headers: configResponse.headers, body },\n `manifest blob response body missing the \"config\" property`,\n );\n }\n break;\n }\n }\n\n if (labels) {\n logger.debug(\n {\n labels,\n },\n 'found labels in manifest',\n );\n }\n return labels;\n } catch (err) /* istanbul ignore next: should be tested in future */ {\n if (err instanceof ExternalHostError) {\n throw err;\n }\n if (err.statusCode === 400 || err.statusCode === 401) {\n logger.debug(\n { registryHost, dockerRepository, err },\n 'Unauthorized docker lookup',\n );\n } else if (err.statusCode === 404) {\n logger.warn(\n {\n err,\n registryHost,\n dockerRepository,\n tag,\n },\n 'Config Manifest is unknown',\n );\n } else if (err.statusCode === 429 && isDockerHost(registryHost)) {\n logger.warn({ err }, 'docker registry failure: too many requests');\n } else if (err.statusCode >= 500 && err.statusCode < 600) {\n logger.debug(\n {\n err,\n registryHost,\n dockerRepository,\n tag,\n },\n 'docker registry failure: internal error',\n );\n } else if (\n err.code === 'ERR_TLS_CERT_ALTNAME_INVALID' ||\n err.code === 'ETIMEDOUT'\n ) {\n logger.debug(\n { registryHost, err },\n 'Error connecting to docker registry',\n );\n } else if (registryHost === 'https://quay.io') {\n // istanbul ignore next\n logger.debug(\n 'Ignoring quay.io errors until they fully support v2 schema',\n );\n } else {\n logger.info(\n { registryHost, dockerRepository, tag, err },\n 'Unknown error getting Docker labels',\n );\n }\n return {};\n }\n }\n\n getLabels(\n registryHost: string,\n dockerRepository: string,\n tag: string,\n ): Promise<Record<string, string> | undefined> {\n return withCache(\n {\n namespace: 'datasource-docker-labels',\n key: `${registryHost}:${dockerRepository}:${tag}`,\n ttlMinutes: 24 * 60,\n },\n () => this._getLabels(registryHost, dockerRepository, tag),\n );\n }\n\n private async getTagsQuayRegistry(\n registry: string,\n repository: string,\n ): Promise<string[]> {\n let tags: string[] = [];\n const limit = 100;\n\n const pageUrl = (page: number): string =>\n `${registry}/api/v1/repository/${repository}/tag/?limit=${limit}&page=${page}&onlyActiveTags=true`;\n\n let page = 1;\n let url: string | null = pageUrl(page);\n while (url && page <= 20) {\n interface QuayRestDockerTags {\n tags: {\n name: string;\n }[];\n has_additional: boolean;\n }\n\n // typescript issue :-/\n // oxlint-disable typescript/no-unnecessary-type-assertion\n const res = (await this.http.getJsonUnchecked<QuayRestDockerTags>(\n url,\n )) as HttpResponse<QuayRestDockerTags>;\n // oxlint-enable typescript/no-unnecessary-type-assertion\n const pageTags = res.body.tags.map((tag) => tag.name);\n tags = tags.concat(pageTags);\n page += 1;\n url = res.body.has_additional ? pageUrl(page) : null;\n }\n return tags;\n }\n\n private async getDockerApiTags(\n registryHost: string,\n dockerRepository: string,\n ): Promise<string[] | null> {\n let tags: string[] = [];\n // AWS ECR limits the maximum number of results to 1000\n // See https://docs.aws.amazon.com/AmazonECR/latest/APIReference/API_DescribeRepositories.html#ECR-DescribeRepositories-request-maxResults\n // See https://docs.aws.amazon.com/AmazonECRPublic/latest/APIReference/API_DescribeRepositories.html#ecrpublic-DescribeRepositories-request-maxResults\n const limit =\n ecrRegex.test(registryHost) || ecrPublicRegex.test(registryHost)\n ? 1000\n : 10000;\n let url: string | null =\n `${registryHost}/${dockerRepository}/tags/list?n=${limit}`;\n url = ensurePathPrefix(url, '/v2');\n const headers = await getAuthHeaders(\n this.http,\n registryHost,\n dockerRepository,\n url,\n );\n if (!headers) {\n logger.debug('Failed to get authHeaders for getTags lookup');\n return null;\n }\n let page = 0;\n const hostsNeedingAllPages = [\n 'https://ghcr.io', // GHCR sorts from oldest to newest, so we need to get all pages\n 'https://quay.io', // Quay sorts from oldest to newest, so we need to get all pages\n ];\n const pages = hostsNeedingAllPages.includes(registryHost)\n ? 1000\n : GlobalConfig.get('dockerMaxPages', 20);\n logger.trace({ registryHost, dockerRepository, pages }, 'docker.getTags');\n let foundMaxResultsError = false;\n do {\n let res: HttpResponse<{ tags: string[] }>;\n try {\n res = await this.http.getJsonUnchecked<{ tags: string[] }>(url, {\n headers,\n noAuth: true,\n });\n } catch (err) {\n if (\n !foundMaxResultsError &&\n err instanceof HttpError &&\n isECRMaxResultsError(err)\n ) {\n const maxResults = 1000;\n url = `${registryHost}/${dockerRepository}/tags/list?n=${maxResults}`;\n url = ensurePathPrefix(url, '/v2');\n foundMaxResultsError = true;\n continue;\n }\n throw err;\n }\n tags = tags.concat(res.body.tags);\n const linkHeader = parseLinkHeader(res.headers.link);\n if (isArtifactoryServer(res)) {\n // Artifactory bug: next link comes back without virtual-repo prefix (RTFACT-18971)\n if (linkHeader?.next?.last) {\n // parse the current URL, strip any old \"last\" param, then set the new one\n const parsed: URL = new URL(url);\n parsed.searchParams.delete('last');\n parsed.searchParams.set('last', linkHeader.next.last);\n url = parsed.href;\n } else {\n url = null;\n }\n } else if (linkHeader?.next?.url) {\n // for the normal case we can still use URL to resolve relative-next\n url = new URL(linkHeader.next.url, url).href;\n } else {\n url = null;\n }\n page += 1;\n } while (url && page < pages);\n return tags;\n }\n\n private async _getTags(\n registryHost: string,\n dockerRepository: string,\n ): Promise<string[] | null> {\n try {\n const isQuay = registryHost === 'https://quay.io';\n let tags: string[] | null;\n if (isQuay) {\n try {\n // Due to pagination and sorting limits on Quay Docker v2 API implementation we try the Quay v1 API first\n tags = await this.getTagsQuayRegistry(registryHost, dockerRepository);\n } catch (err) {\n // If we get a 401 Unauthorized error (v1 API requires separate auth for private images), fall back to Docker v2 API\n if (err.statusCode === 401) {\n logger.debug(\n { registryHost, dockerRepository },\n 'Quay v1 API unauthorized, falling back to Docker v2 API',\n );\n tags = await this.getDockerApiTags(registryHost, dockerRepository);\n } else {\n throw err;\n }\n }\n } else {\n tags = await this.getDockerApiTags(registryHost, dockerRepository);\n }\n return tags;\n } catch (_err) /* istanbul ignore next */ {\n const err = _err instanceof ExternalHostError ? _err.err : _err;\n\n if (\n (err.statusCode === 404 || err.message === PAGE_NOT_FOUND_ERROR) &&\n !dockerRepository.includes('/')\n ) {\n logger.debug(\n `Retrying Tags for ${registryHost}/${dockerRepository} using library/ prefix`,\n );\n return this.getTags(registryHost, 'library/' + dockerRepository);\n }\n // JFrog Artifactory - Retry handling when resolving Docker Official Images\n // These follow the format of {{registryHost}}{{jFrogRepository}}/library/{{dockerRepository}}\n if (\n (err.statusCode === 404 || err.message === PAGE_NOT_FOUND_ERROR) &&\n isArtifactoryServer(err.response) &&\n dockerRepository.split('/').length === 2\n ) {\n logger.debug(\n `JFrog Artifactory: Retrying Tags for ${registryHost}/${dockerRepository} using library/ path between JFrog virtual repository and image`,\n );\n\n const dockerRepositoryParts = dockerRepository.split('/');\n const jfrogRepository = dockerRepositoryParts[0];\n const dockerImage = dockerRepositoryParts[1];\n\n return this.getTags(\n registryHost,\n jfrogRepository + '/library/' + dockerImage,\n );\n }\n if (err.statusCode === 429 && isDockerHost(registryHost)) {\n logger.warn(\n { registryHost, dockerRepository, err },\n 'docker registry failure: too many requests',\n );\n throw new ExternalHostError(err);\n }\n if (err.statusCode >= 500 && err.statusCode < 600) {\n logger.warn(\n { registryHost, dockerRepository, err },\n 'docker registry failure: internal error',\n );\n throw new ExternalHostError(err);\n }\n const errorCodes = ['ECONNRESET', 'ETIMEDOUT'];\n if (errorCodes.includes(err.code)) {\n logger.warn(\n { registryHost, dockerRepository, err },\n 'docker registry connection failure',\n );\n throw new ExternalHostError(err);\n }\n if (isDockerHost(registryHost)) {\n logger.info({ err }, 'Docker Hub lookup failure');\n }\n throw _err;\n }\n }\n\n getTags(\n registryHost: string,\n dockerRepository: string,\n ): Promise<string[] | null> {\n return withCache(\n {\n namespace: 'datasource-docker-tags',\n key: `${registryHost}:${dockerRepository}`,\n },\n () => this._getTags(registryHost, dockerRepository),\n );\n }\n\n /**\n * docker.getDigest\n *\n * The `newValue` supplied here should be a valid tag for the docker image.\n *\n * This function will:\n * - Look up a sha256 digest for a tag on its registry\n * - Return the digest as a string\n */\n private async _getDigest(\n { registryUrl, lookupName, packageName, currentDigest }: DigestConfig,\n newValue?: string,\n ): Promise<string | null> {\n let registryHost: string;\n let dockerRepository: string;\n if (registryUrl && lookupName) {\n // Reuse the resolved values from getReleases()\n registryHost = registryUrl;\n dockerRepository = lookupName;\n } else {\n // Resolve values independently\n ({ registryHost, dockerRepository } = getRegistryRepository(\n packageName,\n registryUrl!,\n ));\n }\n logger.debug(\n // TODO: types (#22198)\n `getDigest(${registryHost}, ${dockerRepository}, ${newValue})`,\n );\n const newTag = isNonEmptyString(newValue) ? newValue : 'latest';\n let digest: string | null = null;\n try {\n let architecture: string | null | undefined = null;\n if (currentDigest && isDockerDigest(currentDigest)) {\n architecture = await this.getImageArchitecture(\n registryHost,\n dockerRepository,\n currentDigest,\n );\n }\n\n let manifestResponse: HttpResponse | null = null;\n if (!architecture) {\n manifestResponse = await this.getManifestResponse(\n registryHost,\n dockerRepository,\n newTag,\n 'head',\n );\n\n if (\n manifestResponse &&\n hasKey('docker-content-digest', manifestResponse.headers)\n ) {\n digest =\n (manifestResponse.headers['docker-content-digest'] as string) ||\n null;\n }\n }\n\n if (\n isNonEmptyString(architecture) ||\n (manifestResponse &&\n !hasKey('docker-content-digest', manifestResponse.headers))\n ) {\n logger.debug(\n { registryHost, dockerRepository },\n 'Architecture-specific digest or missing docker-content-digest header - pulling full manifest',\n );\n manifestResponse = await this.getManifestResponse(\n registryHost,\n dockerRepository,\n newTag,\n );\n\n if (architecture && manifestResponse) {\n const parsed = ManifestJson.safeParse(manifestResponse.body);\n /* istanbul ignore else: hard to test */\n if (parsed.success) {\n const manifestList = parsed.data;\n if (\n manifestList.mediaType ===\n 'application/vnd.docker.distribution.manifest.list.v2+json' ||\n manifestList.mediaType ===\n 'application/vnd.oci.image.index.v1+json'\n ) {\n for (const manifest of manifestList.manifests) {\n if (manifest.platform?.architecture === architecture) {\n digest = manifest.digest;\n break;\n }\n }\n // TODO: return null if no matching architecture digest found\n // https://github.com/renovatebot/renovate/discussions/22639\n } else if (\n hasKey('docker-content-digest', manifestResponse.headers)\n ) {\n // TODO: return null if no matching architecture, requires to fetch the config manifest\n // https://github.com/renovatebot/renovate/discussions/22639\n digest = manifestResponse.headers[\n 'docker-content-digest'\n ] as string;\n }\n } else {\n logger.debug(\n {\n registryHost,\n dockerRepository,\n newTag,\n body: manifestResponse.body,\n headers: manifestResponse.headers,\n err: parsed.error,\n },\n 'Failed to parse manifest response',\n );\n }\n }\n\n if (!digest) {\n logger.debug(\n { registryHost, dockerRepository, newTag },\n 'Extraction digest from manifest response body is deprecated',\n );\n digest = extractDigestFromResponseBody(manifestResponse!);\n }\n }\n\n if (\n !manifestResponse &&\n !dockerRepository.includes('/') &&\n !packageName.includes('/')\n ) {\n logger.debug(\n `Retrying Digest for ${registryHost}/${dockerRepository} using library/ prefix`,\n );\n return this.getDigest(\n {\n registryUrl,\n packageName: 'library/' + packageName,\n currentDigest,\n },\n newValue,\n );\n }\n\n if (manifestResponse) {\n // TODO: fix types (#22198)\n logger.debug(`Got docker digest ${digest!}`);\n }\n } catch (err) /* istanbul ignore next */ {\n if (err instanceof ExternalHostError) {\n throw err;\n }\n logger.debug(\n {\n err,\n packageName,\n newTag,\n },\n 'Unknown Error looking up docker image digest',\n );\n }\n return digest;\n }\n\n override getDigest(\n config: DigestConfig,\n newValue?: string,\n ): Promise<string | null> {\n const newTag = newValue ?? 'latest';\n const { registryHost, dockerRepository } = getRegistryRepository(\n config.packageName,\n config.registryUrl!,\n );\n const digest = config.currentDigest ? `@${config.currentDigest}` : '';\n return withCache(\n {\n namespace: 'datasource-docker-digest',\n key: `${registryHost}:${dockerRepository}:${newTag}${digest}`,\n fallback: true,\n },\n () => this._getDigest(config, newValue),\n );\n }\n\n private async _getDockerHubTags(\n dockerRepository: string,\n ): Promise<Release[] | null> {\n let url = `https://hub.docker.com/v2/repositories/${dockerRepository}/tags?page_size=1000&ordering=last_updated`;\n\n const cache = await DockerHubCache.init(dockerRepository);\n const maxPages = GlobalConfig.get('dockerMaxPages', 20);\n let page = 0,\n needNextPage = true;\n while (needNextPage && page < maxPages) {\n const { val, err } = await this.http\n .getJsonSafe(url, DockerHubTagsPage)\n .unwrap();\n\n if (err) {\n logger.debug({ err }, `Docker: error fetching data from DockerHub`);\n return null;\n }\n page++;\n const { results, next, count } = val;\n\n needNextPage = cache.reconcile(results, count);\n\n if (!next) {\n break;\n }\n\n url = next;\n }\n\n await cache.save();\n\n const items = cache.getItems();\n return items.map(\n ({ name: version, tag_last_pushed, digest: newDigest }) => {\n const release: Release = { version };\n\n const releaseTimestamp = asTimestamp(tag_last_pushed);\n if (releaseTimestamp) {\n release.releaseTimestamp = releaseTimestamp;\n }\n\n if (newDigest) {\n logger.once.debug(\n {\n documentationUrl:\n 'https://github.com/renovatebot/renovate/issues/38659',\n },\n 'Using the `tag_last_pushed` to determine the age of a release from Docker Hub. If this is a digest update, it may lead to inconsistent behaviour, showing the digest as newer than it actually is',\n );\n release.newDigest = newDigest;\n }\n\n return release;\n },\n );\n }\n\n getDockerHubTags(dockerRepository: string): Promise<Release[] | null> {\n return withCache(\n {\n namespace: 'datasource-docker-hub-tags',\n key: `${dockerRepository}`,\n },\n () => this._getDockerHubTags(dockerRepository),\n );\n }\n\n /**\n * docker.getReleases\n *\n * A docker image usually looks something like this: somehost.io/owner/repo:8.1.0-alpine\n * In the above:\n * - 'somehost.io' is the registry\n * - 'owner/repo' is the package name\n * - '8.1.0-alpine' is the tag\n *\n * This function will filter only tags that contain a semver version\n */\n private async _getReleases({\n packageName,\n registryUrl,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n const { registryHost, dockerRepository } = getRegistryRepository(\n packageName,\n registryUrl!,\n );\n\n type TagsResultType = AsyncResult<\n Release[],\n NonNullable<Error | 'tags-error' | 'dockerhub-error'>\n >;\n\n const getTags = (): TagsResultType =>\n Result.wrapNullable(\n this.getTags(registryHost, dockerRepository),\n 'tags-error' as const,\n ).transform((tags) => tags.map((version) => ({ version })));\n\n const getDockerHubTags = (): TagsResultType =>\n Result.wrapNullable(\n this.getDockerHubTags(dockerRepository),\n 'dockerhub-error' as const,\n ).catch(getTags);\n\n const tagsResult =\n registryHost === 'https://index.docker.io' &&\n !getEnv().RENOVATE_X_DOCKER_HUB_TAGS_DISABLE\n ? getDockerHubTags()\n : getTags();\n\n const { val: releases, err } = await tagsResult.unwrap();\n if (err instanceof Error) {\n throw err;\n } else if (err) {\n return null;\n }\n\n const ret: ReleaseResult = {\n registryUrl: registryHost,\n releases,\n };\n if (dockerRepository !== packageName) {\n // This will be reused later if a getDigest() call is made\n ret.lookupName = dockerRepository;\n }\n\n const tags = releases.map((release) => release.version);\n const latestTag = tags.includes('latest')\n ? 'latest'\n : (findLatestStable(tags) ?? tags[tags.length - 1]);\n\n /* v8 ignore next 3 -- TODO: add test */\n if (!latestTag) {\n return ret;\n }\n const labels = await this.getLabels(\n registryHost,\n dockerRepository,\n latestTag,\n );\n if (labels) {\n if (isNonEmptyString(labels[gitRefLabel])) {\n ret.gitRef = labels[gitRefLabel];\n }\n for (const label of sourceLabels) {\n if (isNonEmptyString(labels[label])) {\n ret.sourceUrl = labels[label];\n break;\n }\n }\n if (isNonEmptyString(labels[imageUrlLabel])) {\n ret.homepage = labels[imageUrlLabel];\n }\n }\n return ret;\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n const { registryHost, dockerRepository } = getRegistryRepository(\n config.packageName,\n config.registryUrl!,\n );\n return withCache(\n {\n namespace: 'datasource-docker-releases-v2',\n key: `${registryHost}:${dockerRepository}`,\n cacheable: registryHost === 'https://index.docker.io',\n fallback: true,\n },\n () => this._getReleases(config),\n );\n }\n}\n"],"mappings":"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"}
|
|
@@ -68,6 +68,7 @@ var GoDatasource = class GoDatasource extends Datasource {
|
|
|
68
68
|
case GithubTagsDatasource.id: return this.direct.github.getDigest(source, tag);
|
|
69
69
|
case BitbucketTagsDatasource.id: return this.direct.bitbucket.getDigest(source, tag);
|
|
70
70
|
case GitlabTagsDatasource.id: return this.direct.gitlab.getDigest(source, tag);
|
|
71
|
+
/* v8 ignore next 3: can never happen, makes lint happy */
|
|
71
72
|
default: return null;
|
|
72
73
|
}
|
|
73
74
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["semverId"],"sources":["../../../../lib/modules/datasource/go/index.ts"],"sourcesContent":["import { isString } from '@sindresorhus/is';\nimport { logger } from '../../../logger/index.ts';\nimport { withCache } from '../../../util/cache/package/with-cache.ts';\nimport { getEnv } from '../../../util/env.ts';\nimport { regEx } from '../../../util/regex.ts';\nimport { addSecretForSanitizing } from '../../../util/sanitize.ts';\nimport { parseUrl } from '../../../util/url.ts';\nimport { id as semverId } from '../../versioning/semver/index.ts';\nimport { BitbucketTagsDatasource } from '../bitbucket-tags/index.ts';\nimport { Datasource } from '../datasource.ts';\nimport { ForgejoTagsDatasource } from '../forgejo-tags/index.ts';\nimport { GitTagsDatasource } from '../git-tags/index.ts';\nimport { GiteaTagsDatasource } from '../gitea-tags/index.ts';\nimport { GithubTagsDatasource } from '../github-tags/index.ts';\nimport { GitlabTagsDatasource } from '../gitlab-tags/index.ts';\nimport type {\n DigestConfig,\n GetReleasesConfig,\n ReleaseResult,\n} from '../types.ts';\nimport { BaseGoDatasource } from './base.ts';\nimport { parseGoproxy } from './goproxy-parser.ts';\nimport { GoDirectDatasource } from './releases-direct.ts';\nimport { GoProxyDatasource } from './releases-goproxy.ts';\n\nexport class GoDatasource extends Datasource {\n static readonly id = 'go';\n\n override readonly defaultVersioning = semverId;\n\n constructor() {\n super(GoDatasource.id);\n }\n\n override readonly defaultConfig = {\n commitMessageTopic: 'module {{depName}}',\n };\n\n override readonly customRegistrySupport = false;\n\n override readonly releaseTimestampSupport = true;\n override readonly releaseTimestampNote =\n 'If the release timestamp is not returned from the respective datasoure used to fetch the releases, then Renovate uses the `Time` field in the results instead.';\n override readonly sourceUrlSupport = 'package';\n override readonly sourceUrlNote =\n 'The source URL is determined from the `packageName` and `registryUrl`.';\n\n readonly goproxy = new GoProxyDatasource();\n readonly direct = new GoDirectDatasource();\n\n // Pseudo versions https://go.dev/ref/mod#pseudo-versions\n static readonly pversionRegexp = regEx(\n /v\\d+\\.\\d+\\.\\d+-(?:\\w+\\.)?(?:0\\.)?\\d{14}-(?<digest>[a-f0-9]{12})/,\n );\n\n private _getReleases(\n config: GetReleasesConfig,\n ): Promise<ReleaseResult | null> {\n return this.goproxy.getReleases(config);\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n return withCache(\n {\n namespace: `datasource-${GoDatasource.id}`,\n // TODO: types (#22198)\n key: `getReleases:${config.packageName}`,\n fallback: true,\n },\n () => this._getReleases(config),\n );\n }\n\n /**\n * go.getDigest\n *\n * This datasource resolves a go module URL into its source repository\n * and then fetches the digest if it is on GitHub.\n *\n * This function will:\n * - Determine the source URL for the module\n * - Call the respective getDigest in github to retrieve the commit hash\n */\n private async _getDigest(\n { packageName }: DigestConfig,\n newValue?: string,\n ): Promise<string | null> {\n if (parseGoproxy().some(({ url }) => url === 'off')) {\n logger.debug(\n `Skip digest fetch for ${packageName} with GOPROXY containing \"off\"`,\n );\n return null;\n }\n\n const source = await BaseGoDatasource.getDatasource(packageName);\n if (!source) {\n return null;\n }\n\n // ignore vX.Y.Z-(0.)? pseudo versions that are used Go Modules - look up default branch instead\n // ignore v0.0.0 versions to fetch the digest of default branch, not the commit of non-existing tag `v0.0.0`\n const tag =\n newValue &&\n !GoDatasource.pversionRegexp.test(newValue) &&\n newValue !== 'v0.0.0'\n ? newValue\n : undefined;\n\n switch (source.datasource) {\n case ForgejoTagsDatasource.id: {\n return this.direct.forgejo.getDigest(source, tag);\n }\n case GitTagsDatasource.id: {\n return this.direct.git.getDigest(source, tag);\n }\n case GiteaTagsDatasource.id: {\n return this.direct.gitea.getDigest(source, tag);\n }\n case GithubTagsDatasource.id: {\n return this.direct.github.getDigest(source, tag);\n }\n case BitbucketTagsDatasource.id: {\n return this.direct.bitbucket.getDigest(source, tag);\n }\n case GitlabTagsDatasource.id: {\n return this.direct.gitlab.getDigest(source, tag);\n }\n /* v8 ignore next 3: can never happen, makes lint happy */\n default: {\n return null;\n }\n }\n }\n\n override getDigest(\n config: DigestConfig,\n newValue?: string,\n ): Promise<string | null> {\n return withCache(\n {\n namespace: `datasource-${GoDatasource.id}`,\n key: `getDigest:${config.packageName}:${newValue}`,\n fallback: true,\n },\n () => this._getDigest(config, newValue),\n );\n }\n}\n\nconst env = getEnv();\n/* v8 ignore if -- hard to test */\nif (isString(env.GOPROXY)) {\n const uri = parseUrl(env.GOPROXY);\n if (uri?.password) {\n addSecretForSanitizing(uri.password, 'global');\n } else if (uri?.username) {\n addSecretForSanitizing(uri.username, 'global');\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAyBA,IAAa,eAAb,MAAa,qBAAqB,WAAW;CAC3C,OAAgB,KAAK;CAErB,oBAAsCA;CAEtC,cAAc;AACZ,QAAM,aAAa,GAAG;;CAGxB,gBAAkC,EAChC,oBAAoB,sBACrB;CAED,wBAA0C;CAE1C,0BAA4C;CAC5C,uBACE;CACF,mBAAqC;CACrC,gBACE;CAEF,UAAmB,IAAI,mBAAmB;CAC1C,SAAkB,IAAI,oBAAoB;CAG1C,OAAgB,iBAAiB,MAC/B,kEACD;CAED,aACE,QAC+B;AAC/B,SAAO,KAAK,QAAQ,YAAY,OAAO;;CAGzC,YAAY,QAA0D;AACpE,SAAO,UACL;GACE,WAAW,cAAc,aAAa;GAEtC,KAAK,eAAe,OAAO;GAC3B,UAAU;GACX,QACK,KAAK,aAAa,OAAO,CAChC;;;;;;;;;;;;CAaH,MAAc,WACZ,EAAE,eACF,UACwB;AACxB,MAAI,cAAc,CAAC,MAAM,EAAE,UAAU,QAAQ,MAAM,EAAE;AACnD,UAAO,MACL,yBAAyB,YAAY,gCACtC;AACD,UAAO;;EAGT,MAAM,SAAS,MAAM,iBAAiB,cAAc,YAAY;AAChE,MAAI,CAAC,OACH,QAAO;EAKT,MAAM,MACJ,YACA,CAAC,aAAa,eAAe,KAAK,SAAS,IAC3C,aAAa,WACT,WACA,KAAA;AAEN,UAAQ,OAAO,YAAf;GACE,KAAK,sBAAsB,GACzB,QAAO,KAAK,OAAO,QAAQ,UAAU,QAAQ,IAAI;GAEnD,KAAK,kBAAkB,GACrB,QAAO,KAAK,OAAO,IAAI,UAAU,QAAQ,IAAI;GAE/C,KAAK,oBAAoB,GACvB,QAAO,KAAK,OAAO,MAAM,UAAU,QAAQ,IAAI;GAEjD,KAAK,qBAAqB,GACxB,QAAO,KAAK,OAAO,OAAO,UAAU,QAAQ,IAAI;GAElD,KAAK,wBAAwB,GAC3B,QAAO,KAAK,OAAO,UAAU,UAAU,QAAQ,IAAI;GAErD,KAAK,qBAAqB,GACxB,QAAO,KAAK,OAAO,OAAO,UAAU,QAAQ,IAAI
|
|
1
|
+
{"version":3,"file":"index.js","names":["semverId"],"sources":["../../../../lib/modules/datasource/go/index.ts"],"sourcesContent":["import { isString } from '@sindresorhus/is';\nimport { logger } from '../../../logger/index.ts';\nimport { withCache } from '../../../util/cache/package/with-cache.ts';\nimport { getEnv } from '../../../util/env.ts';\nimport { regEx } from '../../../util/regex.ts';\nimport { addSecretForSanitizing } from '../../../util/sanitize.ts';\nimport { parseUrl } from '../../../util/url.ts';\nimport { id as semverId } from '../../versioning/semver/index.ts';\nimport { BitbucketTagsDatasource } from '../bitbucket-tags/index.ts';\nimport { Datasource } from '../datasource.ts';\nimport { ForgejoTagsDatasource } from '../forgejo-tags/index.ts';\nimport { GitTagsDatasource } from '../git-tags/index.ts';\nimport { GiteaTagsDatasource } from '../gitea-tags/index.ts';\nimport { GithubTagsDatasource } from '../github-tags/index.ts';\nimport { GitlabTagsDatasource } from '../gitlab-tags/index.ts';\nimport type {\n DigestConfig,\n GetReleasesConfig,\n ReleaseResult,\n} from '../types.ts';\nimport { BaseGoDatasource } from './base.ts';\nimport { parseGoproxy } from './goproxy-parser.ts';\nimport { GoDirectDatasource } from './releases-direct.ts';\nimport { GoProxyDatasource } from './releases-goproxy.ts';\n\nexport class GoDatasource extends Datasource {\n static readonly id = 'go';\n\n override readonly defaultVersioning = semverId;\n\n constructor() {\n super(GoDatasource.id);\n }\n\n override readonly defaultConfig = {\n commitMessageTopic: 'module {{depName}}',\n };\n\n override readonly customRegistrySupport = false;\n\n override readonly releaseTimestampSupport = true;\n override readonly releaseTimestampNote =\n 'If the release timestamp is not returned from the respective datasoure used to fetch the releases, then Renovate uses the `Time` field in the results instead.';\n override readonly sourceUrlSupport = 'package';\n override readonly sourceUrlNote =\n 'The source URL is determined from the `packageName` and `registryUrl`.';\n\n readonly goproxy = new GoProxyDatasource();\n readonly direct = new GoDirectDatasource();\n\n // Pseudo versions https://go.dev/ref/mod#pseudo-versions\n static readonly pversionRegexp = regEx(\n /v\\d+\\.\\d+\\.\\d+-(?:\\w+\\.)?(?:0\\.)?\\d{14}-(?<digest>[a-f0-9]{12})/,\n );\n\n private _getReleases(\n config: GetReleasesConfig,\n ): Promise<ReleaseResult | null> {\n return this.goproxy.getReleases(config);\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n return withCache(\n {\n namespace: `datasource-${GoDatasource.id}`,\n // TODO: types (#22198)\n key: `getReleases:${config.packageName}`,\n fallback: true,\n },\n () => this._getReleases(config),\n );\n }\n\n /**\n * go.getDigest\n *\n * This datasource resolves a go module URL into its source repository\n * and then fetches the digest if it is on GitHub.\n *\n * This function will:\n * - Determine the source URL for the module\n * - Call the respective getDigest in github to retrieve the commit hash\n */\n private async _getDigest(\n { packageName }: DigestConfig,\n newValue?: string,\n ): Promise<string | null> {\n if (parseGoproxy().some(({ url }) => url === 'off')) {\n logger.debug(\n `Skip digest fetch for ${packageName} with GOPROXY containing \"off\"`,\n );\n return null;\n }\n\n const source = await BaseGoDatasource.getDatasource(packageName);\n if (!source) {\n return null;\n }\n\n // ignore vX.Y.Z-(0.)? pseudo versions that are used Go Modules - look up default branch instead\n // ignore v0.0.0 versions to fetch the digest of default branch, not the commit of non-existing tag `v0.0.0`\n const tag =\n newValue &&\n !GoDatasource.pversionRegexp.test(newValue) &&\n newValue !== 'v0.0.0'\n ? newValue\n : undefined;\n\n switch (source.datasource) {\n case ForgejoTagsDatasource.id: {\n return this.direct.forgejo.getDigest(source, tag);\n }\n case GitTagsDatasource.id: {\n return this.direct.git.getDigest(source, tag);\n }\n case GiteaTagsDatasource.id: {\n return this.direct.gitea.getDigest(source, tag);\n }\n case GithubTagsDatasource.id: {\n return this.direct.github.getDigest(source, tag);\n }\n case BitbucketTagsDatasource.id: {\n return this.direct.bitbucket.getDigest(source, tag);\n }\n case GitlabTagsDatasource.id: {\n return this.direct.gitlab.getDigest(source, tag);\n }\n /* v8 ignore next 3: can never happen, makes lint happy */\n default: {\n return null;\n }\n }\n }\n\n override getDigest(\n config: DigestConfig,\n newValue?: string,\n ): Promise<string | null> {\n return withCache(\n {\n namespace: `datasource-${GoDatasource.id}`,\n key: `getDigest:${config.packageName}:${newValue}`,\n fallback: true,\n },\n () => this._getDigest(config, newValue),\n );\n }\n}\n\nconst env = getEnv();\n/* v8 ignore if -- hard to test */\nif (isString(env.GOPROXY)) {\n const uri = parseUrl(env.GOPROXY);\n if (uri?.password) {\n addSecretForSanitizing(uri.password, 'global');\n } else if (uri?.username) {\n addSecretForSanitizing(uri.username, 'global');\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAyBA,IAAa,eAAb,MAAa,qBAAqB,WAAW;CAC3C,OAAgB,KAAK;CAErB,oBAAsCA;CAEtC,cAAc;AACZ,QAAM,aAAa,GAAG;;CAGxB,gBAAkC,EAChC,oBAAoB,sBACrB;CAED,wBAA0C;CAE1C,0BAA4C;CAC5C,uBACE;CACF,mBAAqC;CACrC,gBACE;CAEF,UAAmB,IAAI,mBAAmB;CAC1C,SAAkB,IAAI,oBAAoB;CAG1C,OAAgB,iBAAiB,MAC/B,kEACD;CAED,aACE,QAC+B;AAC/B,SAAO,KAAK,QAAQ,YAAY,OAAO;;CAGzC,YAAY,QAA0D;AACpE,SAAO,UACL;GACE,WAAW,cAAc,aAAa;GAEtC,KAAK,eAAe,OAAO;GAC3B,UAAU;GACX,QACK,KAAK,aAAa,OAAO,CAChC;;;;;;;;;;;;CAaH,MAAc,WACZ,EAAE,eACF,UACwB;AACxB,MAAI,cAAc,CAAC,MAAM,EAAE,UAAU,QAAQ,MAAM,EAAE;AACnD,UAAO,MACL,yBAAyB,YAAY,gCACtC;AACD,UAAO;;EAGT,MAAM,SAAS,MAAM,iBAAiB,cAAc,YAAY;AAChE,MAAI,CAAC,OACH,QAAO;EAKT,MAAM,MACJ,YACA,CAAC,aAAa,eAAe,KAAK,SAAS,IAC3C,aAAa,WACT,WACA,KAAA;AAEN,UAAQ,OAAO,YAAf;GACE,KAAK,sBAAsB,GACzB,QAAO,KAAK,OAAO,QAAQ,UAAU,QAAQ,IAAI;GAEnD,KAAK,kBAAkB,GACrB,QAAO,KAAK,OAAO,IAAI,UAAU,QAAQ,IAAI;GAE/C,KAAK,oBAAoB,GACvB,QAAO,KAAK,OAAO,MAAM,UAAU,QAAQ,IAAI;GAEjD,KAAK,qBAAqB,GACxB,QAAO,KAAK,OAAO,OAAO,UAAU,QAAQ,IAAI;GAElD,KAAK,wBAAwB,GAC3B,QAAO,KAAK,OAAO,UAAU,UAAU,QAAQ,IAAI;GAErD,KAAK,qBAAqB,GACxB,QAAO,KAAK,OAAO,OAAO,UAAU,QAAQ,IAAI;;GAGlD,QACE,QAAO;;;CAKb,UACE,QACA,UACwB;AACxB,SAAO,UACL;GACE,WAAW,cAAc,aAAa;GACtC,KAAK,aAAa,OAAO,YAAY,GAAG;GACxC,UAAU;GACX,QACK,KAAK,WAAW,QAAQ,SAAS,CACxC;;;AAIL,MAAM,MAAM,QAAQ;;AAEpB,IAAI,SAAS,IAAI,QAAQ,EAAE;CACzB,MAAM,MAAM,SAAS,IAAI,QAAQ;AACjC,KAAI,KAAK,SACP,wBAAuB,IAAI,UAAU,SAAS;UACrC,KAAK,SACd,wBAAuB,IAAI,UAAU,SAAS"}
|
|
@@ -93,6 +93,7 @@ var GoDirectDatasource = class GoDirectDatasource extends Datasource {
|
|
|
93
93
|
case BitbucketTagsDatasource.id:
|
|
94
94
|
res = await this.bitbucket.getReleases(source);
|
|
95
95
|
break;
|
|
96
|
+
/* v8 ignore next 3 -- should never happen */
|
|
96
97
|
default: return null;
|
|
97
98
|
}
|
|
98
99
|
/* v8 ignore next 3 -- TODO: add test */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"releases-direct.js","names":[],"sources":["../../../../lib/modules/datasource/go/releases-direct.ts"],"sourcesContent":["import { logger } from '../../../logger/index.ts';\nimport { withCache } from '../../../util/cache/package/with-cache.ts';\nimport { regEx } from '../../../util/regex.ts';\nimport { BitbucketTagsDatasource } from '../bitbucket-tags/index.ts';\nimport { Datasource } from '../datasource.ts';\nimport { ForgejoTagsDatasource } from '../forgejo-tags/index.ts';\nimport { GitTagsDatasource } from '../git-tags/index.ts';\nimport { GiteaTagsDatasource } from '../gitea-tags/index.ts';\nimport { GithubTagsDatasource } from '../github-tags/index.ts';\nimport { GitlabTagsDatasource } from '../gitlab-tags/index.ts';\nimport type { GetReleasesConfig, Release, ReleaseResult } from '../types.ts';\nimport { BaseGoDatasource } from './base.ts';\nimport { getSourceUrl } from './common.ts';\n\n/**\n * This function tries to select tags with longest prefix could be constructed from `packageName`.\n *\n * For package named `example.com/foo/bar/baz/qux`, it will try to detect tags with following prefixes:\n *\n * - `foo/bar/baz/qux/vX.Y.Z`\n * - `bar/baz/qux/vX.Y.Z`\n * - `baz/qux/vX.Y.Z`\n * - `qux/vX.Y.Z`\n *\n * If none of the following is found, it falls back to simply returning all tags like `vX.Y.Z`.\n */\nfunction filterByPrefix(packageName: string, releases: Release[]): Release[] {\n const nameParts = packageName\n .replace(regEx(/\\/v\\d+$/), '')\n .split('/')\n .slice(1);\n\n const submoduleReleases: Release[] = [];\n while (nameParts.length) {\n const prefix = `${nameParts.join('/')}/`;\n\n for (const release of releases) {\n if (!release.version.startsWith(prefix)) {\n continue;\n }\n\n const normalizedVersion = release.version.replace(prefix, '');\n if (!normalizedVersion.match(regEx(/^v\\d[^/]*/))) {\n continue;\n }\n\n release.version = release.version.replace(prefix, '');\n submoduleReleases.push(release);\n }\n\n if (submoduleReleases.length) {\n return submoduleReleases;\n }\n\n nameParts.shift();\n }\n\n return releases.filter((release) => release.version.startsWith('v'));\n}\n\nexport class GoDirectDatasource extends Datasource {\n static readonly id = 'go-direct';\n\n readonly forgejo = new ForgejoTagsDatasource();\n git: GitTagsDatasource;\n readonly gitea = new GiteaTagsDatasource();\n github: GithubTagsDatasource;\n gitlab: GitlabTagsDatasource;\n bitbucket: BitbucketTagsDatasource;\n\n constructor() {\n super(GoDirectDatasource.id);\n this.git = new GitTagsDatasource();\n this.github = new GithubTagsDatasource();\n this.gitlab = new GitlabTagsDatasource();\n this.bitbucket = new BitbucketTagsDatasource();\n }\n\n /**\n * go.getReleases\n *\n * This datasource resolves a go module URL into its source repository\n * and then fetch it if it is on GitHub.\n *\n * This function will:\n * - Determine the source URL for the module\n * - Call the respective getReleases in github/gitlab to retrieve the tags\n * - Filter module tags according to the module path\n */\n private async _getReleases(\n config: GetReleasesConfig,\n ): Promise<ReleaseResult | null> {\n const { packageName } = config;\n\n let res: ReleaseResult | null = null;\n\n logger.trace(`go.getReleases(${packageName})`);\n const source = await BaseGoDatasource.getDatasource(packageName);\n\n if (!source) {\n logger.info(\n { packageName },\n 'Unsupported go host - cannot look up versions',\n );\n return null;\n }\n\n switch (source.datasource) {\n case ForgejoTagsDatasource.id: {\n res = await this.forgejo.getReleases(source);\n break;\n }\n case GitTagsDatasource.id: {\n res = await this.git.getReleases(source);\n break;\n }\n case GiteaTagsDatasource.id: {\n res = await this.gitea.getReleases(source);\n break;\n }\n case GithubTagsDatasource.id: {\n res = await this.github.getReleases(source);\n break;\n }\n case GitlabTagsDatasource.id: {\n res = await this.gitlab.getReleases(source);\n break;\n }\n case BitbucketTagsDatasource.id: {\n res = await this.bitbucket.getReleases(source);\n break;\n }\n /* v8 ignore next 3 -- should never happen */\n default: {\n return null;\n }\n }\n\n /* v8 ignore next 3 -- TODO: add test */\n if (!res) {\n return null;\n }\n\n const sourceUrl = res.sourceUrl ?? getSourceUrl(source) ?? null;\n\n return {\n ...res,\n releases: filterByPrefix(packageName, res.releases),\n sourceUrl,\n };\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n return withCache(\n {\n namespace: `datasource-${GoDirectDatasource.id}`,\n key: config.packageName,\n fallback: true,\n },\n () => this._getReleases(config),\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AA0BA,SAAS,eAAe,aAAqB,UAAgC;CAC3E,MAAM,YAAY,YACf,QAAQ,MAAM,UAAU,EAAE,GAAG,CAC7B,MAAM,IAAI,CACV,MAAM,EAAE;CAEX,MAAM,oBAA+B,EAAE;AACvC,QAAO,UAAU,QAAQ;EACvB,MAAM,SAAS,GAAG,UAAU,KAAK,IAAI,CAAC;AAEtC,OAAK,MAAM,WAAW,UAAU;AAC9B,OAAI,CAAC,QAAQ,QAAQ,WAAW,OAAO,CACrC;AAIF,OAAI,CADsB,QAAQ,QAAQ,QAAQ,QAAQ,GAAG,CACtC,MAAM,MAAM,YAAY,CAAC,CAC9C;AAGF,WAAQ,UAAU,QAAQ,QAAQ,QAAQ,QAAQ,GAAG;AACrD,qBAAkB,KAAK,QAAQ;;AAGjC,MAAI,kBAAkB,OACpB,QAAO;AAGT,YAAU,OAAO;;AAGnB,QAAO,SAAS,QAAQ,YAAY,QAAQ,QAAQ,WAAW,IAAI,CAAC;;AAGtE,IAAa,qBAAb,MAAa,2BAA2B,WAAW;CACjD,OAAgB,KAAK;CAErB,UAAmB,IAAI,uBAAuB;CAC9C;CACA,QAAiB,IAAI,qBAAqB;CAC1C;CACA;CACA;CAEA,cAAc;AACZ,QAAM,mBAAmB,GAAG;AAC5B,OAAK,MAAM,IAAI,mBAAmB;AAClC,OAAK,SAAS,IAAI,sBAAsB;AACxC,OAAK,SAAS,IAAI,sBAAsB;AACxC,OAAK,YAAY,IAAI,yBAAyB;;;;;;;;;;;;;CAchD,MAAc,aACZ,QAC+B;EAC/B,MAAM,EAAE,gBAAgB;EAExB,IAAI,MAA4B;AAEhC,SAAO,MAAM,kBAAkB,YAAY,GAAG;EAC9C,MAAM,SAAS,MAAM,iBAAiB,cAAc,YAAY;AAEhE,MAAI,CAAC,QAAQ;AACX,UAAO,KACL,EAAE,aAAa,EACf,gDACD;AACD,UAAO;;AAGT,UAAQ,OAAO,YAAf;GACE,KAAK,sBAAsB;AACzB,UAAM,MAAM,KAAK,QAAQ,YAAY,OAAO;AAC5C;GAEF,KAAK,kBAAkB;AACrB,UAAM,MAAM,KAAK,IAAI,YAAY,OAAO;AACxC;GAEF,KAAK,oBAAoB;AACvB,UAAM,MAAM,KAAK,MAAM,YAAY,OAAO;AAC1C;GAEF,KAAK,qBAAqB;AACxB,UAAM,MAAM,KAAK,OAAO,YAAY,OAAO;AAC3C;GAEF,KAAK,qBAAqB;AACxB,UAAM,MAAM,KAAK,OAAO,YAAY,OAAO;AAC3C;GAEF,KAAK,wBAAwB;AAC3B,UAAM,MAAM,KAAK,UAAU,YAAY,OAAO;AAC9C
|
|
1
|
+
{"version":3,"file":"releases-direct.js","names":[],"sources":["../../../../lib/modules/datasource/go/releases-direct.ts"],"sourcesContent":["import { logger } from '../../../logger/index.ts';\nimport { withCache } from '../../../util/cache/package/with-cache.ts';\nimport { regEx } from '../../../util/regex.ts';\nimport { BitbucketTagsDatasource } from '../bitbucket-tags/index.ts';\nimport { Datasource } from '../datasource.ts';\nimport { ForgejoTagsDatasource } from '../forgejo-tags/index.ts';\nimport { GitTagsDatasource } from '../git-tags/index.ts';\nimport { GiteaTagsDatasource } from '../gitea-tags/index.ts';\nimport { GithubTagsDatasource } from '../github-tags/index.ts';\nimport { GitlabTagsDatasource } from '../gitlab-tags/index.ts';\nimport type { GetReleasesConfig, Release, ReleaseResult } from '../types.ts';\nimport { BaseGoDatasource } from './base.ts';\nimport { getSourceUrl } from './common.ts';\n\n/**\n * This function tries to select tags with longest prefix could be constructed from `packageName`.\n *\n * For package named `example.com/foo/bar/baz/qux`, it will try to detect tags with following prefixes:\n *\n * - `foo/bar/baz/qux/vX.Y.Z`\n * - `bar/baz/qux/vX.Y.Z`\n * - `baz/qux/vX.Y.Z`\n * - `qux/vX.Y.Z`\n *\n * If none of the following is found, it falls back to simply returning all tags like `vX.Y.Z`.\n */\nfunction filterByPrefix(packageName: string, releases: Release[]): Release[] {\n const nameParts = packageName\n .replace(regEx(/\\/v\\d+$/), '')\n .split('/')\n .slice(1);\n\n const submoduleReleases: Release[] = [];\n while (nameParts.length) {\n const prefix = `${nameParts.join('/')}/`;\n\n for (const release of releases) {\n if (!release.version.startsWith(prefix)) {\n continue;\n }\n\n const normalizedVersion = release.version.replace(prefix, '');\n if (!normalizedVersion.match(regEx(/^v\\d[^/]*/))) {\n continue;\n }\n\n release.version = release.version.replace(prefix, '');\n submoduleReleases.push(release);\n }\n\n if (submoduleReleases.length) {\n return submoduleReleases;\n }\n\n nameParts.shift();\n }\n\n return releases.filter((release) => release.version.startsWith('v'));\n}\n\nexport class GoDirectDatasource extends Datasource {\n static readonly id = 'go-direct';\n\n readonly forgejo = new ForgejoTagsDatasource();\n git: GitTagsDatasource;\n readonly gitea = new GiteaTagsDatasource();\n github: GithubTagsDatasource;\n gitlab: GitlabTagsDatasource;\n bitbucket: BitbucketTagsDatasource;\n\n constructor() {\n super(GoDirectDatasource.id);\n this.git = new GitTagsDatasource();\n this.github = new GithubTagsDatasource();\n this.gitlab = new GitlabTagsDatasource();\n this.bitbucket = new BitbucketTagsDatasource();\n }\n\n /**\n * go.getReleases\n *\n * This datasource resolves a go module URL into its source repository\n * and then fetch it if it is on GitHub.\n *\n * This function will:\n * - Determine the source URL for the module\n * - Call the respective getReleases in github/gitlab to retrieve the tags\n * - Filter module tags according to the module path\n */\n private async _getReleases(\n config: GetReleasesConfig,\n ): Promise<ReleaseResult | null> {\n const { packageName } = config;\n\n let res: ReleaseResult | null = null;\n\n logger.trace(`go.getReleases(${packageName})`);\n const source = await BaseGoDatasource.getDatasource(packageName);\n\n if (!source) {\n logger.info(\n { packageName },\n 'Unsupported go host - cannot look up versions',\n );\n return null;\n }\n\n switch (source.datasource) {\n case ForgejoTagsDatasource.id: {\n res = await this.forgejo.getReleases(source);\n break;\n }\n case GitTagsDatasource.id: {\n res = await this.git.getReleases(source);\n break;\n }\n case GiteaTagsDatasource.id: {\n res = await this.gitea.getReleases(source);\n break;\n }\n case GithubTagsDatasource.id: {\n res = await this.github.getReleases(source);\n break;\n }\n case GitlabTagsDatasource.id: {\n res = await this.gitlab.getReleases(source);\n break;\n }\n case BitbucketTagsDatasource.id: {\n res = await this.bitbucket.getReleases(source);\n break;\n }\n /* v8 ignore next 3 -- should never happen */\n default: {\n return null;\n }\n }\n\n /* v8 ignore next 3 -- TODO: add test */\n if (!res) {\n return null;\n }\n\n const sourceUrl = res.sourceUrl ?? getSourceUrl(source) ?? null;\n\n return {\n ...res,\n releases: filterByPrefix(packageName, res.releases),\n sourceUrl,\n };\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n return withCache(\n {\n namespace: `datasource-${GoDirectDatasource.id}`,\n key: config.packageName,\n fallback: true,\n },\n () => this._getReleases(config),\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AA0BA,SAAS,eAAe,aAAqB,UAAgC;CAC3E,MAAM,YAAY,YACf,QAAQ,MAAM,UAAU,EAAE,GAAG,CAC7B,MAAM,IAAI,CACV,MAAM,EAAE;CAEX,MAAM,oBAA+B,EAAE;AACvC,QAAO,UAAU,QAAQ;EACvB,MAAM,SAAS,GAAG,UAAU,KAAK,IAAI,CAAC;AAEtC,OAAK,MAAM,WAAW,UAAU;AAC9B,OAAI,CAAC,QAAQ,QAAQ,WAAW,OAAO,CACrC;AAIF,OAAI,CADsB,QAAQ,QAAQ,QAAQ,QAAQ,GAAG,CACtC,MAAM,MAAM,YAAY,CAAC,CAC9C;AAGF,WAAQ,UAAU,QAAQ,QAAQ,QAAQ,QAAQ,GAAG;AACrD,qBAAkB,KAAK,QAAQ;;AAGjC,MAAI,kBAAkB,OACpB,QAAO;AAGT,YAAU,OAAO;;AAGnB,QAAO,SAAS,QAAQ,YAAY,QAAQ,QAAQ,WAAW,IAAI,CAAC;;AAGtE,IAAa,qBAAb,MAAa,2BAA2B,WAAW;CACjD,OAAgB,KAAK;CAErB,UAAmB,IAAI,uBAAuB;CAC9C;CACA,QAAiB,IAAI,qBAAqB;CAC1C;CACA;CACA;CAEA,cAAc;AACZ,QAAM,mBAAmB,GAAG;AAC5B,OAAK,MAAM,IAAI,mBAAmB;AAClC,OAAK,SAAS,IAAI,sBAAsB;AACxC,OAAK,SAAS,IAAI,sBAAsB;AACxC,OAAK,YAAY,IAAI,yBAAyB;;;;;;;;;;;;;CAchD,MAAc,aACZ,QAC+B;EAC/B,MAAM,EAAE,gBAAgB;EAExB,IAAI,MAA4B;AAEhC,SAAO,MAAM,kBAAkB,YAAY,GAAG;EAC9C,MAAM,SAAS,MAAM,iBAAiB,cAAc,YAAY;AAEhE,MAAI,CAAC,QAAQ;AACX,UAAO,KACL,EAAE,aAAa,EACf,gDACD;AACD,UAAO;;AAGT,UAAQ,OAAO,YAAf;GACE,KAAK,sBAAsB;AACzB,UAAM,MAAM,KAAK,QAAQ,YAAY,OAAO;AAC5C;GAEF,KAAK,kBAAkB;AACrB,UAAM,MAAM,KAAK,IAAI,YAAY,OAAO;AACxC;GAEF,KAAK,oBAAoB;AACvB,UAAM,MAAM,KAAK,MAAM,YAAY,OAAO;AAC1C;GAEF,KAAK,qBAAqB;AACxB,UAAM,MAAM,KAAK,OAAO,YAAY,OAAO;AAC3C;GAEF,KAAK,qBAAqB;AACxB,UAAM,MAAM,KAAK,OAAO,YAAY,OAAO;AAC3C;GAEF,KAAK,wBAAwB;AAC3B,UAAM,MAAM,KAAK,UAAU,YAAY,OAAO;AAC9C;;GAGF,QACE,QAAO;;;AAKX,MAAI,CAAC,IACH,QAAO;EAGT,MAAM,YAAY,IAAI,aAAa,aAAa,OAAO,IAAI;AAE3D,SAAO;GACL,GAAG;GACH,UAAU,eAAe,aAAa,IAAI,SAAS;GACnD;GACD;;CAGH,YAAY,QAA0D;AACpE,SAAO,UACL;GACE,WAAW,cAAc,mBAAmB;GAC5C,KAAK,OAAO;GACZ,UAAU;GACX,QACK,KAAK,aAAa,OAAO,CAChC"}
|
|
@@ -13,7 +13,7 @@ function getLatestSuitableVersion(releases) {
|
|
|
13
13
|
if (!releases?.length) return null;
|
|
14
14
|
const allVersions = releases.map(({ version }) => version);
|
|
15
15
|
const stableVersions = allVersions.filter((x) => api.isStable(x));
|
|
16
|
-
return (stableVersions.length ? stableVersions : allVersions).reduce((latestVersion, version) => compare(version, latestVersion) === 1 ? version : latestVersion);
|
|
16
|
+
return (stableVersions.length ? stableVersions : allVersions).reduce((latestVersion, version) => compare(version, latestVersion) === 1 ? version : /* istanbul ignore next: hard to test */ latestVersion);
|
|
17
17
|
}
|
|
18
18
|
function extractVersions(metadata) {
|
|
19
19
|
const res = {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["mavenVersion","mavenVersioning.id","packageCache.get","packageCache.set"],"sources":["../../../../lib/modules/datasource/maven/index.ts"],"sourcesContent":["import type { XmlDocument } from 'xmldoc';\nimport { logger } from '../../../logger/index.ts';\nimport * as packageCache from '../../../util/cache/package/index.ts';\nimport { asTimestamp } from '../../../util/timestamp.ts';\nimport { ensureTrailingSlash } from '../../../util/url.ts';\nimport { compare } from '../../versioning/maven/compare.ts';\nimport mavenVersion, * as mavenVersioning from '../../versioning/maven/index.ts';\nimport { Datasource } from '../datasource.ts';\nimport type {\n GetReleasesConfig,\n PostprocessReleaseConfig,\n PostprocessReleaseResult,\n RegistryStrategy,\n Release,\n ReleaseResult,\n} from '../types.ts';\nimport { MAVEN_REPO } from './common.ts';\nimport type { MavenDependency, MetadataResults } from './types.ts';\nimport {\n createUrlForDependencyPom,\n downloadMaven,\n downloadMavenXml,\n getDependencyInfo,\n getDependencyParts,\n getMavenUrl,\n} from './util.ts';\n\nfunction getLatestSuitableVersion(releases: Release[]): string | null {\n /* v8 ignore next 3 -- TODO: add test */\n if (!releases?.length) {\n return null;\n }\n const allVersions = releases.map(({ version }) => version);\n const stableVersions = allVersions.filter((x) => mavenVersion.isStable(x));\n const versions = stableVersions.length ? stableVersions : allVersions;\n return versions.reduce((latestVersion, version) =>\n compare(version, latestVersion) === 1\n ? version\n : /* istanbul ignore next: hard to test */ latestVersion,\n );\n}\n\nfunction extractVersions(metadata: XmlDocument): MetadataResults {\n const res: MetadataResults = {};\n const versions = metadata.descendantWithPath('versioning.versions');\n const elements = versions?.childrenNamed('version');\n if (!elements) {\n return res;\n }\n res.versions = elements.map((el) => el.val);\n const latest = metadata.descendantWithPath('versioning.latest');\n if (latest?.val) {\n res.tags ??= {};\n res.tags.latest = latest.val;\n }\n const release = metadata.descendantWithPath('versioning.release');\n if (release?.val) {\n res.tags ??= {};\n res.tags.release = release.val;\n }\n\n return res;\n}\n\nexport const defaultRegistryUrls = [MAVEN_REPO];\n\nexport class MavenDatasource extends Datasource {\n static id = 'maven';\n\n override readonly caching = true;\n\n override readonly defaultRegistryUrls = defaultRegistryUrls;\n\n override readonly defaultVersioning: string = mavenVersioning.id;\n\n override readonly registryStrategy: RegistryStrategy = 'merge';\n\n override readonly releaseTimestampSupport = true;\n override readonly releaseTimestampNote =\n 'The release timestamp is determined from the `Last-Modified` header or the `lastModified` field in the results.';\n override readonly sourceUrlSupport = 'package';\n override readonly sourceUrlNote =\n 'The source URL is determined from the `scm` tags in the results.';\n\n constructor(id = MavenDatasource.id) {\n super(id);\n }\n\n async fetchVersionsFromMetadata(\n dependency: MavenDependency,\n repoUrl: string,\n ): Promise<MetadataResults> {\n const metadataUrl = getMavenUrl(dependency, repoUrl, 'maven-metadata.xml');\n const metadataXmlResult = await downloadMavenXml(this.http, metadataUrl);\n return metadataXmlResult\n .transform(({ data: metadata }) => extractVersions(metadata))\n .onError((err) => {\n logger.debug(\n `Maven: error fetching versions for \"${dependency.display}\": ${err.type}`,\n );\n })\n .unwrapOr({});\n }\n\n async getReleases({\n packageName,\n registryUrl,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n /* v8 ignore next 3 -- should never happen */\n if (!registryUrl) {\n return null;\n }\n\n const dependency = getDependencyParts(packageName);\n const repoUrl = ensureTrailingSlash(registryUrl);\n\n logger.debug(`Looking up ${dependency.display} in repository ${repoUrl}`);\n\n const metadata = await this.fetchVersionsFromMetadata(dependency, repoUrl);\n if (!metadata.versions?.length) {\n return null;\n }\n const releases = metadata.versions.map((version) => ({ version }));\n\n logger.debug(\n `Found ${releases.length} new releases for ${dependency.display} in repository ${repoUrl}`,\n );\n\n const latestSuitableVersion = getLatestSuitableVersion(releases);\n const dependencyInfo =\n latestSuitableVersion &&\n (await getDependencyInfo(\n this.http,\n dependency,\n repoUrl,\n latestSuitableVersion,\n ));\n\n const result: ReleaseResult = {\n ...dependency,\n ...dependencyInfo,\n releases,\n };\n if (metadata.tags) {\n result.tags = metadata.tags;\n if (result.tags.latest) {\n logger.debug(`Setting respectLatest=false for maven ${packageName}`);\n result.respectLatest = false;\n }\n }\n\n if (!this.defaultRegistryUrls.includes(registryUrl)) {\n result.isPrivate = true;\n }\n\n return result;\n }\n\n override async postprocessRelease(\n { packageName, registryUrl }: PostprocessReleaseConfig,\n release: Release,\n ): Promise<PostprocessReleaseResult> {\n const { version, versionOrig } = release;\n const cacheKey = versionOrig\n ? `postprocessRelease:${registryUrl}:${packageName}:${versionOrig}:${version}`\n : `postprocessRelease:${registryUrl}:${packageName}:${version}`;\n const cachedResult = await packageCache.get<PostprocessReleaseResult>(\n 'datasource-maven:postprocess-reject',\n cacheKey,\n );\n\n /* v8 ignore if: hard to test */\n if (cachedResult) {\n return cachedResult;\n }\n\n if (!packageName || !registryUrl) {\n return release;\n }\n\n const dependency = getDependencyParts(packageName);\n\n const pomUrl = await createUrlForDependencyPom(\n this.http,\n release.versionOrig ?? release.version,\n dependency,\n registryUrl,\n );\n\n const artifactUrl = getMavenUrl(dependency, registryUrl, pomUrl);\n const fetchResult = await downloadMaven(this.http, artifactUrl);\n const { val, err } = fetchResult.unwrap();\n\n if (err) {\n const result: PostprocessReleaseResult =\n err.type === 'not-found' ? 'reject' : release;\n if (result === 'reject') {\n await packageCache.set(\n 'datasource-maven:postprocess-reject',\n cacheKey,\n result,\n 24 * 60,\n );\n }\n return result;\n }\n\n if (val.lastModified) {\n release.releaseTimestamp = asTimestamp(val.lastModified);\n }\n\n return release;\n }\n}\n"],"mappings":";;;;;;;;;;AA2BA,SAAS,yBAAyB,UAAoC;;AAEpE,KAAI,CAAC,UAAU,OACb,QAAO;CAET,MAAM,cAAc,SAAS,KAAK,EAAE,cAAc,QAAQ;CAC1D,MAAM,iBAAiB,YAAY,QAAQ,MAAMA,IAAa,SAAS,EAAE,CAAC;AAE1E,SADiB,eAAe,SAAS,iBAAiB,aAC1C,QAAQ,eAAe,YACrC,QAAQ,SAAS,cAAc,KAAK,IAChC,
|
|
1
|
+
{"version":3,"file":"index.js","names":["mavenVersion","mavenVersioning.id","packageCache.get","packageCache.set"],"sources":["../../../../lib/modules/datasource/maven/index.ts"],"sourcesContent":["import type { XmlDocument } from 'xmldoc';\nimport { logger } from '../../../logger/index.ts';\nimport * as packageCache from '../../../util/cache/package/index.ts';\nimport { asTimestamp } from '../../../util/timestamp.ts';\nimport { ensureTrailingSlash } from '../../../util/url.ts';\nimport { compare } from '../../versioning/maven/compare.ts';\nimport mavenVersion, * as mavenVersioning from '../../versioning/maven/index.ts';\nimport { Datasource } from '../datasource.ts';\nimport type {\n GetReleasesConfig,\n PostprocessReleaseConfig,\n PostprocessReleaseResult,\n RegistryStrategy,\n Release,\n ReleaseResult,\n} from '../types.ts';\nimport { MAVEN_REPO } from './common.ts';\nimport type { MavenDependency, MetadataResults } from './types.ts';\nimport {\n createUrlForDependencyPom,\n downloadMaven,\n downloadMavenXml,\n getDependencyInfo,\n getDependencyParts,\n getMavenUrl,\n} from './util.ts';\n\nfunction getLatestSuitableVersion(releases: Release[]): string | null {\n /* v8 ignore next 3 -- TODO: add test */\n if (!releases?.length) {\n return null;\n }\n const allVersions = releases.map(({ version }) => version);\n const stableVersions = allVersions.filter((x) => mavenVersion.isStable(x));\n const versions = stableVersions.length ? stableVersions : allVersions;\n return versions.reduce((latestVersion, version) =>\n compare(version, latestVersion) === 1\n ? version\n : /* istanbul ignore next: hard to test */ latestVersion,\n );\n}\n\nfunction extractVersions(metadata: XmlDocument): MetadataResults {\n const res: MetadataResults = {};\n const versions = metadata.descendantWithPath('versioning.versions');\n const elements = versions?.childrenNamed('version');\n if (!elements) {\n return res;\n }\n res.versions = elements.map((el) => el.val);\n const latest = metadata.descendantWithPath('versioning.latest');\n if (latest?.val) {\n res.tags ??= {};\n res.tags.latest = latest.val;\n }\n const release = metadata.descendantWithPath('versioning.release');\n if (release?.val) {\n res.tags ??= {};\n res.tags.release = release.val;\n }\n\n return res;\n}\n\nexport const defaultRegistryUrls = [MAVEN_REPO];\n\nexport class MavenDatasource extends Datasource {\n static id = 'maven';\n\n override readonly caching = true;\n\n override readonly defaultRegistryUrls = defaultRegistryUrls;\n\n override readonly defaultVersioning: string = mavenVersioning.id;\n\n override readonly registryStrategy: RegistryStrategy = 'merge';\n\n override readonly releaseTimestampSupport = true;\n override readonly releaseTimestampNote =\n 'The release timestamp is determined from the `Last-Modified` header or the `lastModified` field in the results.';\n override readonly sourceUrlSupport = 'package';\n override readonly sourceUrlNote =\n 'The source URL is determined from the `scm` tags in the results.';\n\n constructor(id = MavenDatasource.id) {\n super(id);\n }\n\n async fetchVersionsFromMetadata(\n dependency: MavenDependency,\n repoUrl: string,\n ): Promise<MetadataResults> {\n const metadataUrl = getMavenUrl(dependency, repoUrl, 'maven-metadata.xml');\n const metadataXmlResult = await downloadMavenXml(this.http, metadataUrl);\n return metadataXmlResult\n .transform(({ data: metadata }) => extractVersions(metadata))\n .onError((err) => {\n logger.debug(\n `Maven: error fetching versions for \"${dependency.display}\": ${err.type}`,\n );\n })\n .unwrapOr({});\n }\n\n async getReleases({\n packageName,\n registryUrl,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n /* v8 ignore next 3 -- should never happen */\n if (!registryUrl) {\n return null;\n }\n\n const dependency = getDependencyParts(packageName);\n const repoUrl = ensureTrailingSlash(registryUrl);\n\n logger.debug(`Looking up ${dependency.display} in repository ${repoUrl}`);\n\n const metadata = await this.fetchVersionsFromMetadata(dependency, repoUrl);\n if (!metadata.versions?.length) {\n return null;\n }\n const releases = metadata.versions.map((version) => ({ version }));\n\n logger.debug(\n `Found ${releases.length} new releases for ${dependency.display} in repository ${repoUrl}`,\n );\n\n const latestSuitableVersion = getLatestSuitableVersion(releases);\n const dependencyInfo =\n latestSuitableVersion &&\n (await getDependencyInfo(\n this.http,\n dependency,\n repoUrl,\n latestSuitableVersion,\n ));\n\n const result: ReleaseResult = {\n ...dependency,\n ...dependencyInfo,\n releases,\n };\n if (metadata.tags) {\n result.tags = metadata.tags;\n if (result.tags.latest) {\n logger.debug(`Setting respectLatest=false for maven ${packageName}`);\n result.respectLatest = false;\n }\n }\n\n if (!this.defaultRegistryUrls.includes(registryUrl)) {\n result.isPrivate = true;\n }\n\n return result;\n }\n\n override async postprocessRelease(\n { packageName, registryUrl }: PostprocessReleaseConfig,\n release: Release,\n ): Promise<PostprocessReleaseResult> {\n const { version, versionOrig } = release;\n const cacheKey = versionOrig\n ? `postprocessRelease:${registryUrl}:${packageName}:${versionOrig}:${version}`\n : `postprocessRelease:${registryUrl}:${packageName}:${version}`;\n const cachedResult = await packageCache.get<PostprocessReleaseResult>(\n 'datasource-maven:postprocess-reject',\n cacheKey,\n );\n\n /* v8 ignore if: hard to test */\n if (cachedResult) {\n return cachedResult;\n }\n\n if (!packageName || !registryUrl) {\n return release;\n }\n\n const dependency = getDependencyParts(packageName);\n\n const pomUrl = await createUrlForDependencyPom(\n this.http,\n release.versionOrig ?? release.version,\n dependency,\n registryUrl,\n );\n\n const artifactUrl = getMavenUrl(dependency, registryUrl, pomUrl);\n const fetchResult = await downloadMaven(this.http, artifactUrl);\n const { val, err } = fetchResult.unwrap();\n\n if (err) {\n const result: PostprocessReleaseResult =\n err.type === 'not-found' ? 'reject' : release;\n if (result === 'reject') {\n await packageCache.set(\n 'datasource-maven:postprocess-reject',\n cacheKey,\n result,\n 24 * 60,\n );\n }\n return result;\n }\n\n if (val.lastModified) {\n release.releaseTimestamp = asTimestamp(val.lastModified);\n }\n\n return release;\n }\n}\n"],"mappings":"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"}
|
|
@@ -38,7 +38,7 @@ var NugetV3Api = class NugetV3Api {
|
|
|
38
38
|
serviceId,
|
|
39
39
|
type: t?.split("/")?.shift(),
|
|
40
40
|
version: t?.split("/")?.pop()
|
|
41
|
-
})).filter(({ type, version }) => type === resourceType && semver.valid(version)).sort((x, y) => x.version && y.version ? semver.compare(x.version, y.version) : 0);
|
|
41
|
+
})).filter(({ type, version }) => type === resourceType && semver.valid(version)).sort((x, y) => x.version && y.version ? semver.compare(x.version, y.version) : /* istanbul ignore next: hard to test */ 0);
|
|
42
42
|
if (services.length === 0) {
|
|
43
43
|
await set(NugetV3Api.cacheNamespace, resultCacheKey, null, 60);
|
|
44
44
|
logger.debug({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"v3.js","names":["packageCache.get","packageCache.set","p.all","versioning","HttpError","fs.createCacheWriteStream","fs.pipeline","fs.readCacheFile","fs.rmCache"],"sources":["../../../../lib/modules/datasource/nuget/v3.ts"],"sourcesContent":["import { isNonEmptyString } from '@sindresorhus/is';\nimport extract from 'extract-zip';\nimport semver from 'semver';\nimport upath from 'upath';\nimport { XmlDocument } from 'xmldoc';\nimport { logger } from '../../../logger/index.ts';\nimport { ExternalHostError } from '../../../types/errors/external-host-error.ts';\nimport * as packageCache from '../../../util/cache/package/index.ts';\nimport { withCache } from '../../../util/cache/package/with-cache.ts';\nimport { getEnv } from '../../../util/env.ts';\nimport * as fs from '../../../util/fs/index.ts';\nimport { ensureCacheDir } from '../../../util/fs/index.ts';\nimport { memCacheProvider } from '../../../util/http/cache/memory-http-cache-provider.ts';\nimport type { Http } from '../../../util/http/index.ts';\nimport { HttpError } from '../../../util/http/index.ts';\nimport * as p from '../../../util/promises.ts';\nimport { regEx } from '../../../util/regex.ts';\nimport { asTimestamp } from '../../../util/timestamp.ts';\nimport { ensureTrailingSlash } from '../../../util/url.ts';\nimport { api as versioning } from '../../versioning/nuget/index.ts';\nimport type { Release, ReleaseResult } from '../types.ts';\nimport { massageUrl, removeBuildMeta, sortNugetVersions } from './common.ts';\nimport type {\n CatalogEntry,\n CatalogPage,\n PackageRegistration,\n ServicesIndexRaw,\n} from './types.ts';\n\nexport class NugetV3Api {\n static readonly cacheNamespace = 'datasource-nuget-v3';\n\n async getResourceUrl(\n http: Http,\n url: string,\n resourceType = 'RegistrationsBaseUrl',\n ): Promise<string | null> {\n // https://learn.microsoft.com/nuget/api/service-index\n const resultCacheKey = `${url}:${resourceType}`;\n const cachedResult = await packageCache.get<string>(\n NugetV3Api.cacheNamespace,\n resultCacheKey,\n );\n\n /* v8 ignore next 3 -- TODO: add test */\n if (cachedResult) {\n return cachedResult;\n }\n let servicesIndexRaw: ServicesIndexRaw | undefined;\n try {\n const responseCacheKey = url;\n servicesIndexRaw = await packageCache.get<ServicesIndexRaw>(\n NugetV3Api.cacheNamespace,\n responseCacheKey,\n );\n if (!servicesIndexRaw) {\n servicesIndexRaw = (\n await http.getJsonUnchecked<ServicesIndexRaw>(url, {\n cacheProvider: memCacheProvider,\n })\n ).body;\n await packageCache.set(\n NugetV3Api.cacheNamespace,\n responseCacheKey,\n servicesIndexRaw,\n 3 * 24 * 60,\n );\n }\n\n const services = servicesIndexRaw.resources\n .map(({ '@id': serviceId, '@type': t }) => ({\n serviceId,\n type: t?.split('/')?.shift(),\n version: t?.split('/')?.pop(),\n }))\n .filter(\n ({ type, version }) => type === resourceType && semver.valid(version),\n )\n .sort((x, y) =>\n x.version && y.version\n ? semver.compare(x.version, y.version)\n : /* istanbul ignore next: hard to test */ 0,\n );\n\n if (services.length === 0) {\n await packageCache.set(\n NugetV3Api.cacheNamespace,\n resultCacheKey,\n null,\n 60,\n );\n logger.debug(\n { url, servicesIndexRaw },\n `no ${resourceType} services found`,\n );\n return null;\n }\n\n const { serviceId, version } = services.pop()!;\n\n // istanbul ignore if\n if (\n resourceType === 'RegistrationsBaseUrl' &&\n version &&\n !version.startsWith('3.0.0-') &&\n !semver.satisfies(version, '^3.0.0')\n ) {\n logger.warn(\n { url, version },\n `Nuget: Unknown version returned. Only v3 is supported`,\n );\n }\n\n await packageCache.set(\n NugetV3Api.cacheNamespace,\n resultCacheKey,\n serviceId,\n 60,\n );\n return serviceId;\n } catch (err) {\n // istanbul ignore if: not easy testable with nock\n if (err instanceof ExternalHostError) {\n throw err;\n }\n logger.debug(\n { err, url, servicesIndexRaw },\n `nuget registry failure: can't get ${resourceType}`,\n );\n return null;\n }\n }\n\n async getCatalogEntry(\n http: Http,\n catalogPage: CatalogPage,\n ): Promise<CatalogEntry[]> {\n let items = catalogPage.items;\n if (!items) {\n const url = catalogPage['@id'];\n const catalogPageFull = await http.getJsonUnchecked<CatalogPage>(url);\n items = catalogPageFull.body.items;\n }\n return items.map(({ catalogEntry }) => catalogEntry);\n }\n\n async getReleases(\n http: Http,\n registryUrl: string,\n feedUrl: string,\n pkgName: string,\n ): Promise<ReleaseResult | null> {\n const baseUrl = feedUrl.replace(regEx(/\\/*$/), '');\n const url = `${baseUrl}/${pkgName.toLowerCase()}/index.json`;\n const packageRegistration =\n await http.getJsonUnchecked<PackageRegistration>(url);\n const catalogPages = packageRegistration.body.items || [];\n const catalogPagesQueue = catalogPages.map(\n (page) => (): Promise<CatalogEntry[]> => this.getCatalogEntry(http, page),\n );\n const catalogEntries = (await p.all(catalogPagesQueue))\n .flat()\n .sort((a, b) => sortNugetVersions(a.version, b.version));\n\n let homepage: string | null = null;\n let latestStable: string | null = null;\n let nupkgUrl: string | null = null;\n const releases = catalogEntries.map(\n ({\n version,\n published,\n projectUrl,\n listed,\n packageContent,\n deprecation,\n }) => {\n const release: Release = { version: removeBuildMeta(version) };\n const releaseTimestamp = asTimestamp(published);\n if (releaseTimestamp) {\n release.releaseTimestamp = releaseTimestamp;\n }\n if (\n versioning.isValid(version) &&\n versioning.isStable(version) &&\n listed\n ) {\n latestStable = removeBuildMeta(version);\n homepage = projectUrl ? massageUrl(projectUrl) : homepage;\n nupkgUrl = massageUrl(packageContent);\n }\n\n if (listed === false || deprecation) {\n release.isDeprecated = true;\n }\n return release;\n },\n );\n\n if (!releases.length) {\n return null;\n }\n\n // istanbul ignore next: only happens when no stable version exists\n if (latestStable === null && catalogPages.length) {\n const last = catalogEntries.pop()!;\n latestStable = removeBuildMeta(last.version);\n homepage ??= last.projectUrl ?? null;\n nupkgUrl ??= massageUrl(last.packageContent);\n }\n\n const dep: ReleaseResult = {\n releases,\n };\n\n if (releases.every((release) => release.isDeprecated === true)) {\n dep.deprecationMessage = this.getDeprecationMessage(pkgName);\n }\n\n try {\n const packageBaseAddress = await this.getResourceUrl(\n http,\n registryUrl,\n 'PackageBaseAddress',\n );\n if (isNonEmptyString(packageBaseAddress)) {\n const nuspecUrl = `${ensureTrailingSlash(\n packageBaseAddress,\n )}${pkgName.toLowerCase()}/${\n // TODO: types (#22198)\n latestStable\n }/${pkgName.toLowerCase()}.nuspec`;\n const metaresult = await http.getText(nuspecUrl, {\n cacheProvider: memCacheProvider,\n });\n const nuspec = new XmlDocument(metaresult.body);\n const releaseNotes = nuspec.valueWithPath('metadata.releaseNotes');\n if (releaseNotes) {\n dep.changelogContent = releaseNotes;\n }\n const sourceUrl = nuspec.valueWithPath('metadata.repository@url');\n if (sourceUrl) {\n dep.sourceUrl = massageUrl(sourceUrl);\n }\n } else if (nupkgUrl) {\n const sourceUrl = await this.getSourceUrlFromNupkg(\n http,\n registryUrl,\n pkgName,\n latestStable,\n nupkgUrl,\n );\n if (sourceUrl) {\n dep.sourceUrl = massageUrl(sourceUrl);\n logger.debug(`Determined sourceUrl ${sourceUrl} from ${nupkgUrl}`);\n }\n }\n } catch (err) {\n // istanbul ignore if: not easy testable with nock\n if (err instanceof ExternalHostError) {\n throw err;\n }\n // ignore / silence 404. Seen on proget, if remote connector is used and package is not yet cached\n if (err instanceof HttpError && err.response?.statusCode === 404) {\n logger.debug(\n { registryUrl, pkgName, pkgVersion: latestStable },\n `package manifest (.nuspec) not found`,\n );\n } else {\n logger.debug(\n { err, registryUrl, pkgName, pkgVersion: latestStable },\n `Cannot obtain sourceUrl`,\n );\n }\n }\n\n if (homepage) {\n // only assign if not assigned\n dep.sourceUrl ??= homepage;\n dep.homepage ??= homepage;\n }\n\n return dep;\n }\n\n private async _getSourceUrlFromNupkg(\n http: Http,\n _registryUrl: string,\n packageName: string,\n packageVersion: string | null,\n nupkgUrl: string,\n ): Promise<string | null> {\n /* v8 ignore next 4 */\n if (!getEnv().RENOVATE_X_NUGET_DOWNLOAD_NUPKGS) {\n logger.once.debug('RENOVATE_X_NUGET_DOWNLOAD_NUPKGS is not set');\n return null;\n }\n const cacheDir = await ensureCacheDir('nuget');\n const nupkgFile = upath.join(\n cacheDir,\n `${packageName}.${packageVersion}.nupkg`,\n );\n const nupkgContentsDir = upath.join(\n cacheDir,\n `${packageName}.${packageVersion}`,\n );\n const readStream = http.stream(nupkgUrl);\n try {\n const writeStream = fs.createCacheWriteStream(nupkgFile);\n await fs.pipeline(readStream, writeStream);\n await extract(nupkgFile, { dir: nupkgContentsDir });\n const nuspecFile = upath.join(nupkgContentsDir, `${packageName}.nuspec`);\n const nuspec = new XmlDocument(\n await fs.readCacheFile(nuspecFile, 'utf8'),\n );\n return nuspec.valueWithPath('metadata.repository@url') ?? null;\n } finally {\n await fs.rmCache(nupkgFile);\n await fs.rmCache(nupkgContentsDir);\n }\n }\n\n getSourceUrlFromNupkg(\n http: Http,\n registryUrl: string,\n packageName: string,\n packageVersion: string | null,\n nupkgUrl: string,\n ): Promise<string | null> {\n return withCache(\n {\n namespace: NugetV3Api.cacheNamespace,\n key: `source-url:${registryUrl}:${packageName}`,\n ttlMinutes: 10080, // 1 week\n },\n () =>\n this._getSourceUrlFromNupkg(\n http,\n registryUrl,\n packageName,\n packageVersion,\n nupkgUrl,\n ),\n );\n }\n\n getDeprecationMessage(packageName: string): string {\n return `The package \\`${packageName}\\` is deprecated.`;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"v3.js","names":["packageCache.get","packageCache.set","p.all","versioning","HttpError","fs.createCacheWriteStream","fs.pipeline","fs.readCacheFile","fs.rmCache"],"sources":["../../../../lib/modules/datasource/nuget/v3.ts"],"sourcesContent":["import { isNonEmptyString } from '@sindresorhus/is';\nimport extract from 'extract-zip';\nimport semver from 'semver';\nimport upath from 'upath';\nimport { XmlDocument } from 'xmldoc';\nimport { logger } from '../../../logger/index.ts';\nimport { ExternalHostError } from '../../../types/errors/external-host-error.ts';\nimport * as packageCache from '../../../util/cache/package/index.ts';\nimport { withCache } from '../../../util/cache/package/with-cache.ts';\nimport { getEnv } from '../../../util/env.ts';\nimport * as fs from '../../../util/fs/index.ts';\nimport { ensureCacheDir } from '../../../util/fs/index.ts';\nimport { memCacheProvider } from '../../../util/http/cache/memory-http-cache-provider.ts';\nimport type { Http } from '../../../util/http/index.ts';\nimport { HttpError } from '../../../util/http/index.ts';\nimport * as p from '../../../util/promises.ts';\nimport { regEx } from '../../../util/regex.ts';\nimport { asTimestamp } from '../../../util/timestamp.ts';\nimport { ensureTrailingSlash } from '../../../util/url.ts';\nimport { api as versioning } from '../../versioning/nuget/index.ts';\nimport type { Release, ReleaseResult } from '../types.ts';\nimport { massageUrl, removeBuildMeta, sortNugetVersions } from './common.ts';\nimport type {\n CatalogEntry,\n CatalogPage,\n PackageRegistration,\n ServicesIndexRaw,\n} from './types.ts';\n\nexport class NugetV3Api {\n static readonly cacheNamespace = 'datasource-nuget-v3';\n\n async getResourceUrl(\n http: Http,\n url: string,\n resourceType = 'RegistrationsBaseUrl',\n ): Promise<string | null> {\n // https://learn.microsoft.com/nuget/api/service-index\n const resultCacheKey = `${url}:${resourceType}`;\n const cachedResult = await packageCache.get<string>(\n NugetV3Api.cacheNamespace,\n resultCacheKey,\n );\n\n /* v8 ignore next 3 -- TODO: add test */\n if (cachedResult) {\n return cachedResult;\n }\n let servicesIndexRaw: ServicesIndexRaw | undefined;\n try {\n const responseCacheKey = url;\n servicesIndexRaw = await packageCache.get<ServicesIndexRaw>(\n NugetV3Api.cacheNamespace,\n responseCacheKey,\n );\n if (!servicesIndexRaw) {\n servicesIndexRaw = (\n await http.getJsonUnchecked<ServicesIndexRaw>(url, {\n cacheProvider: memCacheProvider,\n })\n ).body;\n await packageCache.set(\n NugetV3Api.cacheNamespace,\n responseCacheKey,\n servicesIndexRaw,\n 3 * 24 * 60,\n );\n }\n\n const services = servicesIndexRaw.resources\n .map(({ '@id': serviceId, '@type': t }) => ({\n serviceId,\n type: t?.split('/')?.shift(),\n version: t?.split('/')?.pop(),\n }))\n .filter(\n ({ type, version }) => type === resourceType && semver.valid(version),\n )\n .sort((x, y) =>\n x.version && y.version\n ? semver.compare(x.version, y.version)\n : /* istanbul ignore next: hard to test */ 0,\n );\n\n if (services.length === 0) {\n await packageCache.set(\n NugetV3Api.cacheNamespace,\n resultCacheKey,\n null,\n 60,\n );\n logger.debug(\n { url, servicesIndexRaw },\n `no ${resourceType} services found`,\n );\n return null;\n }\n\n const { serviceId, version } = services.pop()!;\n\n // istanbul ignore if\n if (\n resourceType === 'RegistrationsBaseUrl' &&\n version &&\n !version.startsWith('3.0.0-') &&\n !semver.satisfies(version, '^3.0.0')\n ) {\n logger.warn(\n { url, version },\n `Nuget: Unknown version returned. Only v3 is supported`,\n );\n }\n\n await packageCache.set(\n NugetV3Api.cacheNamespace,\n resultCacheKey,\n serviceId,\n 60,\n );\n return serviceId;\n } catch (err) {\n // istanbul ignore if: not easy testable with nock\n if (err instanceof ExternalHostError) {\n throw err;\n }\n logger.debug(\n { err, url, servicesIndexRaw },\n `nuget registry failure: can't get ${resourceType}`,\n );\n return null;\n }\n }\n\n async getCatalogEntry(\n http: Http,\n catalogPage: CatalogPage,\n ): Promise<CatalogEntry[]> {\n let items = catalogPage.items;\n if (!items) {\n const url = catalogPage['@id'];\n const catalogPageFull = await http.getJsonUnchecked<CatalogPage>(url);\n items = catalogPageFull.body.items;\n }\n return items.map(({ catalogEntry }) => catalogEntry);\n }\n\n async getReleases(\n http: Http,\n registryUrl: string,\n feedUrl: string,\n pkgName: string,\n ): Promise<ReleaseResult | null> {\n const baseUrl = feedUrl.replace(regEx(/\\/*$/), '');\n const url = `${baseUrl}/${pkgName.toLowerCase()}/index.json`;\n const packageRegistration =\n await http.getJsonUnchecked<PackageRegistration>(url);\n const catalogPages = packageRegistration.body.items || [];\n const catalogPagesQueue = catalogPages.map(\n (page) => (): Promise<CatalogEntry[]> => this.getCatalogEntry(http, page),\n );\n const catalogEntries = (await p.all(catalogPagesQueue))\n .flat()\n .sort((a, b) => sortNugetVersions(a.version, b.version));\n\n let homepage: string | null = null;\n let latestStable: string | null = null;\n let nupkgUrl: string | null = null;\n const releases = catalogEntries.map(\n ({\n version,\n published,\n projectUrl,\n listed,\n packageContent,\n deprecation,\n }) => {\n const release: Release = { version: removeBuildMeta(version) };\n const releaseTimestamp = asTimestamp(published);\n if (releaseTimestamp) {\n release.releaseTimestamp = releaseTimestamp;\n }\n if (\n versioning.isValid(version) &&\n versioning.isStable(version) &&\n listed\n ) {\n latestStable = removeBuildMeta(version);\n homepage = projectUrl ? massageUrl(projectUrl) : homepage;\n nupkgUrl = massageUrl(packageContent);\n }\n\n if (listed === false || deprecation) {\n release.isDeprecated = true;\n }\n return release;\n },\n );\n\n if (!releases.length) {\n return null;\n }\n\n // istanbul ignore next: only happens when no stable version exists\n if (latestStable === null && catalogPages.length) {\n const last = catalogEntries.pop()!;\n latestStable = removeBuildMeta(last.version);\n homepage ??= last.projectUrl ?? null;\n nupkgUrl ??= massageUrl(last.packageContent);\n }\n\n const dep: ReleaseResult = {\n releases,\n };\n\n if (releases.every((release) => release.isDeprecated === true)) {\n dep.deprecationMessage = this.getDeprecationMessage(pkgName);\n }\n\n try {\n const packageBaseAddress = await this.getResourceUrl(\n http,\n registryUrl,\n 'PackageBaseAddress',\n );\n if (isNonEmptyString(packageBaseAddress)) {\n const nuspecUrl = `${ensureTrailingSlash(\n packageBaseAddress,\n )}${pkgName.toLowerCase()}/${\n // TODO: types (#22198)\n latestStable\n }/${pkgName.toLowerCase()}.nuspec`;\n const metaresult = await http.getText(nuspecUrl, {\n cacheProvider: memCacheProvider,\n });\n const nuspec = new XmlDocument(metaresult.body);\n const releaseNotes = nuspec.valueWithPath('metadata.releaseNotes');\n if (releaseNotes) {\n dep.changelogContent = releaseNotes;\n }\n const sourceUrl = nuspec.valueWithPath('metadata.repository@url');\n if (sourceUrl) {\n dep.sourceUrl = massageUrl(sourceUrl);\n }\n } else if (nupkgUrl) {\n const sourceUrl = await this.getSourceUrlFromNupkg(\n http,\n registryUrl,\n pkgName,\n latestStable,\n nupkgUrl,\n );\n if (sourceUrl) {\n dep.sourceUrl = massageUrl(sourceUrl);\n logger.debug(`Determined sourceUrl ${sourceUrl} from ${nupkgUrl}`);\n }\n }\n } catch (err) {\n // istanbul ignore if: not easy testable with nock\n if (err instanceof ExternalHostError) {\n throw err;\n }\n // ignore / silence 404. Seen on proget, if remote connector is used and package is not yet cached\n if (err instanceof HttpError && err.response?.statusCode === 404) {\n logger.debug(\n { registryUrl, pkgName, pkgVersion: latestStable },\n `package manifest (.nuspec) not found`,\n );\n } else {\n logger.debug(\n { err, registryUrl, pkgName, pkgVersion: latestStable },\n `Cannot obtain sourceUrl`,\n );\n }\n }\n\n if (homepage) {\n // only assign if not assigned\n dep.sourceUrl ??= homepage;\n dep.homepage ??= homepage;\n }\n\n return dep;\n }\n\n private async _getSourceUrlFromNupkg(\n http: Http,\n _registryUrl: string,\n packageName: string,\n packageVersion: string | null,\n nupkgUrl: string,\n ): Promise<string | null> {\n /* v8 ignore next 4 */\n if (!getEnv().RENOVATE_X_NUGET_DOWNLOAD_NUPKGS) {\n logger.once.debug('RENOVATE_X_NUGET_DOWNLOAD_NUPKGS is not set');\n return null;\n }\n const cacheDir = await ensureCacheDir('nuget');\n const nupkgFile = upath.join(\n cacheDir,\n `${packageName}.${packageVersion}.nupkg`,\n );\n const nupkgContentsDir = upath.join(\n cacheDir,\n `${packageName}.${packageVersion}`,\n );\n const readStream = http.stream(nupkgUrl);\n try {\n const writeStream = fs.createCacheWriteStream(nupkgFile);\n await fs.pipeline(readStream, writeStream);\n await extract(nupkgFile, { dir: nupkgContentsDir });\n const nuspecFile = upath.join(nupkgContentsDir, `${packageName}.nuspec`);\n const nuspec = new XmlDocument(\n await fs.readCacheFile(nuspecFile, 'utf8'),\n );\n return nuspec.valueWithPath('metadata.repository@url') ?? null;\n } finally {\n await fs.rmCache(nupkgFile);\n await fs.rmCache(nupkgContentsDir);\n }\n }\n\n getSourceUrlFromNupkg(\n http: Http,\n registryUrl: string,\n packageName: string,\n packageVersion: string | null,\n nupkgUrl: string,\n ): Promise<string | null> {\n return withCache(\n {\n namespace: NugetV3Api.cacheNamespace,\n key: `source-url:${registryUrl}:${packageName}`,\n ttlMinutes: 10080, // 1 week\n },\n () =>\n this._getSourceUrlFromNupkg(\n http,\n registryUrl,\n packageName,\n packageVersion,\n nupkgUrl,\n ),\n );\n }\n\n getDeprecationMessage(packageName: string): string {\n return `The package \\`${packageName}\\` is deprecated.`;\n }\n}\n"],"mappings":"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"}
|
|
@@ -52,7 +52,7 @@ var PackagistDatasource = class PackagistDatasource extends Datasource {
|
|
|
52
52
|
}
|
|
53
53
|
static getPackagistFileUrl(regUrl, regFile) {
|
|
54
54
|
const { key, hash } = regFile;
|
|
55
|
-
return resolveBaseUrl(regUrl, hash ? key.replace("%hash%", hash) : key);
|
|
55
|
+
return resolveBaseUrl(regUrl, hash ? key.replace("%hash%", hash) : /* istanbul ignore next: hard to test */ key);
|
|
56
56
|
}
|
|
57
57
|
async _getPackagistFile(regUrl, regFile) {
|
|
58
58
|
const url = PackagistDatasource.getPackagistFileUrl(regUrl, regFile);
|