renovate 44.149.0 → 44.149.2
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/config/options/index.js +1 -1
- package/dist/config/options/index.js.map +1 -1
- package/dist/config/presets/index.js +2 -1
- package/dist/config/presets/index.js.map +1 -1
- package/dist/global-config-option-defaults.generated.js +1 -1
- package/dist/global-config-option-defaults.generated.js.map +1 -1
- package/dist/modules/datasource/artifactory/index.js +2 -1
- package/dist/modules/datasource/artifactory/index.js.map +1 -1
- package/dist/modules/datasource/aws-eks-addon/index.js +6 -5
- package/dist/modules/datasource/aws-eks-addon/index.js.map +1 -1
- package/dist/modules/datasource/aws-machine-image/index.js +4 -3
- package/dist/modules/datasource/aws-machine-image/index.js.map +1 -1
- package/dist/modules/datasource/aws-rds/index.js +2 -1
- package/dist/modules/datasource/aws-rds/index.js.map +1 -1
- package/dist/modules/datasource/azure-bicep-resource/index.js +2 -1
- package/dist/modules/datasource/azure-bicep-resource/index.js.map +1 -1
- package/dist/modules/datasource/bazel/index.js +2 -1
- package/dist/modules/datasource/bazel/index.js.map +1 -1
- package/dist/modules/datasource/bitrise/index.js +2 -1
- package/dist/modules/datasource/bitrise/index.js.map +1 -1
- package/dist/modules/datasource/buildpacks-registry/index.js +2 -1
- package/dist/modules/datasource/buildpacks-registry/index.js.map +1 -1
- package/dist/modules/datasource/cdnjs/index.js +3 -2
- package/dist/modules/datasource/cdnjs/index.js.map +1 -1
- package/dist/modules/datasource/conan/index.js +3 -2
- package/dist/modules/datasource/conan/index.js.map +1 -1
- package/dist/modules/datasource/conda/index.js +2 -1
- package/dist/modules/datasource/conda/index.js.map +1 -1
- package/dist/modules/datasource/cpan/index.js +1 -1
- package/dist/modules/datasource/cpan/index.js.map +1 -1
- package/dist/modules/datasource/crate/index.js +17 -9
- package/dist/modules/datasource/crate/index.js.map +1 -1
- package/dist/modules/datasource/deb/index.js +3 -2
- package/dist/modules/datasource/deb/index.js.map +1 -1
- package/dist/modules/datasource/deno/index.js +4 -3
- package/dist/modules/datasource/deno/index.js.map +1 -1
- package/dist/modules/datasource/docker/index.js +9 -9
- package/dist/modules/datasource/docker/index.js.map +1 -1
- package/dist/modules/datasource/dotnet-version/index.js +2 -1
- package/dist/modules/datasource/dotnet-version/index.js.map +1 -1
- package/dist/modules/datasource/elm-package/index.js +2 -1
- package/dist/modules/datasource/elm-package/index.js.map +1 -1
- package/dist/modules/datasource/endoflife-date/index.js +2 -1
- package/dist/modules/datasource/endoflife-date/index.js.map +1 -1
- package/dist/modules/datasource/galaxy-collection/index.js +3 -2
- package/dist/modules/datasource/galaxy-collection/index.js.map +1 -1
- package/dist/modules/datasource/git-host-tags.js +6 -5
- package/dist/modules/datasource/git-host-tags.js.map +1 -1
- package/dist/modules/datasource/gitea-tags/base.js +5 -4
- package/dist/modules/datasource/gitea-tags/base.js.map +1 -1
- package/dist/modules/datasource/gitea-tags/util.js +2 -5
- package/dist/modules/datasource/gitea-tags/util.js.map +1 -1
- package/dist/modules/datasource/github-digest/index.js +3 -5
- package/dist/modules/datasource/github-digest/index.js.map +1 -1
- package/dist/modules/datasource/github-release-attachments/index.js +3 -2
- package/dist/modules/datasource/github-release-attachments/index.js.map +1 -1
- package/dist/modules/datasource/gitlab-packages/index.js +2 -1
- package/dist/modules/datasource/gitlab-packages/index.js.map +1 -1
- package/dist/modules/datasource/gitlab-releases/index.js +2 -1
- package/dist/modules/datasource/gitlab-releases/index.js.map +1 -1
- package/dist/modules/datasource/gitlab-tags/index.js +3 -2
- package/dist/modules/datasource/gitlab-tags/index.js.map +1 -1
- package/dist/modules/datasource/glasskube-packages/index.js +2 -1
- package/dist/modules/datasource/glasskube-packages/index.js.map +1 -1
- package/dist/modules/datasource/go/goproxy-parser.js +3 -2
- package/dist/modules/datasource/go/goproxy-parser.js.map +1 -1
- package/dist/modules/datasource/go/index.js +4 -3
- package/dist/modules/datasource/go/index.js.map +1 -1
- package/dist/modules/datasource/go/releases-goproxy.js +6 -3
- package/dist/modules/datasource/go/releases-goproxy.js.map +1 -1
- package/dist/modules/datasource/go/timestamp-cache.js +3 -1
- package/dist/modules/datasource/go/timestamp-cache.js.map +1 -1
- package/dist/modules/datasource/golang-version/index.js +1 -1
- package/dist/modules/datasource/golang-version/index.js.map +1 -1
- package/dist/modules/datasource/gradle-version/index.js +1 -1
- package/dist/modules/datasource/gradle-version/index.js.map +1 -1
- package/dist/modules/datasource/helm/index.js +2 -1
- package/dist/modules/datasource/helm/index.js.map +1 -1
- package/dist/modules/datasource/hermit/index.js +3 -2
- package/dist/modules/datasource/hermit/index.js.map +1 -1
- package/dist/modules/datasource/hex/index.js +3 -2
- package/dist/modules/datasource/hex/index.js.map +1 -1
- package/dist/modules/datasource/hexpm-bob/index.js +2 -1
- package/dist/modules/datasource/hexpm-bob/index.js.map +1 -1
- package/dist/modules/datasource/index.js +4 -3
- package/dist/modules/datasource/index.js.map +1 -1
- package/dist/modules/datasource/java-version/index.js +2 -1
- package/dist/modules/datasource/java-version/index.js.map +1 -1
- package/dist/modules/datasource/jsr/index.js +2 -1
- package/dist/modules/datasource/jsr/index.js.map +1 -1
- package/dist/modules/datasource/maven/index.js +2 -1
- package/dist/modules/datasource/maven/index.js.map +1 -1
- package/dist/modules/datasource/nextcloud/index.js +2 -1
- package/dist/modules/datasource/nextcloud/index.js.map +1 -1
- package/dist/modules/datasource/node-version/index.js +1 -1
- package/dist/modules/datasource/node-version/index.js.map +1 -1
- package/dist/modules/datasource/nuget/v3.js +3 -2
- package/dist/modules/datasource/nuget/v3.js.map +1 -1
- package/dist/modules/datasource/orb/index.js +2 -1
- package/dist/modules/datasource/orb/index.js.map +1 -1
- package/dist/modules/datasource/packagist/index.js +4 -3
- package/dist/modules/datasource/packagist/index.js.map +1 -1
- package/dist/modules/datasource/pod/index.js +2 -1
- package/dist/modules/datasource/pod/index.js.map +1 -1
- package/dist/modules/datasource/python-version/index.js +1 -1
- package/dist/modules/datasource/python-version/index.js.map +1 -1
- package/dist/modules/datasource/repology/index.js +2 -1
- package/dist/modules/datasource/repology/index.js.map +1 -1
- package/dist/modules/datasource/rpm/index.js +2 -1
- package/dist/modules/datasource/rpm/index.js.map +1 -1
- package/dist/modules/datasource/rubygems/index.js +2 -1
- package/dist/modules/datasource/rubygems/index.js.map +1 -1
- package/dist/modules/datasource/rubygems/metadata-cache.js +2 -1
- package/dist/modules/datasource/rubygems/metadata-cache.js.map +1 -1
- package/dist/modules/datasource/sbt-package/index.js +11 -10
- package/dist/modules/datasource/sbt-package/index.js.map +1 -1
- package/dist/modules/datasource/terraform-module/index.js +2 -1
- package/dist/modules/datasource/terraform-module/index.js.map +1 -1
- package/dist/modules/datasource/terraform-provider/index.js +6 -5
- package/dist/modules/datasource/terraform-provider/index.js.map +1 -1
- package/dist/modules/datasource/unity3d/index.js +2 -1
- package/dist/modules/datasource/unity3d/index.js.map +1 -1
- package/dist/modules/datasource/unity3d-packages/index.js +2 -1
- package/dist/modules/datasource/unity3d-packages/index.js.map +1 -1
- package/dist/modules/manager/bazel/parser.js +3 -1
- package/dist/modules/manager/bazel/parser.js.map +1 -1
- package/dist/modules/manager/github-actions/extract.js +2 -1
- package/dist/modules/manager/github-actions/extract.js.map +1 -1
- package/dist/modules/manager/terraform/lockfile/hash.js +2 -1
- package/dist/modules/manager/terraform/lockfile/hash.js.map +1 -1
- package/dist/modules/platform/bitbucket/pr-cache.js +3 -2
- package/dist/modules/platform/bitbucket/pr-cache.js.map +1 -1
- package/dist/modules/platform/bitbucket-server/pr-cache.js +3 -2
- package/dist/modules/platform/bitbucket-server/pr-cache.js.map +1 -1
- package/dist/modules/platform/gitea/pr-cache.js +7 -6
- package/dist/modules/platform/gitea/pr-cache.js.map +1 -1
- package/dist/modules/platform/gitlab/pr-cache.js +3 -2
- package/dist/modules/platform/gitlab/pr-cache.js.map +1 -1
- package/dist/util/cache/package/index.js +1 -1
- package/dist/util/cache/package/key.js +8 -1
- package/dist/util/cache/package/key.js.map +1 -1
- package/dist/util/cache/package/with-cache.js +2 -1
- package/dist/util/cache/package/with-cache.js.map +1 -1
- package/dist/util/exec/containerbase.js +2 -1
- package/dist/util/exec/containerbase.js.map +1 -1
- package/dist/util/github/graphql/cache-strategies/memory-cache-strategy.js +2 -1
- package/dist/util/github/graphql/cache-strategies/memory-cache-strategy.js.map +1 -1
- package/dist/util/github/graphql/datasource-fetcher.js +5 -8
- package/dist/util/github/graphql/datasource-fetcher.js.map +1 -1
- package/dist/util/http/cache/memory-http-cache-provider.js +2 -1
- package/dist/util/http/cache/memory-http-cache-provider.js.map +1 -1
- package/dist/util/http/cache/package-http-cache-provider.js +2 -1
- package/dist/util/http/cache/package-http-cache-provider.js.map +1 -1
- package/dist/util/jsonata.js +2 -1
- package/dist/util/jsonata.js.map +1 -1
- package/dist/util/merge-confidence/index.js +10 -7
- package/dist/util/merge-confidence/index.js.map +1 -1
- package/dist/workers/repository/update/pr/changelog/release-notes.js +4 -5
- package/dist/workers/repository/update/pr/changelog/release-notes.js.map +1 -1
- package/dist/workers/repository/update/pr/changelog/source.js +2 -1
- package/dist/workers/repository/update/pr/changelog/source.js.map +1 -1
- package/package.json +2 -2
- package/renovate-schema.json +2 -2
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import "../../../constants/error-messages.js";
|
|
2
2
|
import { newlineRegex, regEx } from "../../../util/regex.js";
|
|
3
3
|
import { logger } from "../../../logger/index.js";
|
|
4
|
+
import { buildCacheKey } from "../../../util/cache/package/key.js";
|
|
4
5
|
import { ExternalHostError } from "../../../types/errors/external-host-error.js";
|
|
5
6
|
import { withCache } from "../../../util/cache/package/with-cache.js";
|
|
6
7
|
import { refusedHostMessage } from "../../../util/http/util.js";
|
|
@@ -131,7 +132,7 @@ var PodDatasource = class PodDatasource extends Datasource {
|
|
|
131
132
|
return withCache({
|
|
132
133
|
ttlMinutes: 30,
|
|
133
134
|
namespace: `datasource-${PodDatasource.id}`,
|
|
134
|
-
key:
|
|
135
|
+
key: buildCacheKey(config.registryUrl, config.packageName),
|
|
135
136
|
fallback: true
|
|
136
137
|
}, () => this._getReleases(config));
|
|
137
138
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../../lib/modules/datasource/pod/index.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport {\n HOST_BLOCKED,\n HOST_DISABLED,\n} 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 { GithubHttp } from '../../../util/http/github.ts';\nimport type { HttpError } from '../../../util/http/index.ts';\nimport { refusedHostMessage } from '../../../util/http/util.ts';\nimport { newlineRegex, regEx } from '../../../util/regex.ts';\nimport { Datasource } from '../datasource.ts';\nimport { massageGithubUrl } from '../metadata.ts';\nimport type { GetReleasesConfig, ReleaseResult } from '../types.ts';\n\ntype URLFormatOptions =\n | 'withShardWithSpec'\n | 'withShardWithoutSpec'\n | 'withSpecsWithoutShard'\n | 'withoutSpecsWithoutShard';\n\nfunction shardParts(packageName: string): string[] {\n return crypto\n .createHash('md5')\n .update(packageName)\n .digest('hex')\n .slice(0, 3)\n .split('');\n}\n\nconst githubRegex = regEx(\n /(?<hostURL>^https:\\/\\/[a-zA-Z0-9-.]+)\\/(?<account>[^/]+)\\/(?<repo>[^/]+?)(?:\\.git|\\/.*)?$/,\n);\n\nfunction releasesGithubUrl(\n packageName: string,\n opts: {\n hostURL: string;\n account: string;\n repo: string;\n useShard: boolean;\n useSpecs: boolean;\n },\n): string {\n const { hostURL, account, repo, useShard, useSpecs } = opts;\n const prefix =\n hostURL && hostURL !== 'https://github.com'\n ? `${hostURL}/api/v3/repos`\n : 'https://api.github.com/repos';\n const shard = shardParts(packageName).join('/');\n // `Specs` in the pods repo URL is a new requirement for legacy support also allow pod repo URL without `Specs`\n const packageNamePath = useSpecs ? `Specs/${packageName}` : packageName;\n const shardPath = useSpecs\n ? `Specs/${shard}/${packageName}`\n : `${shard}/${packageName}`;\n const suffix = useShard ? shardPath : packageNamePath;\n return `${prefix}/${account}/${repo}/contents/${suffix}`;\n}\n\nfunction handleError(packageName: string, err: HttpError): void {\n const errorData = { packageName, err };\n\n const statusCode = err.response?.statusCode ?? 0;\n if (statusCode === 429 || (statusCode >= 500 && statusCode < 600)) {\n logger.warn({ packageName, err }, `CocoaPods registry failure`);\n throw new ExternalHostError(err);\n }\n\n if (statusCode === 401) {\n logger.debug(errorData, 'Authorization error');\n } else if (statusCode === 404) {\n logger.debug(errorData, 'Package lookup error');\n } else if ([HOST_BLOCKED, HOST_DISABLED].includes(err.message)) {\n logger.trace(errorData, refusedHostMessage(err));\n } else {\n logger.warn(errorData, 'CocoaPods lookup failure: Unknown error');\n }\n}\n\nfunction isDefaultRepo(url: string): boolean {\n const match = githubRegex.exec(url);\n if (match?.groups) {\n const { account, repo } = match.groups;\n return (\n account.toLowerCase() === 'cocoapods' && repo.toLowerCase() === 'specs'\n ); // https://github.com/CocoaPods/Specs.git\n }\n return false;\n}\n\nfunction releasesCDNUrl(packageName: string, registryUrl: string): string {\n const shard = shardParts(packageName).join('_');\n return `${registryUrl}/all_pods_versions_${shard}.txt`;\n}\n\nexport class PodDatasource extends Datasource {\n static readonly id = 'pod';\n\n override getDefaultRegistryUrls(_packageName: string): string[] {\n return ['https://cdn.cocoapods.org'];\n }\n\n override readonly registryStrategy = 'hunt';\n\n githubHttp: GithubHttp;\n\n constructor() {\n super(PodDatasource.id);\n this.githubHttp = new GithubHttp(PodDatasource.id);\n }\n\n private async requestCDN(\n url: string,\n packageName: string,\n ): Promise<string | null> {\n try {\n const resp = await this.http.getText(url);\n if (resp?.body) {\n return resp.body;\n }\n } catch (err) {\n handleError(packageName, err);\n }\n\n return null;\n }\n\n private async requestGithub<T = unknown>(\n url: string,\n packageName: string,\n ): Promise<T | null> {\n try {\n const resp = await this.githubHttp.getJsonUnchecked<T>(url);\n // v8 ignore else -- an empty json body throws before it gets here\n if (resp?.body) {\n return resp.body;\n }\n } catch (err) {\n handleError(packageName, err);\n }\n\n return null;\n }\n\n private async getReleasesFromGithub(\n packageName: string,\n opts: { hostURL: string; account: string; repo: string },\n useShard = true,\n useSpecs = true,\n urlFormatOptions: URLFormatOptions = 'withShardWithSpec',\n ): Promise<ReleaseResult | null> {\n const url = releasesGithubUrl(packageName, { ...opts, useShard, useSpecs });\n const resp = await this.requestGithub<{ name: string }[]>(url, packageName);\n if (resp) {\n const releases = resp.map(({ name }) => ({ version: name }));\n return { releases };\n }\n\n // support different url formats\n switch (urlFormatOptions) {\n case 'withShardWithSpec':\n return this.getReleasesFromGithub(\n packageName,\n opts,\n true,\n false,\n 'withShardWithoutSpec',\n );\n case 'withShardWithoutSpec':\n return this.getReleasesFromGithub(\n packageName,\n opts,\n false,\n true,\n 'withSpecsWithoutShard',\n );\n case 'withSpecsWithoutShard':\n return this.getReleasesFromGithub(\n packageName,\n opts,\n false,\n false,\n 'withoutSpecsWithoutShard',\n );\n case 'withoutSpecsWithoutShard':\n default:\n return null;\n }\n }\n\n private async getReleasesFromCDN(\n packageName: string,\n registryUrl: string,\n ): Promise<ReleaseResult | null> {\n const url = releasesCDNUrl(packageName, registryUrl);\n const resp = await this.requestCDN(url, packageName);\n if (resp) {\n const lines = resp.split(newlineRegex);\n for (const line of lines) {\n const [name, ...versions] = line.split('/');\n if (name === packageName.replace(regEx(/\\/.*$/), '')) {\n const releases = versions.map((version) => ({ version }));\n return { releases };\n }\n }\n }\n return null;\n }\n\n private async _getReleases({\n packageName,\n registryUrl,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n /* v8 ignore next -- should never happen */\n if (!registryUrl) {\n return null;\n }\n\n const podName = packageName.replace(regEx(/\\/.*$/), '');\n let baseUrl = registryUrl.replace(regEx(/\\/+$/), '');\n // In order to not abuse github API limits, query CDN instead\n if (isDefaultRepo(baseUrl)) {\n [baseUrl] = this.getDefaultRegistryUrls('');\n }\n\n let result: ReleaseResult | null = null;\n const match = githubRegex.exec(baseUrl);\n // We would ideally have a reliable way to differentiate between\n // a CDN URL and a Github URL, but we'll start with detecting Artifactory\n if (match?.groups && !baseUrl.includes('/api/pods/')) {\n baseUrl = massageGithubUrl(baseUrl);\n const { hostURL, account, repo } = match.groups;\n const opts = { hostURL, account, repo };\n result = await this.getReleasesFromGithub(podName, opts);\n } else {\n result = await this.getReleasesFromCDN(podName, baseUrl);\n }\n\n return result;\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n return withCache(\n {\n ttlMinutes: 30,\n namespace: `datasource-${PodDatasource.id}`,\n // TODO: types (#22198)\n key: `${config.registryUrl}:${config.packageName}`,\n fallback: true,\n },\n () => this._getReleases(config),\n );\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../../lib/modules/datasource/pod/index.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport {\n HOST_BLOCKED,\n HOST_DISABLED,\n} from '../../../constants/error-messages.ts';\nimport { logger } from '../../../logger/index.ts';\nimport { ExternalHostError } from '../../../types/errors/external-host-error.ts';\nimport { buildCacheKey } from '../../../util/cache/package/key.ts';\nimport { withCache } from '../../../util/cache/package/with-cache.ts';\nimport { GithubHttp } from '../../../util/http/github.ts';\nimport type { HttpError } from '../../../util/http/index.ts';\nimport { refusedHostMessage } from '../../../util/http/util.ts';\nimport { newlineRegex, regEx } from '../../../util/regex.ts';\nimport { Datasource } from '../datasource.ts';\nimport { massageGithubUrl } from '../metadata.ts';\nimport type { GetReleasesConfig, ReleaseResult } from '../types.ts';\n\ntype URLFormatOptions =\n | 'withShardWithSpec'\n | 'withShardWithoutSpec'\n | 'withSpecsWithoutShard'\n | 'withoutSpecsWithoutShard';\n\nfunction shardParts(packageName: string): string[] {\n return crypto\n .createHash('md5')\n .update(packageName)\n .digest('hex')\n .slice(0, 3)\n .split('');\n}\n\nconst githubRegex = regEx(\n /(?<hostURL>^https:\\/\\/[a-zA-Z0-9-.]+)\\/(?<account>[^/]+)\\/(?<repo>[^/]+?)(?:\\.git|\\/.*)?$/,\n);\n\nfunction releasesGithubUrl(\n packageName: string,\n opts: {\n hostURL: string;\n account: string;\n repo: string;\n useShard: boolean;\n useSpecs: boolean;\n },\n): string {\n const { hostURL, account, repo, useShard, useSpecs } = opts;\n const prefix =\n hostURL && hostURL !== 'https://github.com'\n ? `${hostURL}/api/v3/repos`\n : 'https://api.github.com/repos';\n const shard = shardParts(packageName).join('/');\n // `Specs` in the pods repo URL is a new requirement for legacy support also allow pod repo URL without `Specs`\n const packageNamePath = useSpecs ? `Specs/${packageName}` : packageName;\n const shardPath = useSpecs\n ? `Specs/${shard}/${packageName}`\n : `${shard}/${packageName}`;\n const suffix = useShard ? shardPath : packageNamePath;\n return `${prefix}/${account}/${repo}/contents/${suffix}`;\n}\n\nfunction handleError(packageName: string, err: HttpError): void {\n const errorData = { packageName, err };\n\n const statusCode = err.response?.statusCode ?? 0;\n if (statusCode === 429 || (statusCode >= 500 && statusCode < 600)) {\n logger.warn({ packageName, err }, `CocoaPods registry failure`);\n throw new ExternalHostError(err);\n }\n\n if (statusCode === 401) {\n logger.debug(errorData, 'Authorization error');\n } else if (statusCode === 404) {\n logger.debug(errorData, 'Package lookup error');\n } else if ([HOST_BLOCKED, HOST_DISABLED].includes(err.message)) {\n logger.trace(errorData, refusedHostMessage(err));\n } else {\n logger.warn(errorData, 'CocoaPods lookup failure: Unknown error');\n }\n}\n\nfunction isDefaultRepo(url: string): boolean {\n const match = githubRegex.exec(url);\n if (match?.groups) {\n const { account, repo } = match.groups;\n return (\n account.toLowerCase() === 'cocoapods' && repo.toLowerCase() === 'specs'\n ); // https://github.com/CocoaPods/Specs.git\n }\n return false;\n}\n\nfunction releasesCDNUrl(packageName: string, registryUrl: string): string {\n const shard = shardParts(packageName).join('_');\n return `${registryUrl}/all_pods_versions_${shard}.txt`;\n}\n\nexport class PodDatasource extends Datasource {\n static readonly id = 'pod';\n\n override getDefaultRegistryUrls(_packageName: string): string[] {\n return ['https://cdn.cocoapods.org'];\n }\n\n override readonly registryStrategy = 'hunt';\n\n githubHttp: GithubHttp;\n\n constructor() {\n super(PodDatasource.id);\n this.githubHttp = new GithubHttp(PodDatasource.id);\n }\n\n private async requestCDN(\n url: string,\n packageName: string,\n ): Promise<string | null> {\n try {\n const resp = await this.http.getText(url);\n if (resp?.body) {\n return resp.body;\n }\n } catch (err) {\n handleError(packageName, err);\n }\n\n return null;\n }\n\n private async requestGithub<T = unknown>(\n url: string,\n packageName: string,\n ): Promise<T | null> {\n try {\n const resp = await this.githubHttp.getJsonUnchecked<T>(url);\n // v8 ignore else -- an empty json body throws before it gets here\n if (resp?.body) {\n return resp.body;\n }\n } catch (err) {\n handleError(packageName, err);\n }\n\n return null;\n }\n\n private async getReleasesFromGithub(\n packageName: string,\n opts: { hostURL: string; account: string; repo: string },\n useShard = true,\n useSpecs = true,\n urlFormatOptions: URLFormatOptions = 'withShardWithSpec',\n ): Promise<ReleaseResult | null> {\n const url = releasesGithubUrl(packageName, { ...opts, useShard, useSpecs });\n const resp = await this.requestGithub<{ name: string }[]>(url, packageName);\n if (resp) {\n const releases = resp.map(({ name }) => ({ version: name }));\n return { releases };\n }\n\n // support different url formats\n switch (urlFormatOptions) {\n case 'withShardWithSpec':\n return this.getReleasesFromGithub(\n packageName,\n opts,\n true,\n false,\n 'withShardWithoutSpec',\n );\n case 'withShardWithoutSpec':\n return this.getReleasesFromGithub(\n packageName,\n opts,\n false,\n true,\n 'withSpecsWithoutShard',\n );\n case 'withSpecsWithoutShard':\n return this.getReleasesFromGithub(\n packageName,\n opts,\n false,\n false,\n 'withoutSpecsWithoutShard',\n );\n case 'withoutSpecsWithoutShard':\n default:\n return null;\n }\n }\n\n private async getReleasesFromCDN(\n packageName: string,\n registryUrl: string,\n ): Promise<ReleaseResult | null> {\n const url = releasesCDNUrl(packageName, registryUrl);\n const resp = await this.requestCDN(url, packageName);\n if (resp) {\n const lines = resp.split(newlineRegex);\n for (const line of lines) {\n const [name, ...versions] = line.split('/');\n if (name === packageName.replace(regEx(/\\/.*$/), '')) {\n const releases = versions.map((version) => ({ version }));\n return { releases };\n }\n }\n }\n return null;\n }\n\n private async _getReleases({\n packageName,\n registryUrl,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n /* v8 ignore next -- should never happen */\n if (!registryUrl) {\n return null;\n }\n\n const podName = packageName.replace(regEx(/\\/.*$/), '');\n let baseUrl = registryUrl.replace(regEx(/\\/+$/), '');\n // In order to not abuse github API limits, query CDN instead\n if (isDefaultRepo(baseUrl)) {\n [baseUrl] = this.getDefaultRegistryUrls('');\n }\n\n let result: ReleaseResult | null = null;\n const match = githubRegex.exec(baseUrl);\n // We would ideally have a reliable way to differentiate between\n // a CDN URL and a Github URL, but we'll start with detecting Artifactory\n if (match?.groups && !baseUrl.includes('/api/pods/')) {\n baseUrl = massageGithubUrl(baseUrl);\n const { hostURL, account, repo } = match.groups;\n const opts = { hostURL, account, repo };\n result = await this.getReleasesFromGithub(podName, opts);\n } else {\n result = await this.getReleasesFromCDN(podName, baseUrl);\n }\n\n return result;\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n return withCache(\n {\n ttlMinutes: 30,\n namespace: `datasource-${PodDatasource.id}`,\n key: buildCacheKey(config.registryUrl, config.packageName),\n fallback: true,\n },\n () => this._getReleases(config),\n );\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["versioning","eolRegistryUrl","HttpError"],"sources":["../../../../lib/modules/datasource/python-version/index.ts"],"sourcesContent":["import { logger } from '../../../logger/index.ts';\nimport { coerceArray } from '../../../util/array.ts';\nimport { HttpError } from '../../../util/http/index.ts';\nimport { id as versioning } from '../../versioning/python/index.ts';\nimport { Datasource } from '../datasource.ts';\nimport { registryUrl as eolRegistryUrl } from '../endoflife-date/common.ts';\nimport { EndoflifeDateDatasource } from '../endoflife-date/index.ts';\nimport { GithubReleasesDatasource } from '../github-releases/index.ts';\nimport type { GetReleasesConfig, ReleaseResult } from '../types.ts';\nimport { datasource, defaultRegistryUrl, githubBaseUrl } from './common.ts';\nimport { PythonRelease } from './schema.ts';\n\nexport class PythonVersionDatasource extends Datasource {\n static readonly id = datasource;\n pythonPrebuildDatasource: GithubReleasesDatasource;\n pythonEolDatasource: EndoflifeDateDatasource;\n\n constructor() {\n super(datasource);\n this.pythonPrebuildDatasource = new GithubReleasesDatasource();\n this.pythonEolDatasource = new EndoflifeDateDatasource();\n }\n\n override supportsCustomRegistry(_packageName: string): boolean {\n return false;\n }\n\n override getDefaultRegistryUrls(_packageName: string): string[] {\n return [defaultRegistryUrl];\n }\n\n override readonly defaultVersioning = versioning;\n\n async getPrebuildReleases(): Promise<ReleaseResult | null> {\n return await this.pythonPrebuildDatasource.getReleases({\n registryUrl: githubBaseUrl,\n packageName: 'containerbase/python-prebuild',\n });\n }\n\n async getEolReleases(): Promise<ReleaseResult | null> {\n return await this.pythonEolDatasource.getReleases({\n registryUrl: eolRegistryUrl,\n packageName: 'python',\n });\n }\n\n private async fetchReleases({\n registryUrl,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n /* v8 ignore next -- should never happen */\n if (!registryUrl) {\n return null;\n }\n const pythonPrebuildReleases = await this.getPrebuildReleases();\n const pythonPrebuildVersions = new Set<string>(\n pythonPrebuildReleases?.releases.map((release) => release.version),\n );\n const pythonEolReleases = await this.getEolReleases();\n const pythonEolVersions = new Map(\n pythonEolReleases?.releases\n .filter((release) => release.isDeprecated !== undefined)\n .map((release) => [\n release.version.split('.').slice(0, 2).join('.'),\n release.isDeprecated,\n ]),\n );\n const result: ReleaseResult = {\n homepage: 'https://python.org',\n sourceUrl: 'https://github.com/python/cpython',\n registryUrl,\n releases: [],\n };\n try {\n const response = await this.http.getJson(registryUrl, PythonRelease);\n result.releases.push(\n ...response.body\n .filter((release) => release.isStable)\n .filter((release) => pythonPrebuildVersions.has(release.version)),\n );\n } catch (err) {\n if (err instanceof HttpError && err.response?.statusCode === 429) {\n logger.debug(\n { err },\n 'Rate limited by python.org, using prebuild releases',\n );\n result.releases.push(...coerceArray(pythonPrebuildReleases?.releases));\n } else {\n this.handleGenericErrors(err);\n }\n }\n for (const release of result.releases) {\n release.isDeprecated = pythonEolVersions.get(\n release.version.split('.').slice(0, 2).join('.'),\n );\n }\n\n return result.releases.length ? result : null;\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n return this.cached(\n {\n key:
|
|
1
|
+
{"version":3,"file":"index.js","names":["versioning","eolRegistryUrl","HttpError"],"sources":["../../../../lib/modules/datasource/python-version/index.ts"],"sourcesContent":["import { logger } from '../../../logger/index.ts';\nimport { coerceArray } from '../../../util/array.ts';\nimport { HttpError } from '../../../util/http/index.ts';\nimport { id as versioning } from '../../versioning/python/index.ts';\nimport { Datasource } from '../datasource.ts';\nimport { registryUrl as eolRegistryUrl } from '../endoflife-date/common.ts';\nimport { EndoflifeDateDatasource } from '../endoflife-date/index.ts';\nimport { GithubReleasesDatasource } from '../github-releases/index.ts';\nimport type { GetReleasesConfig, ReleaseResult } from '../types.ts';\nimport { datasource, defaultRegistryUrl, githubBaseUrl } from './common.ts';\nimport { PythonRelease } from './schema.ts';\n\nexport class PythonVersionDatasource extends Datasource {\n static readonly id = datasource;\n pythonPrebuildDatasource: GithubReleasesDatasource;\n pythonEolDatasource: EndoflifeDateDatasource;\n\n constructor() {\n super(datasource);\n this.pythonPrebuildDatasource = new GithubReleasesDatasource();\n this.pythonEolDatasource = new EndoflifeDateDatasource();\n }\n\n override supportsCustomRegistry(_packageName: string): boolean {\n return false;\n }\n\n override getDefaultRegistryUrls(_packageName: string): string[] {\n return [defaultRegistryUrl];\n }\n\n override readonly defaultVersioning = versioning;\n\n async getPrebuildReleases(): Promise<ReleaseResult | null> {\n return await this.pythonPrebuildDatasource.getReleases({\n registryUrl: githubBaseUrl,\n packageName: 'containerbase/python-prebuild',\n });\n }\n\n async getEolReleases(): Promise<ReleaseResult | null> {\n return await this.pythonEolDatasource.getReleases({\n registryUrl: eolRegistryUrl,\n packageName: 'python',\n });\n }\n\n private async fetchReleases({\n registryUrl,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n /* v8 ignore next -- should never happen */\n if (!registryUrl) {\n return null;\n }\n const pythonPrebuildReleases = await this.getPrebuildReleases();\n const pythonPrebuildVersions = new Set<string>(\n pythonPrebuildReleases?.releases.map((release) => release.version),\n );\n const pythonEolReleases = await this.getEolReleases();\n const pythonEolVersions = new Map(\n pythonEolReleases?.releases\n .filter((release) => release.isDeprecated !== undefined)\n .map((release) => [\n release.version.split('.').slice(0, 2).join('.'),\n release.isDeprecated,\n ]),\n );\n const result: ReleaseResult = {\n homepage: 'https://python.org',\n sourceUrl: 'https://github.com/python/cpython',\n registryUrl,\n releases: [],\n };\n try {\n const response = await this.http.getJson(registryUrl, PythonRelease);\n result.releases.push(\n ...response.body\n .filter((release) => release.isStable)\n .filter((release) => pythonPrebuildVersions.has(release.version)),\n );\n } catch (err) {\n if (err instanceof HttpError && err.response?.statusCode === 429) {\n logger.debug(\n { err },\n 'Rate limited by python.org, using prebuild releases',\n );\n result.releases.push(...coerceArray(pythonPrebuildReleases?.releases));\n } else {\n this.handleGenericErrors(err);\n }\n }\n for (const release of result.releases) {\n release.isDeprecated = pythonEolVersions.get(\n release.version.split('.').slice(0, 2).join('.'),\n );\n }\n\n return result.releases.length ? result : null;\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n return this.cached(\n {\n key: config.registryUrl!,\n fallback: true,\n },\n () => this.fetchReleases(config),\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;AAYA,IAAa,0BAAb,cAA6C,WAAW;CACtD,OAAgB,KAAK;CACrB;CACA;CAEA,cAAc;EACZ,MAAM,UAAU;EAChB,KAAK,2BAA2B,IAAI,yBAAyB;EAC7D,KAAK,sBAAsB,IAAI,wBAAwB;CACzD;CAEA,uBAAgC,cAA+B;EAC7D,OAAO;CACT;CAEA,uBAAgC,cAAgC;EAC9D,OAAO,CAAC,kBAAkB;CAC5B;CAEA,oBAAsCA;CAEtC,MAAM,sBAAqD;EACzD,OAAO,MAAM,KAAK,yBAAyB,YAAY;GACrD,aAAa;GACb,aAAa;EACf,CAAC;CACH;CAEA,MAAM,iBAAgD;EACpD,OAAO,MAAM,KAAK,oBAAoB,YAAY;GACnCC;GACb,aAAa;EACf,CAAC;CACH;CAEA,MAAc,cAAc,EAC1B,eACmD;;EAEnD,IAAI,CAAC,aACH,OAAO;EAET,MAAM,yBAAyB,MAAM,KAAK,oBAAoB;EAC9D,MAAM,yBAAyB,IAAI,IACjC,wBAAwB,SAAS,KAAK,YAAY,QAAQ,OAAO,CACnE;EACA,MAAM,oBAAoB,MAAM,KAAK,eAAe;EACpD,MAAM,oBAAoB,IAAI,IAC5B,mBAAmB,SAChB,QAAQ,YAAY,QAAQ,iBAAiB,KAAA,CAAS,CAAC,CACvD,KAAK,YAAY,CAChB,QAAQ,QAAQ,MAAM,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,GAC/C,QAAQ,YACV,CAAC,CACL;EACA,MAAM,SAAwB;GAC5B,UAAU;GACV,WAAW;GACX;GACA,UAAU,CAAC;EACb;EACA,IAAI;GACF,MAAM,WAAW,MAAM,KAAK,KAAK,QAAQ,aAAa,aAAa;GACnE,OAAO,SAAS,KACd,GAAG,SAAS,KACT,QAAQ,YAAY,QAAQ,QAAQ,CAAC,CACrC,QAAQ,YAAY,uBAAuB,IAAI,QAAQ,OAAO,CAAC,CACpE;EACF,SAAS,KAAK;GACZ,IAAI,eAAeC,gBAAa,IAAI,UAAU,eAAe,KAAK;IAChE,OAAO,MACL,EAAE,IAAI,GACN,qDACF;IACA,OAAO,SAAS,KAAK,GAAG,YAAY,wBAAwB,QAAQ,CAAC;GACvE,OACE,KAAK,oBAAoB,GAAG;EAEhC;EACA,KAAK,MAAM,WAAW,OAAO,UAC3B,QAAQ,eAAe,kBAAkB,IACvC,QAAQ,QAAQ,MAAM,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,CACjD;EAGF,OAAO,OAAO,SAAS,SAAS,SAAS;CAC3C;CAEA,YAAY,QAA0D;EACpE,OAAO,KAAK,OACV;GACE,KAAK,OAAO;GACZ,UAAU;EACZ,SACM,KAAK,cAAc,MAAM,CACjC;CACF;AACF"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import "../../../constants/error-messages.js";
|
|
2
2
|
import { logger } from "../../../logger/index.js";
|
|
3
3
|
import { getQueryString, joinUrlParts } from "../../../util/url.js";
|
|
4
|
+
import { buildCacheKey } from "../../../util/cache/package/key.js";
|
|
4
5
|
import { ExternalHostError } from "../../../types/errors/external-host-error.js";
|
|
5
6
|
import { withCache } from "../../../util/cache/package/with-cache.js";
|
|
6
7
|
import { refusedHostMessage } from "../../../util/http/util.js";
|
|
@@ -87,7 +88,7 @@ var RepologyDatasource = class RepologyDatasource extends Datasource {
|
|
|
87
88
|
return withCache({
|
|
88
89
|
ttlMinutes: 60,
|
|
89
90
|
namespace: `datasource-${RepologyDatasource.id}`,
|
|
90
|
-
key:
|
|
91
|
+
key: buildCacheKey(registryUrl, repoName, pkgName)
|
|
91
92
|
}, () => this._queryPackage(registryUrl, repoName, pkgName));
|
|
92
93
|
}
|
|
93
94
|
async getReleases({ packageName, registryUrl }) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../../lib/modules/datasource/repology/index.ts"],"sourcesContent":["import { isNonEmptyArray } from '@sindresorhus/is';\nimport {\n HOST_BLOCKED,\n HOST_DISABLED,\n} 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 { refusedHostMessage } from '../../../util/http/util.ts';\nimport { getQueryString, joinUrlParts } from '../../../util/url.ts';\nimport { Datasource } from '../datasource.ts';\nimport type { GetReleasesConfig, ReleaseResult } from '../types.ts';\nimport { type RepologyPackage, RepologyPackages } from './schema.ts';\nimport type { RepologyPackageType } from './types.ts';\n\nconst packageTypes: RepologyPackageType[] = ['binname', 'srcname'];\n\nfunction findPackageInResponse(\n response: RepologyPackage[],\n repoName: string,\n pkgName: string,\n types: RepologyPackageType[],\n): RepologyPackage[] | null {\n const repoPackages = response.filter((pkg) => pkg.repo === repoName);\n\n if (repoPackages.length === 0) {\n // no packages associated with repoName\n return null;\n }\n\n if (repoPackages.length === 1) {\n // repo contains exactly one package, so we can return them safely\n return repoPackages;\n }\n\n // In some cases Repology bundles multiple packages into a single project, which might result in ambiguous results.\n // We need to do additional filtering by matching allowed package types passed as params with package description.\n // Remaining packages are the one we are looking for\n const packagesWithType = repoPackages.filter((pkg) => {\n for (const pkgType of types) {\n if (pkg[pkgType] && pkg[pkgType] === pkgName) {\n return true;\n }\n }\n return false;\n });\n\n return packagesWithType.length > 0 ? packagesWithType : null;\n}\n\nexport class RepologyDatasource extends Datasource {\n static readonly id = 'repology';\n\n override getDefaultRegistryUrls(_packageName: string): string[] {\n return ['https://repology.org/'];\n }\n\n override readonly registryStrategy = 'hunt';\n\n constructor() {\n super(RepologyDatasource.id);\n }\n\n private async queryPackages(url: string): Promise<RepologyPackage[]> {\n try {\n const res = await this.http.getJson(url, RepologyPackages);\n return res.body;\n } catch (err) {\n if (err.statusCode === 404) {\n // Return an array here because the api does not return proper http codes\n // and instead of an 404 error an empty array with code 200 is returned\n // When querying the resolver 404 is thrown if package could not be resolved\n // and 403 if the repo is not supported\n // 403 is handled later because in this case we are trying the API\n return [];\n }\n\n throw err;\n }\n }\n\n private async queryPackagesViaResolver(\n registryUrl: string,\n repoName: string,\n packageName: string,\n packageType: RepologyPackageType,\n ): Promise<RepologyPackage[]> {\n const query = getQueryString({\n repo: repoName,\n name_type: packageType,\n target_page: 'api_v1_project',\n noautoresolve: 'on',\n name: packageName,\n });\n\n // Retrieve list of packages by looking up Repology project\n const packages = await this.queryPackages(\n joinUrlParts(registryUrl, `tools/project-by?${query}`),\n );\n\n return packages;\n }\n\n private async queryPackagesViaAPI(\n registryUrl: string,\n packageName: string,\n ): Promise<RepologyPackage[]> {\n // Directly query the package via the API. This will only work if `packageName` has the\n // same name as the repology project\n const packages = await this.queryPackages(\n joinUrlParts(registryUrl, `api/v1/project`, packageName),\n );\n\n return packages;\n }\n\n private async _queryPackage(\n registryUrl: string,\n repoName: string,\n pkgName: string,\n ): Promise<RepologyPackage[] | undefined> {\n let response: RepologyPackage[];\n // Try getting the packages from tools/project-by first for type binname and\n // afterwards for srcname. This needs to be done first, because some packages\n // resolve to repology projects which have a different name than the package\n // e.g. `pulseaudio-utils` resolves to project `pulseaudio`, BUT there is also\n // a project called `pulseaudio-utils` but it does not contain the package we\n // are looking for.\n try {\n for (const pkgType of packageTypes) {\n response = await this.queryPackagesViaResolver(\n registryUrl,\n repoName,\n pkgName,\n pkgType,\n );\n\n // v8 ignore else -- the resolver either returns a body or throws\n if (response) {\n const pkg = findPackageInResponse(response, repoName, pkgName, [\n pkgType,\n ]);\n if (isNonEmptyArray(pkg)) {\n // exit immediately if package found\n return pkg;\n }\n }\n }\n } catch (err) {\n if (err.statusCode === 403) {\n logger.debug(\n { repoName, pkgName },\n 'Repology does not support tools/project-by lookups for repository. Will try direct API access now',\n );\n\n // If the repository is not supported in tools/project-by we try directly accessing the\n // API. This will support all repositories but requires that the project name is equal to the\n // package name. This won't be always the case but for a good portion we might be able to resolve\n // the package this way.\n response = await this.queryPackagesViaAPI(registryUrl, pkgName);\n const pkg = findPackageInResponse(\n response,\n repoName,\n pkgName,\n packageTypes,\n );\n if (isNonEmptyArray(pkg)) {\n // exit immediately if package found\n return pkg;\n }\n } else if (err.statusCode === 300) {\n logger.warn(\n { repoName, pkgName },\n 'Ambiguous redirection from package name to project name in Repology. Skipping this package',\n );\n return undefined;\n }\n\n throw err;\n }\n\n logger.debug(\n { repoName, pkgName },\n 'Repository or package not found on Repology',\n );\n\n return undefined;\n }\n\n queryPackage(\n registryUrl: string,\n repoName: string,\n pkgName: string,\n ): Promise<RepologyPackage[] | undefined> {\n return withCache(\n {\n ttlMinutes: 60,\n namespace: `datasource-${RepologyDatasource.id}`,\n key: joinUrlParts(registryUrl, repoName, pkgName),\n },\n () => this._queryPackage(registryUrl, repoName, pkgName),\n );\n }\n\n async getReleases({\n packageName,\n registryUrl,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n /* v8 ignore next -- should never happen */\n if (!registryUrl) {\n return null;\n }\n // Ensure lookup name contains both repository and package\n const [repoName, pkgName] = packageName.split('/', 2);\n if (!repoName || !pkgName) {\n throw new ExternalHostError(\n new Error(\n 'Repology lookup name must contain repository and package separated by slash (<repo>/<pkg>)',\n ),\n );\n }\n\n logger.trace(`repology.getReleases(${repoName}, ${pkgName})`);\n try {\n // Try to retrieve (cached) package information from Repology\n const pkg = await this.queryPackage(registryUrl, repoName, pkgName);\n if (!pkg) {\n return null;\n }\n\n // Always prefer origversion if available, otherwise default to version\n // This is required as source packages usually have no origversion\n const releases = pkg.map((item) => ({\n version: item.origversion ?? item.version,\n }));\n return { releases };\n } catch (err) {\n if ([HOST_BLOCKED, HOST_DISABLED].includes(err.message)) {\n logger.trace({ packageName, err }, refusedHostMessage(err));\n } else {\n logger.once.warn(\n { packageName, err },\n 'Repology lookup failed with unexpected error',\n );\n }\n\n this.handleGenericErrors(err);\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../../lib/modules/datasource/repology/index.ts"],"sourcesContent":["import { isNonEmptyArray } from '@sindresorhus/is';\nimport {\n HOST_BLOCKED,\n HOST_DISABLED,\n} from '../../../constants/error-messages.ts';\nimport { logger } from '../../../logger/index.ts';\nimport { ExternalHostError } from '../../../types/errors/external-host-error.ts';\nimport { buildCacheKey } from '../../../util/cache/package/key.ts';\nimport { withCache } from '../../../util/cache/package/with-cache.ts';\nimport { refusedHostMessage } from '../../../util/http/util.ts';\nimport { getQueryString, joinUrlParts } from '../../../util/url.ts';\nimport { Datasource } from '../datasource.ts';\nimport type { GetReleasesConfig, ReleaseResult } from '../types.ts';\nimport { type RepologyPackage, RepologyPackages } from './schema.ts';\nimport type { RepologyPackageType } from './types.ts';\n\nconst packageTypes: RepologyPackageType[] = ['binname', 'srcname'];\n\nfunction findPackageInResponse(\n response: RepologyPackage[],\n repoName: string,\n pkgName: string,\n types: RepologyPackageType[],\n): RepologyPackage[] | null {\n const repoPackages = response.filter((pkg) => pkg.repo === repoName);\n\n if (repoPackages.length === 0) {\n // no packages associated with repoName\n return null;\n }\n\n if (repoPackages.length === 1) {\n // repo contains exactly one package, so we can return them safely\n return repoPackages;\n }\n\n // In some cases Repology bundles multiple packages into a single project, which might result in ambiguous results.\n // We need to do additional filtering by matching allowed package types passed as params with package description.\n // Remaining packages are the one we are looking for\n const packagesWithType = repoPackages.filter((pkg) => {\n for (const pkgType of types) {\n if (pkg[pkgType] && pkg[pkgType] === pkgName) {\n return true;\n }\n }\n return false;\n });\n\n return packagesWithType.length > 0 ? packagesWithType : null;\n}\n\nexport class RepologyDatasource extends Datasource {\n static readonly id = 'repology';\n\n override getDefaultRegistryUrls(_packageName: string): string[] {\n return ['https://repology.org/'];\n }\n\n override readonly registryStrategy = 'hunt';\n\n constructor() {\n super(RepologyDatasource.id);\n }\n\n private async queryPackages(url: string): Promise<RepologyPackage[]> {\n try {\n const res = await this.http.getJson(url, RepologyPackages);\n return res.body;\n } catch (err) {\n if (err.statusCode === 404) {\n // Return an array here because the api does not return proper http codes\n // and instead of an 404 error an empty array with code 200 is returned\n // When querying the resolver 404 is thrown if package could not be resolved\n // and 403 if the repo is not supported\n // 403 is handled later because in this case we are trying the API\n return [];\n }\n\n throw err;\n }\n }\n\n private async queryPackagesViaResolver(\n registryUrl: string,\n repoName: string,\n packageName: string,\n packageType: RepologyPackageType,\n ): Promise<RepologyPackage[]> {\n const query = getQueryString({\n repo: repoName,\n name_type: packageType,\n target_page: 'api_v1_project',\n noautoresolve: 'on',\n name: packageName,\n });\n\n // Retrieve list of packages by looking up Repology project\n const packages = await this.queryPackages(\n joinUrlParts(registryUrl, `tools/project-by?${query}`),\n );\n\n return packages;\n }\n\n private async queryPackagesViaAPI(\n registryUrl: string,\n packageName: string,\n ): Promise<RepologyPackage[]> {\n // Directly query the package via the API. This will only work if `packageName` has the\n // same name as the repology project\n const packages = await this.queryPackages(\n joinUrlParts(registryUrl, `api/v1/project`, packageName),\n );\n\n return packages;\n }\n\n private async _queryPackage(\n registryUrl: string,\n repoName: string,\n pkgName: string,\n ): Promise<RepologyPackage[] | undefined> {\n let response: RepologyPackage[];\n // Try getting the packages from tools/project-by first for type binname and\n // afterwards for srcname. This needs to be done first, because some packages\n // resolve to repology projects which have a different name than the package\n // e.g. `pulseaudio-utils` resolves to project `pulseaudio`, BUT there is also\n // a project called `pulseaudio-utils` but it does not contain the package we\n // are looking for.\n try {\n for (const pkgType of packageTypes) {\n response = await this.queryPackagesViaResolver(\n registryUrl,\n repoName,\n pkgName,\n pkgType,\n );\n\n // v8 ignore else -- the resolver either returns a body or throws\n if (response) {\n const pkg = findPackageInResponse(response, repoName, pkgName, [\n pkgType,\n ]);\n if (isNonEmptyArray(pkg)) {\n // exit immediately if package found\n return pkg;\n }\n }\n }\n } catch (err) {\n if (err.statusCode === 403) {\n logger.debug(\n { repoName, pkgName },\n 'Repology does not support tools/project-by lookups for repository. Will try direct API access now',\n );\n\n // If the repository is not supported in tools/project-by we try directly accessing the\n // API. This will support all repositories but requires that the project name is equal to the\n // package name. This won't be always the case but for a good portion we might be able to resolve\n // the package this way.\n response = await this.queryPackagesViaAPI(registryUrl, pkgName);\n const pkg = findPackageInResponse(\n response,\n repoName,\n pkgName,\n packageTypes,\n );\n if (isNonEmptyArray(pkg)) {\n // exit immediately if package found\n return pkg;\n }\n } else if (err.statusCode === 300) {\n logger.warn(\n { repoName, pkgName },\n 'Ambiguous redirection from package name to project name in Repology. Skipping this package',\n );\n return undefined;\n }\n\n throw err;\n }\n\n logger.debug(\n { repoName, pkgName },\n 'Repository or package not found on Repology',\n );\n\n return undefined;\n }\n\n queryPackage(\n registryUrl: string,\n repoName: string,\n pkgName: string,\n ): Promise<RepologyPackage[] | undefined> {\n return withCache(\n {\n ttlMinutes: 60,\n namespace: `datasource-${RepologyDatasource.id}`,\n key: buildCacheKey(registryUrl, repoName, pkgName),\n },\n () => this._queryPackage(registryUrl, repoName, pkgName),\n );\n }\n\n async getReleases({\n packageName,\n registryUrl,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n /* v8 ignore next -- should never happen */\n if (!registryUrl) {\n return null;\n }\n // Ensure lookup name contains both repository and package\n const [repoName, pkgName] = packageName.split('/', 2);\n if (!repoName || !pkgName) {\n throw new ExternalHostError(\n new Error(\n 'Repology lookup name must contain repository and package separated by slash (<repo>/<pkg>)',\n ),\n );\n }\n\n logger.trace(`repology.getReleases(${repoName}, ${pkgName})`);\n try {\n // Try to retrieve (cached) package information from Repology\n const pkg = await this.queryPackage(registryUrl, repoName, pkgName);\n if (!pkg) {\n return null;\n }\n\n // Always prefer origversion if available, otherwise default to version\n // This is required as source packages usually have no origversion\n const releases = pkg.map((item) => ({\n version: item.origversion ?? item.version,\n }));\n return { releases };\n } catch (err) {\n if ([HOST_BLOCKED, HOST_DISABLED].includes(err.message)) {\n logger.trace({ packageName, err }, refusedHostMessage(err));\n } else {\n logger.once.warn(\n { packageName, err },\n 'Repology lookup failed with unexpected error',\n );\n }\n\n this.handleGenericErrors(err);\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { buildCacheKey } from "../../../util/cache/package/key.js";
|
|
1
2
|
import { Datasource } from "../datasource.js";
|
|
2
3
|
import { RpmXmlMetadataProvider } from "./providers/xml.js";
|
|
3
4
|
import { fetchPrimaryUrl } from "./repomd.js";
|
|
@@ -43,7 +44,7 @@ var RpmDatasource = class RpmDatasource extends Datasource {
|
|
|
43
44
|
}
|
|
44
45
|
getReleases(config) {
|
|
45
46
|
return this.cached({
|
|
46
|
-
key:
|
|
47
|
+
key: buildCacheKey(config.registryUrl, config.packageName),
|
|
47
48
|
ttlMinutes: 1440,
|
|
48
49
|
fallback: true
|
|
49
50
|
}, () => this.fetchReleases(config));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../../lib/modules/datasource/rpm/index.ts"],"sourcesContent":["import { Datasource } from '../datasource.ts';\nimport type { GetReleasesConfig, ReleaseResult } from '../types.ts';\nimport { datasource } from './common.ts';\nimport { RpmXmlMetadataProvider } from './providers/xml.ts';\nimport { fetchPrimaryUrl } from './repomd.ts';\n\nexport class RpmDatasource extends Datasource {\n static readonly id = datasource;\n\n private readonly xmlProvider: RpmXmlMetadataProvider;\n\n constructor() {\n super(RpmDatasource.id);\n this.xmlProvider = new RpmXmlMetadataProvider(this.http);\n }\n\n /**\n * Users are able to specify custom RPM repositories as long as they follow the format.\n * There is a URI http://linux.duke.edu/metadata/common in the <sha>-primary.xml.\n * But according to this post, it's not something we can really look into or reference.\n * @see{https://lists.rpm.org/pipermail/rpm-ecosystem/2015-October/000283.html}\n */\n override supportsCustomRegistry(_packageName: string): boolean {\n return true;\n }\n\n /**\n * Users can specify multiple repositories and the datasource will aggregate the releases\n * @example\n * Every Fedora release has \"release\" and \"updates\" repositories.\n * To get the latest package version, these repositories should be aggregated.\n */\n override readonly registryStrategy = 'merge';\n\n /**\n * Fetches the release information for a given package from the registry URL.\n *\n * @param registryUrl - the registryUrl should be the folder which contains repodata.xml and its corresponding file list <sha256>-primary.xml.gz, e.g.: https://packages.microsoft.com/azurelinux/3.0/prod/cloud-native/x86_64/repodata/\n * @param packageName - the name of the package to fetch releases for.\n * @returns The release result if the package is found, otherwise null.\n */\n private async fetchReleases({\n registryUrl,\n packageName,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n if (!registryUrl || !packageName) {\n return null;\n }\n\n try {\n const primaryUrl = await this.getPrimaryUrl(registryUrl);\n return await this.getReleasesByPackageName(primaryUrl, packageName);\n } catch (err) {\n this.handleGenericErrors(err);\n }\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n return this.cached(\n {\n key:
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../../lib/modules/datasource/rpm/index.ts"],"sourcesContent":["import { buildCacheKey } from '../../../util/cache/package/key.ts';\nimport { Datasource } from '../datasource.ts';\nimport type { GetReleasesConfig, ReleaseResult } from '../types.ts';\nimport { datasource } from './common.ts';\nimport { RpmXmlMetadataProvider } from './providers/xml.ts';\nimport { fetchPrimaryUrl } from './repomd.ts';\n\nexport class RpmDatasource extends Datasource {\n static readonly id = datasource;\n\n private readonly xmlProvider: RpmXmlMetadataProvider;\n\n constructor() {\n super(RpmDatasource.id);\n this.xmlProvider = new RpmXmlMetadataProvider(this.http);\n }\n\n /**\n * Users are able to specify custom RPM repositories as long as they follow the format.\n * There is a URI http://linux.duke.edu/metadata/common in the <sha>-primary.xml.\n * But according to this post, it's not something we can really look into or reference.\n * @see{https://lists.rpm.org/pipermail/rpm-ecosystem/2015-October/000283.html}\n */\n override supportsCustomRegistry(_packageName: string): boolean {\n return true;\n }\n\n /**\n * Users can specify multiple repositories and the datasource will aggregate the releases\n * @example\n * Every Fedora release has \"release\" and \"updates\" repositories.\n * To get the latest package version, these repositories should be aggregated.\n */\n override readonly registryStrategy = 'merge';\n\n /**\n * Fetches the release information for a given package from the registry URL.\n *\n * @param registryUrl - the registryUrl should be the folder which contains repodata.xml and its corresponding file list <sha256>-primary.xml.gz, e.g.: https://packages.microsoft.com/azurelinux/3.0/prod/cloud-native/x86_64/repodata/\n * @param packageName - the name of the package to fetch releases for.\n * @returns The release result if the package is found, otherwise null.\n */\n private async fetchReleases({\n registryUrl,\n packageName,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n if (!registryUrl || !packageName) {\n return null;\n }\n\n try {\n const primaryUrl = await this.getPrimaryUrl(registryUrl);\n return await this.getReleasesByPackageName(primaryUrl, packageName);\n } catch (err) {\n this.handleGenericErrors(err);\n }\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n return this.cached(\n {\n key: buildCacheKey(config.registryUrl, config.packageName),\n ttlMinutes: 1440,\n fallback: true,\n },\n () => this.fetchReleases(config),\n );\n }\n\n getPrimaryUrl(registryUrl: string): Promise<string> {\n return this.cached(\n {\n key: registryUrl,\n ttlMinutes: 1440,\n },\n () => fetchPrimaryUrl(this.http, registryUrl),\n );\n }\n\n getReleasesByPackageName(\n primaryUrl: string,\n packageName: string,\n ): Promise<ReleaseResult | null> {\n return this.xmlProvider.getReleases(primaryUrl, packageName);\n }\n}\n"],"mappings":";;;;;AAOA,IAAa,gBAAb,MAAa,sBAAsB,WAAW;CAC5C,OAAgB,KAAA;CAEhB;CAEA,cAAc;EACZ,MAAM,cAAc,EAAE;EACtB,KAAK,cAAc,IAAI,uBAAuB,KAAK,IAAI;CACzD;;;;;;;CAQA,uBAAgC,cAA+B;EAC7D,OAAO;CACT;;;;;;;CAQA,mBAAqC;;;;;;;;CASrC,MAAc,cAAc,EAC1B,aACA,eACmD;EACnD,IAAI,CAAC,eAAe,CAAC,aACnB,OAAO;EAGT,IAAI;GACF,MAAM,aAAa,MAAM,KAAK,cAAc,WAAW;GACvD,OAAO,MAAM,KAAK,yBAAyB,YAAY,WAAW;EACpE,SAAS,KAAK;GACZ,KAAK,oBAAoB,GAAG;EAC9B;CACF;CAEA,YAAY,QAA0D;EACpE,OAAO,KAAK,OACV;GACE,KAAK,cAAc,OAAO,aAAa,OAAO,WAAW;GACzD,YAAY;GACZ,UAAU;EACZ,SACM,KAAK,cAAc,MAAM,CACjC;CACF;CAEA,cAAc,aAAsC;EAClD,OAAO,KAAK,OACV;GACE,KAAK;GACL,YAAY;EACd,SACM,gBAAgB,KAAK,MAAM,WAAW,CAC9C;CACF;CAEA,yBACE,YACA,aAC+B;EAC/B,OAAO,KAAK,YAAY,YAAY,YAAY,WAAW;CAC7D;AACF"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { logger } from "../../../logger/index.js";
|
|
2
2
|
import { getQueryString, joinUrlParts, parseUrl } from "../../../util/url.js";
|
|
3
|
+
import { buildCacheKey } from "../../../util/cache/package/key.js";
|
|
3
4
|
import { id } from "../../versioning/ruby/index.js";
|
|
4
5
|
import { RequestError } from "../../../util/http/got.js";
|
|
5
6
|
import { AsyncResult, Result } from "../../../util/result.js";
|
|
@@ -56,7 +57,7 @@ var RubygemsDatasource = class RubygemsDatasource extends Datasource {
|
|
|
56
57
|
getReleases(config) {
|
|
57
58
|
const registryHostname = parseUrl(config.registryUrl)?.hostname;
|
|
58
59
|
return this.cached({
|
|
59
|
-
key:
|
|
60
|
+
key: buildCacheKey("releases", config.registryUrl, config.packageName),
|
|
60
61
|
fallback: true,
|
|
61
62
|
cacheable: registryHostname === "rubygems.org"
|
|
62
63
|
}, () => this.fetchReleases(config));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["HttpError","rubyVersioning.id"],"sources":["../../../../lib/modules/datasource/rubygems/index.ts"],"sourcesContent":["import { Marshal } from '@qnighy/marshal';\nimport type { ZodError } from 'zod/v4';\nimport { logger } from '../../../logger/index.ts';\nimport { Http, HttpError } from '../../../util/http/index.ts';\nimport { AsyncResult, Result } from '../../../util/result.ts';\nimport { getQueryString, joinUrlParts, parseUrl } from '../../../util/url.ts';\nimport * as rubyVersioning from '../../versioning/ruby/index.ts';\nimport { Datasource } from '../datasource.ts';\nimport type { GetReleasesConfig, ReleaseResult } from '../types.ts';\nimport { getV1Releases } from './common.ts';\nimport { MetadataCache } from './metadata-cache.ts';\nimport { GemInfo, MarshalledVersionInfo } from './schema.ts';\nimport { VersionsEndpointCache } from './versions-endpoint-cache.ts';\n\nfunction unlessServerSide<\n T extends NonNullable<unknown>,\n E extends NonNullable<unknown>,\n>(err: E, cb: () => AsyncResult<T, E>): AsyncResult<T, E> {\n if (err instanceof HttpError && err.response?.statusCode) {\n const code = err.response.statusCode;\n if (code >= 500 && code <= 599) {\n return AsyncResult.err(err);\n }\n }\n return cb();\n}\n\nexport class RubygemsDatasource extends Datasource {\n static readonly id = 'rubygems';\n\n private metadataCache: MetadataCache;\n\n constructor() {\n super(RubygemsDatasource.id, new Http(RubygemsDatasource.id));\n this.versionsEndpointCache = new VersionsEndpointCache(this.http);\n this.metadataCache = new MetadataCache(this.http);\n }\n\n override getDefaultRegistryUrls(_packageName: string): string[] {\n return ['https://rubygems.org'];\n }\n\n override readonly defaultVersioning = rubyVersioning.id;\n\n override readonly registryStrategy = 'hunt';\n\n private readonly versionsEndpointCache: VersionsEndpointCache;\n\n override readonly releaseTimestampSupport = true;\n override readonly releaseTimestampNote =\n 'The release timestamp is determined from the `created_at` field in the results.';\n override readonly sourceUrlSupport = 'release';\n override readonly sourceUrlNote =\n 'The source URL is determined from the `source_code_uri` field in the results.';\n\n private async fetchReleases({\n packageName,\n registryUrl,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n /* v8 ignore next -- should never happen */\n if (!registryUrl) {\n return null;\n }\n\n const registryHostname = parseUrl(registryUrl)?.hostname;\n\n let result: AsyncResult<ReleaseResult, Error | string>;\n if (registryHostname === 'rubygems.org') {\n result = Result.wrap(\n this.versionsEndpointCache.getVersions(registryUrl, packageName),\n ).transform((versions) =>\n this.metadataCache.getRelease(registryUrl, packageName, versions),\n );\n } else if (\n registryHostname === 'rubygems.pkg.github.com' ||\n registryHostname === 'gitlab.com'\n ) {\n result = this.getReleasesViaDeprecatedAPI(registryUrl, packageName);\n } else {\n result = getV1Releases(this.http, registryUrl, packageName)\n .catch((err) =>\n unlessServerSide(err, () =>\n this.getReleasesViaInfoEndpoint(registryUrl, packageName),\n ),\n )\n .catch((err) =>\n unlessServerSide(err, () =>\n this.getReleasesViaDeprecatedAPI(registryUrl, packageName),\n ),\n );\n }\n\n const { val, err } = await result.unwrap();\n if (val) {\n return val;\n }\n\n if (err instanceof Error) {\n this.handleGenericErrors(err);\n }\n\n logger.debug({ packageName, registryUrl }, `Rubygems fetch error: ${err}`);\n return null;\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n const registryHostname = parseUrl(config.registryUrl)?.hostname;\n return this.cached(\n {\n
|
|
1
|
+
{"version":3,"file":"index.js","names":["HttpError","rubyVersioning.id"],"sources":["../../../../lib/modules/datasource/rubygems/index.ts"],"sourcesContent":["import { Marshal } from '@qnighy/marshal';\nimport type { ZodError } from 'zod/v4';\nimport { logger } from '../../../logger/index.ts';\nimport { buildCacheKey } from '../../../util/cache/package/key.ts';\nimport { Http, HttpError } from '../../../util/http/index.ts';\nimport { AsyncResult, Result } from '../../../util/result.ts';\nimport { getQueryString, joinUrlParts, parseUrl } from '../../../util/url.ts';\nimport * as rubyVersioning from '../../versioning/ruby/index.ts';\nimport { Datasource } from '../datasource.ts';\nimport type { GetReleasesConfig, ReleaseResult } from '../types.ts';\nimport { getV1Releases } from './common.ts';\nimport { MetadataCache } from './metadata-cache.ts';\nimport { GemInfo, MarshalledVersionInfo } from './schema.ts';\nimport { VersionsEndpointCache } from './versions-endpoint-cache.ts';\n\nfunction unlessServerSide<\n T extends NonNullable<unknown>,\n E extends NonNullable<unknown>,\n>(err: E, cb: () => AsyncResult<T, E>): AsyncResult<T, E> {\n if (err instanceof HttpError && err.response?.statusCode) {\n const code = err.response.statusCode;\n if (code >= 500 && code <= 599) {\n return AsyncResult.err(err);\n }\n }\n return cb();\n}\n\nexport class RubygemsDatasource extends Datasource {\n static readonly id = 'rubygems';\n\n private metadataCache: MetadataCache;\n\n constructor() {\n super(RubygemsDatasource.id, new Http(RubygemsDatasource.id));\n this.versionsEndpointCache = new VersionsEndpointCache(this.http);\n this.metadataCache = new MetadataCache(this.http);\n }\n\n override getDefaultRegistryUrls(_packageName: string): string[] {\n return ['https://rubygems.org'];\n }\n\n override readonly defaultVersioning = rubyVersioning.id;\n\n override readonly registryStrategy = 'hunt';\n\n private readonly versionsEndpointCache: VersionsEndpointCache;\n\n override readonly releaseTimestampSupport = true;\n override readonly releaseTimestampNote =\n 'The release timestamp is determined from the `created_at` field in the results.';\n override readonly sourceUrlSupport = 'release';\n override readonly sourceUrlNote =\n 'The source URL is determined from the `source_code_uri` field in the results.';\n\n private async fetchReleases({\n packageName,\n registryUrl,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n /* v8 ignore next -- should never happen */\n if (!registryUrl) {\n return null;\n }\n\n const registryHostname = parseUrl(registryUrl)?.hostname;\n\n let result: AsyncResult<ReleaseResult, Error | string>;\n if (registryHostname === 'rubygems.org') {\n result = Result.wrap(\n this.versionsEndpointCache.getVersions(registryUrl, packageName),\n ).transform((versions) =>\n this.metadataCache.getRelease(registryUrl, packageName, versions),\n );\n } else if (\n registryHostname === 'rubygems.pkg.github.com' ||\n registryHostname === 'gitlab.com'\n ) {\n result = this.getReleasesViaDeprecatedAPI(registryUrl, packageName);\n } else {\n result = getV1Releases(this.http, registryUrl, packageName)\n .catch((err) =>\n unlessServerSide(err, () =>\n this.getReleasesViaInfoEndpoint(registryUrl, packageName),\n ),\n )\n .catch((err) =>\n unlessServerSide(err, () =>\n this.getReleasesViaDeprecatedAPI(registryUrl, packageName),\n ),\n );\n }\n\n const { val, err } = await result.unwrap();\n if (val) {\n return val;\n }\n\n if (err instanceof Error) {\n this.handleGenericErrors(err);\n }\n\n logger.debug({ packageName, registryUrl }, `Rubygems fetch error: ${err}`);\n return null;\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n const registryHostname = parseUrl(config.registryUrl)?.hostname;\n return this.cached(\n {\n key: buildCacheKey('releases', config.registryUrl, config.packageName),\n fallback: true,\n cacheable: registryHostname === 'rubygems.org',\n },\n () => this.fetchReleases(config),\n );\n }\n\n private getReleasesViaInfoEndpoint(\n registryUrl: string,\n packageName: string,\n ): AsyncResult<ReleaseResult, Error | ZodError> {\n const url = joinUrlParts(registryUrl, '/info', packageName);\n return Result.wrap(this.http.getText(url))\n .transform(({ body }) => body)\n .parse(GemInfo);\n }\n\n private getReleasesViaDeprecatedAPI(\n registryUrl: string,\n packageName: string,\n ): AsyncResult<ReleaseResult, Error | ZodError> {\n const path = joinUrlParts(registryUrl, `/api/v1/dependencies`);\n const query = getQueryString({ gems: packageName });\n const url = `${path}?${query}`;\n const bufPromise = this.http.getBuffer(url);\n return Result.wrap(bufPromise).transform(({ body }) =>\n MarshalledVersionInfo.safeParse(Marshal.parse(body)),\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;;;AAeA,SAAS,iBAGP,KAAQ,IAAgD;CACxD,IAAI,eAAeA,gBAAa,IAAI,UAAU,YAAY;EACxD,MAAM,OAAO,IAAI,SAAS;EAC1B,IAAI,QAAQ,OAAO,QAAQ,KACzB,OAAO,YAAY,IAAI,GAAG;CAE9B;CACA,OAAO,GAAG;AACZ;AAEA,IAAa,qBAAb,MAAa,2BAA2B,WAAW;CACjD,OAAgB,KAAK;CAErB;CAEA,cAAc;EACZ,MAAM,mBAAmB,IAAI,IAAI,KAAK,mBAAmB,EAAE,CAAC;EAC5D,KAAK,wBAAwB,IAAI,sBAAsB,KAAK,IAAI;EAChE,KAAK,gBAAgB,IAAI,cAAc,KAAK,IAAI;CAClD;CAEA,uBAAgC,cAAgC;EAC9D,OAAO,CAAC,sBAAsB;CAChC;CAEA,oBAAsCC;CAEtC,mBAAqC;CAErC;CAEA,0BAA4C;CAC5C,uBACE;CACF,mBAAqC;CACrC,gBACE;CAEF,MAAc,cAAc,EAC1B,aACA,eACmD;;EAEnD,IAAI,CAAC,aACH,OAAO;EAGT,MAAM,mBAAmB,SAAS,WAAW,CAAC,EAAE;EAEhD,IAAI;EACJ,IAAI,qBAAqB,gBACvB,SAAS,OAAO,KACd,KAAK,sBAAsB,YAAY,aAAa,WAAW,CACjE,CAAC,CAAC,WAAW,aACX,KAAK,cAAc,WAAW,aAAa,aAAa,QAAQ,CAClE;OACK,IACL,qBAAqB,6BACrB,qBAAqB,cAErB,SAAS,KAAK,4BAA4B,aAAa,WAAW;OAElE,SAAS,cAAc,KAAK,MAAM,aAAa,WAAW,CAAC,CACxD,OAAO,QACN,iBAAiB,WACf,KAAK,2BAA2B,aAAa,WAAW,CAC1D,CACF,CAAC,CACA,OAAO,QACN,iBAAiB,WACf,KAAK,4BAA4B,aAAa,WAAW,CAC3D,CACF;EAGJ,MAAM,EAAE,KAAK,QAAQ,MAAM,OAAO,OAAO;EACzC,IAAI,KACF,OAAO;EAGT,IAAI,eAAe,OACjB,KAAK,oBAAoB,GAAG;EAG9B,OAAO,MAAM;GAAE;GAAa;EAAY,GAAG,yBAAyB,KAAK;EACzE,OAAO;CACT;CAEA,YAAY,QAA0D;EACpE,MAAM,mBAAmB,SAAS,OAAO,WAAW,CAAC,EAAE;EACvD,OAAO,KAAK,OACV;GACE,KAAK,cAAc,YAAY,OAAO,aAAa,OAAO,WAAW;GACrE,UAAU;GACV,WAAW,qBAAqB;EAClC,SACM,KAAK,cAAc,MAAM,CACjC;CACF;CAEA,2BACE,aACA,aAC8C;EAC9C,MAAM,MAAM,aAAa,aAAa,SAAS,WAAW;EAC1D,OAAO,OAAO,KAAK,KAAK,KAAK,QAAQ,GAAG,CAAC,CAAC,CACvC,WAAW,EAAE,WAAW,IAAI,CAAC,CAC7B,MAAM,OAAO;CAClB;CAEA,4BACE,aACA,aAC8C;EAG9C,MAAM,MAAM,GAFC,aAAa,aAAa,sBAErB,EAAE,GADN,eAAe,EAAE,MAAM,YAAY,CACtB;EAC3B,MAAM,aAAa,KAAK,KAAK,UAAU,GAAG;EAC1C,OAAO,OAAO,KAAK,UAAU,CAAC,CAAC,WAAW,EAAE,WAC1C,sBAAsB,UAAU,QAAQ,MAAM,IAAI,CAAC,CACrD;CACF;AACF"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { logger } from "../../../logger/index.js";
|
|
2
2
|
import { parseUrl } from "../../../util/url.js";
|
|
3
|
+
import { buildCacheKey } from "../../../util/cache/package/key.js";
|
|
3
4
|
import { toSha256 } from "../../../util/hash.js";
|
|
4
5
|
import { get, set } from "../../../util/cache/package/index.js";
|
|
5
6
|
import { Result } from "../../../util/result.js";
|
|
@@ -22,7 +23,7 @@ var MetadataCache = class {
|
|
|
22
23
|
}
|
|
23
24
|
async getRelease(registryUrl, packageName, versions) {
|
|
24
25
|
const cacheNs = `datasource-rubygems`;
|
|
25
|
-
const cacheKey =
|
|
26
|
+
const cacheKey = buildCacheKey("metadata-cache", registryUrl, packageName);
|
|
26
27
|
const versionsHash = hashVersions(versions);
|
|
27
28
|
function loadCache() {
|
|
28
29
|
return Result.wrapNullable(get(cacheNs, cacheKey), { type: "cache-not-found" }).transform((cache) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metadata-cache.js","names":["packageCache.get","packageCache.set"],"sources":["../../../../lib/modules/datasource/rubygems/metadata-cache.ts"],"sourcesContent":["import { logger } from '../../../logger/index.ts';\nimport * as packageCache from '../../../util/cache/package/index.ts';\nimport { toSha256 } from '../../../util/hash.ts';\nimport type { Http } from '../../../util/http/index.ts';\nimport type { AsyncResult } from '../../../util/result.ts';\nimport { Result } from '../../../util/result.ts';\nimport { parseUrl } from '../../../util/url.ts';\nimport type { ReleaseResult } from '../types.ts';\nimport { getV1Releases } from './common.ts';\n\ninterface CacheRecord {\n hash: string;\n data: ReleaseResult;\n isFallback?: true;\n}\n\nfunction hashVersions(versions: string[]): string {\n return toSha256(versions.sort().join(','));\n}\n\nfunction hashReleases(releases: ReleaseResult): string {\n const versions = releases.releases.map((release) => {\n const platform = release.constraints?.platform?.[0];\n\n if (platform && platform !== 'ruby') {\n return `${release.version}-${platform}`;\n }\n\n return release.version;\n });\n\n return hashVersions(versions);\n}\n\ninterface CacheNotFoundError {\n type: 'cache-not-found';\n}\ninterface CacheStaleError {\n type: 'cache-stale';\n cache: CacheRecord;\n}\ninterface CacheInvalidError {\n type: 'cache-invalid';\n}\ntype CacheLoadError = CacheNotFoundError | CacheStaleError;\ntype CacheError = CacheNotFoundError | CacheStaleError | CacheInvalidError;\n\nexport class MetadataCache {\n private readonly http: Http;\n\n constructor(http: Http) {\n this.http = http;\n }\n\n async getRelease(\n registryUrl: string,\n packageName: string,\n versions: string[],\n ): Promise<ReleaseResult> {\n const cacheNs = `datasource-rubygems`;\n const cacheKey =
|
|
1
|
+
{"version":3,"file":"metadata-cache.js","names":["packageCache.get","packageCache.set"],"sources":["../../../../lib/modules/datasource/rubygems/metadata-cache.ts"],"sourcesContent":["import { logger } from '../../../logger/index.ts';\nimport * as packageCache from '../../../util/cache/package/index.ts';\nimport { buildCacheKey } from '../../../util/cache/package/key.ts';\nimport { toSha256 } from '../../../util/hash.ts';\nimport type { Http } from '../../../util/http/index.ts';\nimport type { AsyncResult } from '../../../util/result.ts';\nimport { Result } from '../../../util/result.ts';\nimport { parseUrl } from '../../../util/url.ts';\nimport type { ReleaseResult } from '../types.ts';\nimport { getV1Releases } from './common.ts';\n\ninterface CacheRecord {\n hash: string;\n data: ReleaseResult;\n isFallback?: true;\n}\n\nfunction hashVersions(versions: string[]): string {\n return toSha256(versions.sort().join(','));\n}\n\nfunction hashReleases(releases: ReleaseResult): string {\n const versions = releases.releases.map((release) => {\n const platform = release.constraints?.platform?.[0];\n\n if (platform && platform !== 'ruby') {\n return `${release.version}-${platform}`;\n }\n\n return release.version;\n });\n\n return hashVersions(versions);\n}\n\ninterface CacheNotFoundError {\n type: 'cache-not-found';\n}\ninterface CacheStaleError {\n type: 'cache-stale';\n cache: CacheRecord;\n}\ninterface CacheInvalidError {\n type: 'cache-invalid';\n}\ntype CacheLoadError = CacheNotFoundError | CacheStaleError;\ntype CacheError = CacheNotFoundError | CacheStaleError | CacheInvalidError;\n\nexport class MetadataCache {\n private readonly http: Http;\n\n constructor(http: Http) {\n this.http = http;\n }\n\n async getRelease(\n registryUrl: string,\n packageName: string,\n versions: string[],\n ): Promise<ReleaseResult> {\n const cacheNs = `datasource-rubygems`;\n const cacheKey = buildCacheKey('metadata-cache', registryUrl, packageName);\n const versionsHash = hashVersions(versions);\n\n function loadCache(): AsyncResult<ReleaseResult, CacheLoadError> {\n return Result.wrapNullable<CacheRecord, CacheLoadError, CacheLoadError>(\n packageCache.get<CacheRecord>(cacheNs, cacheKey),\n { type: 'cache-not-found' },\n ).transform((cache) => {\n return versionsHash === cache.hash\n ? Result.ok(cache.data)\n : Result.err({ type: 'cache-stale', cache });\n });\n }\n\n async function saveCache(\n cache: CacheRecord,\n ttlMinutes = 100 * 24 * 60,\n ttlDelta = 10 * 24 * 60,\n ): Promise<void> {\n const registryHostname = parseUrl(registryUrl)?.hostname;\n // v8 ignore else -- needs a save against a non-rubygems.org host\n if (registryHostname === 'rubygems.org') {\n const ttlRandomDelta = Math.floor(Math.random() * ttlDelta);\n const ttl = ttlMinutes + ttlRandomDelta;\n await packageCache.set(cacheNs, cacheKey, cache, ttl);\n }\n }\n\n return await loadCache()\n .catch((err) =>\n getV1Releases(this.http, registryUrl, packageName).transform(\n async (\n data: ReleaseResult,\n ): Promise<Result<ReleaseResult, CacheError>> => {\n const dataHash = hashReleases(data);\n if (dataHash === versionsHash) {\n await saveCache({\n hash: dataHash,\n data,\n });\n return Result.ok(data);\n }\n\n /**\n * Return stale cache for 24 hours,\n * if metadata is inconsistent with versions list.\n */\n if (err.type === 'cache-stale') {\n const staleCache = err.cache;\n // v8 ignore else -- needs a stale cache already marked fallback\n if (!staleCache.isFallback) {\n await saveCache(\n { ...staleCache, isFallback: true },\n 24 * 60,\n 0,\n );\n }\n return Result.ok(staleCache.data);\n }\n\n return Result.err({ type: 'cache-invalid' });\n },\n ),\n )\n .catch((err) => {\n logger.debug(\n { err },\n 'Rubygems: error fetching rubygems data, falling back to versions-only result',\n );\n const releases = versions.map((version) => ({ version }));\n return Result.ok({ releases });\n })\n .unwrapOrThrow();\n }\n}\n"],"mappings":";;;;;;;;AAiBA,SAAS,aAAa,UAA4B;CAChD,OAAO,SAAS,SAAS,KAAK,CAAC,CAAC,KAAK,GAAG,CAAC;AAC3C;AAEA,SAAS,aAAa,UAAiC;CAWrD,OAAO,aAVU,SAAS,SAAS,KAAK,YAAY;EAClD,MAAM,WAAW,QAAQ,aAAa,WAAW;EAEjD,IAAI,YAAY,aAAa,QAC3B,OAAO,GAAG,QAAQ,QAAQ,GAAG;EAG/B,OAAO,QAAQ;CACjB,CAE2B,CAAC;AAC9B;AAeA,IAAa,gBAAb,MAA2B;CACzB;CAEA,YAAY,MAAY;EACtB,KAAK,OAAO;CACd;CAEA,MAAM,WACJ,aACA,aACA,UACwB;EACxB,MAAM,UAAU;EAChB,MAAM,WAAW,cAAc,kBAAkB,aAAa,WAAW;EACzE,MAAM,eAAe,aAAa,QAAQ;EAE1C,SAAS,YAAwD;GAC/D,OAAO,OAAO,aACZA,IAA8B,SAAS,QAAQ,GAC/C,EAAE,MAAM,kBAAkB,CAC5B,CAAC,CAAC,WAAW,UAAU;IACrB,OAAO,iBAAiB,MAAM,OAC1B,OAAO,GAAG,MAAM,IAAI,IACpB,OAAO,IAAI;KAAE,MAAM;KAAe;IAAM,CAAC;GAC/C,CAAC;EACH;EAEA,eAAe,UACb,OACA,aAAa,OACb,WAAW,OACI;;GAGf,IAFyB,SAAS,WAAW,CAAC,EAAE,aAEvB,gBAAgB;IAEvC,MAAM,MAAM,aADW,KAAK,MAAM,KAAK,OAAO,IAAI,QACZ;IACtC,MAAMC,IAAiB,SAAS,UAAU,OAAO,GAAG;GACtD;EACF;EAEA,OAAO,MAAM,UAAU,CAAC,CACrB,OAAO,QACN,cAAc,KAAK,MAAM,aAAa,WAAW,CAAC,CAAC,UACjD,OACE,SAC+C;GAC/C,MAAM,WAAW,aAAa,IAAI;GAClC,IAAI,aAAa,cAAc;IAC7B,MAAM,UAAU;KACd,MAAM;KACN;IACF,CAAC;IACD,OAAO,OAAO,GAAG,IAAI;GACvB;;;;;GAMA,IAAI,IAAI,SAAS,eAAe;IAC9B,MAAM,aAAa,IAAI;;IAEvB,IAAI,CAAC,WAAW,YACd,MAAM,UACJ;KAAE,GAAG;KAAY,YAAY;IAAK,GAClC,MACA,CACF;IAEF,OAAO,OAAO,GAAG,WAAW,IAAI;GAClC;GAEA,OAAO,OAAO,IAAI,EAAE,MAAM,gBAAgB,CAAC;EAC7C,CACF,CACF,CAAC,CACA,OAAO,QAAQ;GACd,OAAO,MACL,EAAE,IAAI,GACN,8EACF;GACA,MAAM,WAAW,SAAS,KAAK,aAAa,EAAE,QAAQ,EAAE;GACxD,OAAO,OAAO,GAAG,EAAE,SAAS,CAAC;EAC/B,CAAC,CAAC,CACD,cAAc;CACnB;AACF"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { regEx } from "../../../util/regex.js";
|
|
2
2
|
import { logger } from "../../../logger/index.js";
|
|
3
3
|
import { ensureTrailingSlash, parseUrl, trimTrailingSlash } from "../../../util/url.js";
|
|
4
|
+
import { buildCacheKey } from "../../../util/cache/package/key.js";
|
|
4
5
|
import { compare } from "../../versioning/maven/compare.js";
|
|
5
6
|
import "../../versioning/ivy/index.js";
|
|
6
7
|
import { get, set } from "../../../util/cache/package/index.js";
|
|
@@ -14,6 +15,10 @@ import { extractPageLinks, getLatestVersion } from "./util.js";
|
|
|
14
15
|
import upath from "upath";
|
|
15
16
|
import { XmlDocument } from "xmldoc";
|
|
16
17
|
//#region lib/modules/datasource/sbt-package/index.ts
|
|
18
|
+
/** Returns the package cache key of the package URLs found for a package. */
|
|
19
|
+
function getPackageUrlsCacheKey(registryUrl, packageName) {
|
|
20
|
+
return buildCacheKey("package-urls", registryUrl, packageName);
|
|
21
|
+
}
|
|
17
22
|
var SbtPackageDatasource = class SbtPackageDatasource extends MavenDatasource {
|
|
18
23
|
static id = "sbt-package";
|
|
19
24
|
getDefaultRegistryUrls(_packageName) {
|
|
@@ -39,7 +44,7 @@ var SbtPackageDatasource = class SbtPackageDatasource extends MavenDatasource {
|
|
|
39
44
|
const { groupId, artifactId, scalaVersion } = SbtPackageDatasource.parseDepCoordinate(packageName);
|
|
40
45
|
const groupIdSplit = groupId.split(".");
|
|
41
46
|
const repoRootUrl = ensureTrailingSlash(registryUrl);
|
|
42
|
-
const validRootUrlKey =
|
|
47
|
+
const validRootUrlKey = buildCacheKey("valid-root-url", registryUrl, packageName);
|
|
43
48
|
const validRootUrl = await get("datasource-sbt-package", validRootUrlKey);
|
|
44
49
|
const packageRootUrls = [];
|
|
45
50
|
// istanbul ignore if: not easily testable
|
|
@@ -82,7 +87,7 @@ var SbtPackageDatasource = class SbtPackageDatasource extends MavenDatasource {
|
|
|
82
87
|
break;
|
|
83
88
|
}
|
|
84
89
|
if (!packageUrls) return null;
|
|
85
|
-
const invalidPackageUrlsKey =
|
|
90
|
+
const invalidPackageUrlsKey = buildCacheKey("invalid-package-urls", registryUrl, packageName);
|
|
86
91
|
const invalidPackageUrls = new Set(await get("datasource-sbt-package", invalidPackageUrlsKey));
|
|
87
92
|
packageUrls = packageUrls.filter((url) => !invalidPackageUrls.has(url));
|
|
88
93
|
const allVersions = /* @__PURE__ */ new Set();
|
|
@@ -108,10 +113,7 @@ var SbtPackageDatasource = class SbtPackageDatasource extends MavenDatasource {
|
|
|
108
113
|
}
|
|
109
114
|
if (invalidPackageUrls.size > 0) await set("datasource-sbt-package", invalidPackageUrlsKey, [...invalidPackageUrls], 43200);
|
|
110
115
|
// v8 ignore else -- an empty list returns before reaching here
|
|
111
|
-
if (packageUrls.length > 0)
|
|
112
|
-
const packageUrlsKey = `package-urls:${registryUrl}:${packageName}`;
|
|
113
|
-
await set("datasource-sbt-package", packageUrlsKey, packageUrls, 43200);
|
|
114
|
-
}
|
|
116
|
+
if (packageUrls.length > 0) await set("datasource-sbt-package", getPackageUrlsCacheKey(registryUrl, packageName), packageUrls, 43200);
|
|
115
117
|
const versions = [...allVersions];
|
|
116
118
|
if (!versions.length) return null;
|
|
117
119
|
const res = {
|
|
@@ -125,14 +127,13 @@ var SbtPackageDatasource = class SbtPackageDatasource extends MavenDatasource {
|
|
|
125
127
|
return res;
|
|
126
128
|
}
|
|
127
129
|
async getPomInfo(registryUrl, packageName, version, pkgUrls) {
|
|
128
|
-
const packageUrlsKey = `package-urls:${registryUrl}:${packageName}`;
|
|
129
130
|
// istanbul ignore next: will be covered later
|
|
130
|
-
const packageUrls = pkgUrls ?? await get("datasource-sbt-package",
|
|
131
|
+
const packageUrls = pkgUrls ?? await get("datasource-sbt-package", getPackageUrlsCacheKey(registryUrl, packageName));
|
|
131
132
|
// istanbul ignore if
|
|
132
133
|
if (!packageUrls?.length) return null;
|
|
133
134
|
// istanbul ignore if
|
|
134
135
|
if (!version) return null;
|
|
135
|
-
const invalidPomFilesKey =
|
|
136
|
+
const invalidPomFilesKey = buildCacheKey("invalid-pom-files", registryUrl, packageName, version);
|
|
136
137
|
const invalidPomFiles = new Set(await get("datasource-sbt-package", invalidPomFilesKey));
|
|
137
138
|
async function saveCache() {
|
|
138
139
|
if (invalidPomFiles.size > 0) await set("datasource-sbt-package", invalidPomFilesKey, [...invalidPomFiles], 43200);
|
|
@@ -186,7 +187,7 @@ var SbtPackageDatasource = class SbtPackageDatasource extends MavenDatasource {
|
|
|
186
187
|
postprocessRelease(config, release) {
|
|
187
188
|
return withCache({
|
|
188
189
|
namespace: "datasource-sbt-package",
|
|
189
|
-
key:
|
|
190
|
+
key: buildCacheKey("postprocessRelease", config.registryUrl, config.packageName, release.version),
|
|
190
191
|
ttlMinutes: 43200
|
|
191
192
|
}, () => this._postprocessRelease(config, release));
|
|
192
193
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["packageCache.get","packageCache.set"],"sources":["../../../../lib/modules/datasource/sbt-package/index.ts"],"sourcesContent":["import upath from 'upath';\nimport { XmlDocument } from 'xmldoc';\nimport { logger } from '../../../logger/index.ts';\nimport * as packageCache from '../../../util/cache/package/index.ts';\nimport { withCache } from '../../../util/cache/package/with-cache.ts';\nimport { Http } from '../../../util/http/index.ts';\nimport { regEx } from '../../../util/regex.ts';\nimport type { Timestamp } from '../../../util/timestamp.ts';\nimport { asTimestamp } from '../../../util/timestamp.ts';\nimport {\n ensureTrailingSlash,\n parseUrl,\n trimTrailingSlash,\n} from '../../../util/url.ts';\nimport * as ivyVersioning from '../../versioning/ivy/index.ts';\nimport { compare } from '../../versioning/maven/compare.ts';\nimport { MAVEN_REPO } from '../maven/common.ts';\nimport { MavenDatasource } from '../maven/index.ts';\nimport { downloadHttpContent, downloadHttpProtocol } from '../maven/util.ts';\nimport type {\n GetReleasesConfig,\n PostprocessReleaseConfig,\n PostprocessReleaseResult,\n RegistryStrategy,\n Release,\n ReleaseResult,\n} from '../types.ts';\nimport { extractPageLinks, getLatestVersion } from './util.ts';\n\ninterface ScalaDepCoordinate {\n groupId: string;\n artifactId: string;\n scalaVersion?: string;\n}\n\ninterface PomInfo {\n homepage?: string;\n sourceUrl?: string;\n releaseTimestamp?: Timestamp;\n}\n\nexport class SbtPackageDatasource extends MavenDatasource {\n static override readonly id = 'sbt-package';\n\n override getDefaultRegistryUrls(_packageName: string): string[] {\n return [MAVEN_REPO];\n }\n\n override readonly defaultVersioning = ivyVersioning.id;\n\n override readonly registryStrategy: RegistryStrategy = 'hunt';\n\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 = SbtPackageDatasource.id) {\n super(id, new Http('sbt'));\n }\n\n protected static parseDepCoordinate(packageName: string): ScalaDepCoordinate {\n const [groupId, javaArtifactId] = packageName.split(':');\n const [artifactId, scalaVersion] = javaArtifactId.split('_');\n return { groupId, artifactId, scalaVersion };\n }\n\n async getSbtReleases(\n registryUrl: string,\n packageName: string,\n ): Promise<ReleaseResult | null> {\n const { groupId, artifactId, scalaVersion } =\n SbtPackageDatasource.parseDepCoordinate(packageName);\n\n const groupIdSplit = groupId.split('.');\n const repoRootUrl = ensureTrailingSlash(registryUrl);\n\n const validRootUrlKey = `valid-root-url:${registryUrl}:${packageName}`;\n const validRootUrl = await packageCache.get<string>(\n 'datasource-sbt-package',\n validRootUrlKey,\n );\n\n const packageRootUrls: string[] = [];\n // istanbul ignore if: not easily testable\n if (validRootUrl) {\n packageRootUrls.push(validRootUrl);\n } else {\n function packageRootUrlWith(sep: string): string {\n return `${repoRootUrl}${groupIdSplit.join(sep)}`;\n }\n packageRootUrls.push(ensureTrailingSlash(packageRootUrlWith('/')));\n packageRootUrls.push(ensureTrailingSlash(packageRootUrlWith('.')));\n }\n\n let dependencyUrl: string | undefined;\n let packageUrls: string[] | undefined;\n for (const packageRootUrl of packageRootUrls) {\n const packageRootContent = await downloadHttpContent(\n this.http,\n packageRootUrl,\n );\n if (!packageRootContent) {\n continue;\n }\n\n await packageCache.set(\n 'datasource-sbt-package',\n validRootUrlKey,\n packageRootUrl,\n 30 * 24 * 60,\n );\n\n dependencyUrl = trimTrailingSlash(packageRootUrl);\n\n const parsedPackageRootUrl = parseUrl(packageRootUrl);\n if (!parsedPackageRootUrl) {\n logger.warn({ packageRootUrl }, 'Failed to parse packageURL');\n continue;\n }\n const rootPath = parsedPackageRootUrl.pathname;\n const artifactSubdirs = extractPageLinks(packageRootContent, (href) => {\n const path = href.replace(rootPath, '');\n\n if (\n path.startsWith(`${artifactId}_native`) ||\n path.startsWith(`${artifactId}_sjs`)\n ) {\n return null;\n }\n\n if (path === artifactId || path.startsWith(`${artifactId}_`)) {\n return ensureTrailingSlash(`${packageRootUrl}${path}`);\n }\n\n return null;\n });\n\n if (scalaVersion) {\n const scalaSubdir = artifactSubdirs.find((x) =>\n x.endsWith(`/${artifactId}_${scalaVersion}/`),\n );\n // v8 ignore else -- needs a nested directory listing fixture\n if (scalaSubdir) {\n packageUrls = [scalaSubdir];\n break;\n }\n }\n\n packageUrls = artifactSubdirs;\n break;\n }\n\n if (!packageUrls) {\n return null;\n }\n\n const invalidPackageUrlsKey = `invalid-package-urls:${registryUrl}:${packageName}`;\n const invalidPackageUrls = new Set(\n await packageCache.get<string[]>(\n 'datasource-sbt-package',\n invalidPackageUrlsKey,\n ),\n );\n packageUrls = packageUrls.filter((url) => !invalidPackageUrls.has(url));\n\n const allVersions = new Set<string>();\n for (const pkgUrl of packageUrls) {\n const parsedPkgUrl = parseUrl(pkgUrl);\n if (!parsedPkgUrl) {\n invalidPackageUrls.add(pkgUrl);\n continue;\n }\n\n const packageContent = await downloadHttpContent(this.http, pkgUrl);\n // istanbul ignore if\n if (!packageContent) {\n invalidPackageUrls.add(pkgUrl);\n continue;\n }\n\n const rootPath = parsedPkgUrl.pathname;\n const versions = extractPageLinks(packageContent, (href) => {\n const path = href.replace(rootPath, '');\n if (path.startsWith('.')) {\n return null;\n }\n\n return path;\n });\n\n for (const version of versions) {\n allVersions.add(version);\n }\n }\n\n if (invalidPackageUrls.size > 0) {\n await packageCache.set(\n 'datasource-sbt-package',\n invalidPackageUrlsKey,\n [...invalidPackageUrls],\n 30 * 24 * 60,\n );\n }\n\n // v8 ignore else -- an empty list returns before reaching here\n if (packageUrls.length > 0) {\n const packageUrlsKey = `package-urls:${registryUrl}:${packageName}`;\n await packageCache.set(\n 'datasource-sbt-package',\n packageUrlsKey,\n packageUrls,\n 30 * 24 * 60,\n );\n }\n\n const versions = [...allVersions];\n if (!versions.length) {\n return null;\n }\n\n const releases: Release[] = [...allVersions]\n .sort(compare)\n .map((version) => ({ version }));\n const res: ReleaseResult = { releases, dependencyUrl };\n\n const latestVersion = getLatestVersion(versions);\n const pomInfo = await this.getPomInfo(\n registryUrl,\n packageName,\n latestVersion,\n packageUrls,\n );\n\n if (pomInfo?.homepage) {\n res.homepage = pomInfo.homepage;\n }\n\n if (pomInfo?.sourceUrl) {\n res.sourceUrl = pomInfo.sourceUrl;\n }\n\n return res;\n }\n\n async getPomInfo(\n registryUrl: string,\n packageName: string,\n version: string | null,\n pkgUrls?: string[],\n ): Promise<PomInfo | null> {\n const packageUrlsKey = `package-urls:${registryUrl}:${packageName}`;\n // istanbul ignore next: will be covered later\n const packageUrls =\n pkgUrls ??\n (await packageCache.get<string[]>(\n 'datasource-sbt-package',\n packageUrlsKey,\n ));\n\n // istanbul ignore if\n if (!packageUrls?.length) {\n return null;\n }\n\n // istanbul ignore if\n if (!version) {\n return null;\n }\n\n const invalidPomFilesKey = `invalid-pom-files:${registryUrl}:${packageName}:${version}`;\n const invalidPomFiles = new Set(\n await packageCache.get<string[]>(\n 'datasource-sbt-package',\n invalidPomFilesKey,\n ),\n );\n\n async function saveCache(): Promise<void> {\n if (invalidPomFiles.size > 0) {\n await packageCache.set(\n 'datasource-sbt-package',\n invalidPomFilesKey,\n [...invalidPomFiles],\n 30 * 24 * 60,\n );\n }\n }\n\n for (const packageUrl of packageUrls) {\n const artifactDir = upath.basename(packageUrl);\n const [artifact] = artifactDir.split('_');\n\n for (const pomFilePrefix of [artifactDir, artifact]) {\n const pomFileName = `${pomFilePrefix}-${version}.pom`;\n const pomUrl = `${packageUrl}${version}/${pomFileName}`;\n if (invalidPomFiles.has(pomUrl)) {\n continue;\n }\n\n const res = await downloadHttpProtocol(this.http, pomUrl);\n const { val } = res.unwrap();\n if (!val) {\n invalidPomFiles.add(pomUrl);\n continue;\n }\n\n const result: PomInfo = {};\n\n const releaseTimestamp = asTimestamp(val.lastModified);\n if (releaseTimestamp) {\n result.releaseTimestamp = releaseTimestamp;\n }\n\n const pomXml = new XmlDocument(val.data);\n\n const homepage = pomXml.valueWithPath('url');\n if (homepage) {\n result.homepage = homepage;\n }\n\n const sourceUrl = pomXml.valueWithPath('scm.url');\n if (sourceUrl) {\n result.sourceUrl = sourceUrl\n .replace(regEx(/^scm:/), '')\n .replace(regEx(/^git:/), '')\n .replace(regEx(/^git@github.com:/), 'https://github.com/')\n .replace(regEx(/\\.git$/), '');\n }\n\n await saveCache();\n return result;\n }\n }\n\n await saveCache();\n return null;\n }\n\n override async getReleases(\n config: GetReleasesConfig,\n ): Promise<ReleaseResult | null> {\n const { packageName, registryUrl } = config;\n // istanbul ignore if\n if (!registryUrl) {\n return null;\n }\n\n const sbtReleases = await this.getSbtReleases(registryUrl, packageName);\n if (sbtReleases) {\n return sbtReleases;\n }\n\n logger.debug(\n `Sbt: no versions discovered for ${packageName} listing organization root package folder, fallback to maven datasource for version discovery`,\n );\n const mavenReleaseResult = await super.getReleases(config);\n if (mavenReleaseResult) {\n return mavenReleaseResult;\n }\n\n logger.debug(`Sbt: no versions found for \"${packageName}\"`);\n return null;\n }\n\n private async _postprocessRelease(\n config: PostprocessReleaseConfig,\n release: Release,\n ): Promise<PostprocessReleaseResult> {\n /* v8 ignore next -- should never happen */\n if (!config.registryUrl) {\n return release;\n }\n\n const res = await this.getPomInfo(\n config.registryUrl,\n config.packageName,\n release.version,\n );\n\n // v8 ignore else -- needs a pom fixture carrying no timestamp\n if (res?.releaseTimestamp) {\n release.releaseTimestamp = res.releaseTimestamp;\n }\n\n return release;\n }\n\n override postprocessRelease(\n config: PostprocessReleaseConfig,\n release: Release,\n ): Promise<PostprocessReleaseResult> {\n return withCache(\n {\n namespace: 'datasource-sbt-package',\n key: `postprocessRelease:${config.registryUrl}:${config.packageName}:${release.version}`,\n ttlMinutes: 30 * 24 * 60,\n },\n () => this._postprocessRelease(config, release),\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAyCA,IAAa,uBAAb,MAAa,6BAA6B,gBAAgB;CACxD,OAAyB,KAAK;CAE9B,uBAAgC,cAAgC;EAC9D,OAAO,CAAC,UAAU;CACpB;CAEA,oBAAsC;CAEtC,mBAAuD;CAEvD,mBAAqC;CACrC,gBACE;CAEF,YAAY,KAAK,qBAAqB,IAAI;EACxC,MAAM,IAAI,IAAI,KAAK,KAAK,CAAC;CAC3B;CAEA,OAAiB,mBAAmB,aAAyC;EAC3E,MAAM,CAAC,SAAS,kBAAkB,YAAY,MAAM,GAAG;EACvD,MAAM,CAAC,YAAY,gBAAgB,eAAe,MAAM,GAAG;EAC3D,OAAO;GAAE;GAAS;GAAY;EAAa;CAC7C;CAEA,MAAM,eACJ,aACA,aAC+B;EAC/B,MAAM,EAAE,SAAS,YAAY,iBAC3B,qBAAqB,mBAAmB,WAAW;EAErD,MAAM,eAAe,QAAQ,MAAM,GAAG;EACtC,MAAM,cAAc,oBAAoB,WAAW;EAEnD,MAAM,kBAAkB,kBAAkB,YAAY,GAAG;EACzD,MAAM,eAAe,MAAMA,IACzB,0BACA,eACF;EAEA,MAAM,kBAA4B,CAAC;;EAEnC,IAAI,cACF,gBAAgB,KAAK,YAAY;OAC5B;GACL,SAAS,mBAAmB,KAAqB;IAC/C,OAAO,GAAG,cAAc,aAAa,KAAK,GAAG;GAC/C;GACA,gBAAgB,KAAK,oBAAoB,mBAAmB,GAAG,CAAC,CAAC;GACjE,gBAAgB,KAAK,oBAAoB,mBAAmB,GAAG,CAAC,CAAC;EACnE;EAEA,IAAI;EACJ,IAAI;EACJ,KAAK,MAAM,kBAAkB,iBAAiB;GAC5C,MAAM,qBAAqB,MAAM,oBAC/B,KAAK,MACL,cACF;GACA,IAAI,CAAC,oBACH;GAGF,MAAMC,IACJ,0BACA,iBACA,gBACA,KACF;GAEA,gBAAgB,kBAAkB,cAAc;GAEhD,MAAM,uBAAuB,SAAS,cAAc;GACpD,IAAI,CAAC,sBAAsB;IACzB,OAAO,KAAK,EAAE,eAAe,GAAG,4BAA4B;IAC5D;GACF;GACA,MAAM,WAAW,qBAAqB;GACtC,MAAM,kBAAkB,iBAAiB,qBAAqB,SAAS;IACrE,MAAM,OAAO,KAAK,QAAQ,UAAU,EAAE;IAEtC,IACE,KAAK,WAAW,GAAG,WAAW,QAAQ,KACtC,KAAK,WAAW,GAAG,WAAW,KAAK,GAEnC,OAAO;IAGT,IAAI,SAAS,cAAc,KAAK,WAAW,GAAG,WAAW,EAAE,GACzD,OAAO,oBAAoB,GAAG,iBAAiB,MAAM;IAGvD,OAAO;GACT,CAAC;GAED,IAAI,cAAc;IAChB,MAAM,cAAc,gBAAgB,MAAM,MACxC,EAAE,SAAS,IAAI,WAAW,GAAG,aAAa,EAAE,CAC9C;;IAEA,IAAI,aAAa;KACf,cAAc,CAAC,WAAW;KAC1B;IACF;GACF;GAEA,cAAc;GACd;EACF;EAEA,IAAI,CAAC,aACH,OAAO;EAGT,MAAM,wBAAwB,wBAAwB,YAAY,GAAG;EACrE,MAAM,qBAAqB,IAAI,IAC7B,MAAMD,IACJ,0BACA,qBACF,CACF;EACA,cAAc,YAAY,QAAQ,QAAQ,CAAC,mBAAmB,IAAI,GAAG,CAAC;EAEtE,MAAM,8BAAc,IAAI,IAAY;EACpC,KAAK,MAAM,UAAU,aAAa;GAChC,MAAM,eAAe,SAAS,MAAM;GACpC,IAAI,CAAC,cAAc;IACjB,mBAAmB,IAAI,MAAM;IAC7B;GACF;GAEA,MAAM,iBAAiB,MAAM,oBAAoB,KAAK,MAAM,MAAM;;GAElE,IAAI,CAAC,gBAAgB;IACnB,mBAAmB,IAAI,MAAM;IAC7B;GACF;GAEA,MAAM,WAAW,aAAa;GAC9B,MAAM,WAAW,iBAAiB,iBAAiB,SAAS;IAC1D,MAAM,OAAO,KAAK,QAAQ,UAAU,EAAE;IACtC,IAAI,KAAK,WAAW,GAAG,GACrB,OAAO;IAGT,OAAO;GACT,CAAC;GAED,KAAK,MAAM,WAAW,UACpB,YAAY,IAAI,OAAO;EAE3B;EAEA,IAAI,mBAAmB,OAAO,GAC5B,MAAMC,IACJ,0BACA,uBACA,CAAC,GAAG,kBAAkB,GACtB,KACF;;EAIF,IAAI,YAAY,SAAS,GAAG;GAC1B,MAAM,iBAAiB,gBAAgB,YAAY,GAAG;GACtD,MAAMA,IACJ,0BACA,gBACA,aACA,KACF;EACF;EAEA,MAAM,WAAW,CAAC,GAAG,WAAW;EAChC,IAAI,CAAC,SAAS,QACZ,OAAO;EAMT,MAAM,MAAqB;GAAE,UAHD,CAAC,GAAG,WAAW,CAAC,CACzC,KAAK,OAAO,CAAC,CACb,KAAK,aAAa,EAAE,QAAQ,EACK;GAAG;EAAc;EAErD,MAAM,gBAAgB,iBAAiB,QAAQ;EAC/C,MAAM,UAAU,MAAM,KAAK,WACzB,aACA,aACA,eACA,WACF;EAEA,IAAI,SAAS,UACX,IAAI,WAAW,QAAQ;EAGzB,IAAI,SAAS,WACX,IAAI,YAAY,QAAQ;EAG1B,OAAO;CACT;CAEA,MAAM,WACJ,aACA,aACA,SACA,SACyB;EACzB,MAAM,iBAAiB,gBAAgB,YAAY,GAAG;;EAEtD,MAAM,cACJ,WACC,MAAMD,IACL,0BACA,cACF;;EAGF,IAAI,CAAC,aAAa,QAChB,OAAO;;EAIT,IAAI,CAAC,SACH,OAAO;EAGT,MAAM,qBAAqB,qBAAqB,YAAY,GAAG,YAAY,GAAG;EAC9E,MAAM,kBAAkB,IAAI,IAC1B,MAAMA,IACJ,0BACA,kBACF,CACF;EAEA,eAAe,YAA2B;GACxC,IAAI,gBAAgB,OAAO,GACzB,MAAMC,IACJ,0BACA,oBACA,CAAC,GAAG,eAAe,GACnB,KACF;EAEJ;EAEA,KAAK,MAAM,cAAc,aAAa;GACpC,MAAM,cAAc,MAAM,SAAS,UAAU;GAC7C,MAAM,CAAC,YAAY,YAAY,MAAM,GAAG;GAExC,KAAK,MAAM,iBAAiB,CAAC,aAAa,QAAQ,GAAG;IAEnD,MAAM,SAAS,GAAG,aAAa,QAAQ,GAAG,GADnB,cAAc,GAAG,QAAQ;IAEhD,IAAI,gBAAgB,IAAI,MAAM,GAC5B;IAIF,MAAM,EAAE,SAAQ,MADE,qBAAqB,KAAK,MAAM,MAAM,EAAA,CACpC,OAAO;IAC3B,IAAI,CAAC,KAAK;KACR,gBAAgB,IAAI,MAAM;KAC1B;IACF;IAEA,MAAM,SAAkB,CAAC;IAEzB,MAAM,mBAAmB,YAAY,IAAI,YAAY;IACrD,IAAI,kBACF,OAAO,mBAAmB;IAG5B,MAAM,SAAS,IAAI,YAAY,IAAI,IAAI;IAEvC,MAAM,WAAW,OAAO,cAAc,KAAK;IAC3C,IAAI,UACF,OAAO,WAAW;IAGpB,MAAM,YAAY,OAAO,cAAc,SAAS;IAChD,IAAI,WACF,OAAO,YAAY,UAChB,QAAQ,MAAM,OAAO,GAAG,EAAE,CAAC,CAC3B,QAAQ,MAAM,OAAO,GAAG,EAAE,CAAC,CAC3B,QAAQ,MAAM,kBAAkB,GAAG,qBAAqB,CAAC,CACzD,QAAQ,MAAM,QAAQ,GAAG,EAAE;IAGhC,MAAM,UAAU;IAChB,OAAO;GACT;EACF;EAEA,MAAM,UAAU;EAChB,OAAO;CACT;CAEA,MAAe,YACb,QAC+B;EAC/B,MAAM,EAAE,aAAa,gBAAgB;;EAErC,IAAI,CAAC,aACH,OAAO;EAGT,MAAM,cAAc,MAAM,KAAK,eAAe,aAAa,WAAW;EACtE,IAAI,aACF,OAAO;EAGT,OAAO,MACL,mCAAmC,YAAY,8FACjD;EACA,MAAM,qBAAqB,MAAM,MAAM,YAAY,MAAM;EACzD,IAAI,oBACF,OAAO;EAGT,OAAO,MAAM,+BAA+B,YAAY,EAAE;EAC1D,OAAO;CACT;CAEA,MAAc,oBACZ,QACA,SACmC;;EAEnC,IAAI,CAAC,OAAO,aACV,OAAO;EAGT,MAAM,MAAM,MAAM,KAAK,WACrB,OAAO,aACP,OAAO,aACP,QAAQ,OACV;;EAGA,IAAI,KAAK,kBACP,QAAQ,mBAAmB,IAAI;EAGjC,OAAO;CACT;CAEA,mBACE,QACA,SACmC;EACnC,OAAO,UACL;GACE,WAAW;GACX,KAAK,sBAAsB,OAAO,YAAY,GAAG,OAAO,YAAY,GAAG,QAAQ;GAC/E,YAAY;EACd,SACM,KAAK,oBAAoB,QAAQ,OAAO,CAChD;CACF;AACF"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["packageCache.get","packageCache.set"],"sources":["../../../../lib/modules/datasource/sbt-package/index.ts"],"sourcesContent":["import upath from 'upath';\nimport { XmlDocument } from 'xmldoc';\nimport { logger } from '../../../logger/index.ts';\nimport * as packageCache from '../../../util/cache/package/index.ts';\nimport { buildCacheKey } from '../../../util/cache/package/key.ts';\nimport { withCache } from '../../../util/cache/package/with-cache.ts';\nimport { Http } from '../../../util/http/index.ts';\nimport { regEx } from '../../../util/regex.ts';\nimport type { Timestamp } from '../../../util/timestamp.ts';\nimport { asTimestamp } from '../../../util/timestamp.ts';\nimport {\n ensureTrailingSlash,\n parseUrl,\n trimTrailingSlash,\n} from '../../../util/url.ts';\nimport * as ivyVersioning from '../../versioning/ivy/index.ts';\nimport { compare } from '../../versioning/maven/compare.ts';\nimport { MAVEN_REPO } from '../maven/common.ts';\nimport { MavenDatasource } from '../maven/index.ts';\nimport { downloadHttpContent, downloadHttpProtocol } from '../maven/util.ts';\nimport type {\n GetReleasesConfig,\n PostprocessReleaseConfig,\n PostprocessReleaseResult,\n RegistryStrategy,\n Release,\n ReleaseResult,\n} from '../types.ts';\nimport { extractPageLinks, getLatestVersion } from './util.ts';\n\ninterface ScalaDepCoordinate {\n groupId: string;\n artifactId: string;\n scalaVersion?: string;\n}\n\ninterface PomInfo {\n homepage?: string;\n sourceUrl?: string;\n releaseTimestamp?: Timestamp;\n}\n\n/** Returns the package cache key of the package URLs found for a package. */\nfunction getPackageUrlsCacheKey(\n registryUrl: string,\n packageName: string,\n): string {\n return buildCacheKey('package-urls', registryUrl, packageName);\n}\n\nexport class SbtPackageDatasource extends MavenDatasource {\n static override readonly id = 'sbt-package';\n\n override getDefaultRegistryUrls(_packageName: string): string[] {\n return [MAVEN_REPO];\n }\n\n override readonly defaultVersioning = ivyVersioning.id;\n\n override readonly registryStrategy: RegistryStrategy = 'hunt';\n\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 = SbtPackageDatasource.id) {\n super(id, new Http('sbt'));\n }\n\n protected static parseDepCoordinate(packageName: string): ScalaDepCoordinate {\n const [groupId, javaArtifactId] = packageName.split(':');\n const [artifactId, scalaVersion] = javaArtifactId.split('_');\n return { groupId, artifactId, scalaVersion };\n }\n\n async getSbtReleases(\n registryUrl: string,\n packageName: string,\n ): Promise<ReleaseResult | null> {\n const { groupId, artifactId, scalaVersion } =\n SbtPackageDatasource.parseDepCoordinate(packageName);\n\n const groupIdSplit = groupId.split('.');\n const repoRootUrl = ensureTrailingSlash(registryUrl);\n\n const validRootUrlKey = buildCacheKey(\n 'valid-root-url',\n registryUrl,\n packageName,\n );\n const validRootUrl = await packageCache.get<string>(\n 'datasource-sbt-package',\n validRootUrlKey,\n );\n\n const packageRootUrls: string[] = [];\n // istanbul ignore if: not easily testable\n if (validRootUrl) {\n packageRootUrls.push(validRootUrl);\n } else {\n function packageRootUrlWith(sep: string): string {\n return `${repoRootUrl}${groupIdSplit.join(sep)}`;\n }\n packageRootUrls.push(ensureTrailingSlash(packageRootUrlWith('/')));\n packageRootUrls.push(ensureTrailingSlash(packageRootUrlWith('.')));\n }\n\n let dependencyUrl: string | undefined;\n let packageUrls: string[] | undefined;\n for (const packageRootUrl of packageRootUrls) {\n const packageRootContent = await downloadHttpContent(\n this.http,\n packageRootUrl,\n );\n if (!packageRootContent) {\n continue;\n }\n\n await packageCache.set(\n 'datasource-sbt-package',\n validRootUrlKey,\n packageRootUrl,\n 30 * 24 * 60,\n );\n\n dependencyUrl = trimTrailingSlash(packageRootUrl);\n\n const parsedPackageRootUrl = parseUrl(packageRootUrl);\n if (!parsedPackageRootUrl) {\n logger.warn({ packageRootUrl }, 'Failed to parse packageURL');\n continue;\n }\n const rootPath = parsedPackageRootUrl.pathname;\n const artifactSubdirs = extractPageLinks(packageRootContent, (href) => {\n const path = href.replace(rootPath, '');\n\n if (\n path.startsWith(`${artifactId}_native`) ||\n path.startsWith(`${artifactId}_sjs`)\n ) {\n return null;\n }\n\n if (path === artifactId || path.startsWith(`${artifactId}_`)) {\n return ensureTrailingSlash(`${packageRootUrl}${path}`);\n }\n\n return null;\n });\n\n if (scalaVersion) {\n const scalaSubdir = artifactSubdirs.find((x) =>\n x.endsWith(`/${artifactId}_${scalaVersion}/`),\n );\n // v8 ignore else -- needs a nested directory listing fixture\n if (scalaSubdir) {\n packageUrls = [scalaSubdir];\n break;\n }\n }\n\n packageUrls = artifactSubdirs;\n break;\n }\n\n if (!packageUrls) {\n return null;\n }\n\n const invalidPackageUrlsKey = buildCacheKey(\n 'invalid-package-urls',\n registryUrl,\n packageName,\n );\n const invalidPackageUrls = new Set(\n await packageCache.get<string[]>(\n 'datasource-sbt-package',\n invalidPackageUrlsKey,\n ),\n );\n packageUrls = packageUrls.filter((url) => !invalidPackageUrls.has(url));\n\n const allVersions = new Set<string>();\n for (const pkgUrl of packageUrls) {\n const parsedPkgUrl = parseUrl(pkgUrl);\n if (!parsedPkgUrl) {\n invalidPackageUrls.add(pkgUrl);\n continue;\n }\n\n const packageContent = await downloadHttpContent(this.http, pkgUrl);\n // istanbul ignore if\n if (!packageContent) {\n invalidPackageUrls.add(pkgUrl);\n continue;\n }\n\n const rootPath = parsedPkgUrl.pathname;\n const versions = extractPageLinks(packageContent, (href) => {\n const path = href.replace(rootPath, '');\n if (path.startsWith('.')) {\n return null;\n }\n\n return path;\n });\n\n for (const version of versions) {\n allVersions.add(version);\n }\n }\n\n if (invalidPackageUrls.size > 0) {\n await packageCache.set(\n 'datasource-sbt-package',\n invalidPackageUrlsKey,\n [...invalidPackageUrls],\n 30 * 24 * 60,\n );\n }\n\n // v8 ignore else -- an empty list returns before reaching here\n if (packageUrls.length > 0) {\n await packageCache.set(\n 'datasource-sbt-package',\n getPackageUrlsCacheKey(registryUrl, packageName),\n packageUrls,\n 30 * 24 * 60,\n );\n }\n\n const versions = [...allVersions];\n if (!versions.length) {\n return null;\n }\n\n const releases: Release[] = [...allVersions]\n .sort(compare)\n .map((version) => ({ version }));\n const res: ReleaseResult = { releases, dependencyUrl };\n\n const latestVersion = getLatestVersion(versions);\n const pomInfo = await this.getPomInfo(\n registryUrl,\n packageName,\n latestVersion,\n packageUrls,\n );\n\n if (pomInfo?.homepage) {\n res.homepage = pomInfo.homepage;\n }\n\n if (pomInfo?.sourceUrl) {\n res.sourceUrl = pomInfo.sourceUrl;\n }\n\n return res;\n }\n\n async getPomInfo(\n registryUrl: string,\n packageName: string,\n version: string | null,\n pkgUrls?: string[],\n ): Promise<PomInfo | null> {\n // istanbul ignore next: will be covered later\n const packageUrls =\n pkgUrls ??\n (await packageCache.get<string[]>(\n 'datasource-sbt-package',\n getPackageUrlsCacheKey(registryUrl, packageName),\n ));\n\n // istanbul ignore if\n if (!packageUrls?.length) {\n return null;\n }\n\n // istanbul ignore if\n if (!version) {\n return null;\n }\n\n const invalidPomFilesKey = buildCacheKey(\n 'invalid-pom-files',\n registryUrl,\n packageName,\n version,\n );\n const invalidPomFiles = new Set(\n await packageCache.get<string[]>(\n 'datasource-sbt-package',\n invalidPomFilesKey,\n ),\n );\n\n async function saveCache(): Promise<void> {\n if (invalidPomFiles.size > 0) {\n await packageCache.set(\n 'datasource-sbt-package',\n invalidPomFilesKey,\n [...invalidPomFiles],\n 30 * 24 * 60,\n );\n }\n }\n\n for (const packageUrl of packageUrls) {\n const artifactDir = upath.basename(packageUrl);\n const [artifact] = artifactDir.split('_');\n\n for (const pomFilePrefix of [artifactDir, artifact]) {\n const pomFileName = `${pomFilePrefix}-${version}.pom`;\n const pomUrl = `${packageUrl}${version}/${pomFileName}`;\n if (invalidPomFiles.has(pomUrl)) {\n continue;\n }\n\n const res = await downloadHttpProtocol(this.http, pomUrl);\n const { val } = res.unwrap();\n if (!val) {\n invalidPomFiles.add(pomUrl);\n continue;\n }\n\n const result: PomInfo = {};\n\n const releaseTimestamp = asTimestamp(val.lastModified);\n if (releaseTimestamp) {\n result.releaseTimestamp = releaseTimestamp;\n }\n\n const pomXml = new XmlDocument(val.data);\n\n const homepage = pomXml.valueWithPath('url');\n if (homepage) {\n result.homepage = homepage;\n }\n\n const sourceUrl = pomXml.valueWithPath('scm.url');\n if (sourceUrl) {\n result.sourceUrl = sourceUrl\n .replace(regEx(/^scm:/), '')\n .replace(regEx(/^git:/), '')\n .replace(regEx(/^git@github.com:/), 'https://github.com/')\n .replace(regEx(/\\.git$/), '');\n }\n\n await saveCache();\n return result;\n }\n }\n\n await saveCache();\n return null;\n }\n\n override async getReleases(\n config: GetReleasesConfig,\n ): Promise<ReleaseResult | null> {\n const { packageName, registryUrl } = config;\n // istanbul ignore if\n if (!registryUrl) {\n return null;\n }\n\n const sbtReleases = await this.getSbtReleases(registryUrl, packageName);\n if (sbtReleases) {\n return sbtReleases;\n }\n\n logger.debug(\n `Sbt: no versions discovered for ${packageName} listing organization root package folder, fallback to maven datasource for version discovery`,\n );\n const mavenReleaseResult = await super.getReleases(config);\n if (mavenReleaseResult) {\n return mavenReleaseResult;\n }\n\n logger.debug(`Sbt: no versions found for \"${packageName}\"`);\n return null;\n }\n\n private async _postprocessRelease(\n config: PostprocessReleaseConfig,\n release: Release,\n ): Promise<PostprocessReleaseResult> {\n /* v8 ignore next -- should never happen */\n if (!config.registryUrl) {\n return release;\n }\n\n const res = await this.getPomInfo(\n config.registryUrl,\n config.packageName,\n release.version,\n );\n\n // v8 ignore else -- needs a pom fixture carrying no timestamp\n if (res?.releaseTimestamp) {\n release.releaseTimestamp = res.releaseTimestamp;\n }\n\n return release;\n }\n\n override postprocessRelease(\n config: PostprocessReleaseConfig,\n release: Release,\n ): Promise<PostprocessReleaseResult> {\n return withCache(\n {\n namespace: 'datasource-sbt-package',\n key: buildCacheKey(\n 'postprocessRelease',\n config.registryUrl,\n config.packageName,\n release.version,\n ),\n ttlMinutes: 30 * 24 * 60,\n },\n () => this._postprocessRelease(config, release),\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AA2CA,SAAS,uBACP,aACA,aACQ;CACR,OAAO,cAAc,gBAAgB,aAAa,WAAW;AAC/D;AAEA,IAAa,uBAAb,MAAa,6BAA6B,gBAAgB;CACxD,OAAyB,KAAK;CAE9B,uBAAgC,cAAgC;EAC9D,OAAO,CAAC,UAAU;CACpB;CAEA,oBAAsC;CAEtC,mBAAuD;CAEvD,mBAAqC;CACrC,gBACE;CAEF,YAAY,KAAK,qBAAqB,IAAI;EACxC,MAAM,IAAI,IAAI,KAAK,KAAK,CAAC;CAC3B;CAEA,OAAiB,mBAAmB,aAAyC;EAC3E,MAAM,CAAC,SAAS,kBAAkB,YAAY,MAAM,GAAG;EACvD,MAAM,CAAC,YAAY,gBAAgB,eAAe,MAAM,GAAG;EAC3D,OAAO;GAAE;GAAS;GAAY;EAAa;CAC7C;CAEA,MAAM,eACJ,aACA,aAC+B;EAC/B,MAAM,EAAE,SAAS,YAAY,iBAC3B,qBAAqB,mBAAmB,WAAW;EAErD,MAAM,eAAe,QAAQ,MAAM,GAAG;EACtC,MAAM,cAAc,oBAAoB,WAAW;EAEnD,MAAM,kBAAkB,cACtB,kBACA,aACA,WACF;EACA,MAAM,eAAe,MAAMA,IACzB,0BACA,eACF;EAEA,MAAM,kBAA4B,CAAC;;EAEnC,IAAI,cACF,gBAAgB,KAAK,YAAY;OAC5B;GACL,SAAS,mBAAmB,KAAqB;IAC/C,OAAO,GAAG,cAAc,aAAa,KAAK,GAAG;GAC/C;GACA,gBAAgB,KAAK,oBAAoB,mBAAmB,GAAG,CAAC,CAAC;GACjE,gBAAgB,KAAK,oBAAoB,mBAAmB,GAAG,CAAC,CAAC;EACnE;EAEA,IAAI;EACJ,IAAI;EACJ,KAAK,MAAM,kBAAkB,iBAAiB;GAC5C,MAAM,qBAAqB,MAAM,oBAC/B,KAAK,MACL,cACF;GACA,IAAI,CAAC,oBACH;GAGF,MAAMC,IACJ,0BACA,iBACA,gBACA,KACF;GAEA,gBAAgB,kBAAkB,cAAc;GAEhD,MAAM,uBAAuB,SAAS,cAAc;GACpD,IAAI,CAAC,sBAAsB;IACzB,OAAO,KAAK,EAAE,eAAe,GAAG,4BAA4B;IAC5D;GACF;GACA,MAAM,WAAW,qBAAqB;GACtC,MAAM,kBAAkB,iBAAiB,qBAAqB,SAAS;IACrE,MAAM,OAAO,KAAK,QAAQ,UAAU,EAAE;IAEtC,IACE,KAAK,WAAW,GAAG,WAAW,QAAQ,KACtC,KAAK,WAAW,GAAG,WAAW,KAAK,GAEnC,OAAO;IAGT,IAAI,SAAS,cAAc,KAAK,WAAW,GAAG,WAAW,EAAE,GACzD,OAAO,oBAAoB,GAAG,iBAAiB,MAAM;IAGvD,OAAO;GACT,CAAC;GAED,IAAI,cAAc;IAChB,MAAM,cAAc,gBAAgB,MAAM,MACxC,EAAE,SAAS,IAAI,WAAW,GAAG,aAAa,EAAE,CAC9C;;IAEA,IAAI,aAAa;KACf,cAAc,CAAC,WAAW;KAC1B;IACF;GACF;GAEA,cAAc;GACd;EACF;EAEA,IAAI,CAAC,aACH,OAAO;EAGT,MAAM,wBAAwB,cAC5B,wBACA,aACA,WACF;EACA,MAAM,qBAAqB,IAAI,IAC7B,MAAMD,IACJ,0BACA,qBACF,CACF;EACA,cAAc,YAAY,QAAQ,QAAQ,CAAC,mBAAmB,IAAI,GAAG,CAAC;EAEtE,MAAM,8BAAc,IAAI,IAAY;EACpC,KAAK,MAAM,UAAU,aAAa;GAChC,MAAM,eAAe,SAAS,MAAM;GACpC,IAAI,CAAC,cAAc;IACjB,mBAAmB,IAAI,MAAM;IAC7B;GACF;GAEA,MAAM,iBAAiB,MAAM,oBAAoB,KAAK,MAAM,MAAM;;GAElE,IAAI,CAAC,gBAAgB;IACnB,mBAAmB,IAAI,MAAM;IAC7B;GACF;GAEA,MAAM,WAAW,aAAa;GAC9B,MAAM,WAAW,iBAAiB,iBAAiB,SAAS;IAC1D,MAAM,OAAO,KAAK,QAAQ,UAAU,EAAE;IACtC,IAAI,KAAK,WAAW,GAAG,GACrB,OAAO;IAGT,OAAO;GACT,CAAC;GAED,KAAK,MAAM,WAAW,UACpB,YAAY,IAAI,OAAO;EAE3B;EAEA,IAAI,mBAAmB,OAAO,GAC5B,MAAMC,IACJ,0BACA,uBACA,CAAC,GAAG,kBAAkB,GACtB,KACF;;EAIF,IAAI,YAAY,SAAS,GACvB,MAAMA,IACJ,0BACA,uBAAuB,aAAa,WAAW,GAC/C,aACA,KACF;EAGF,MAAM,WAAW,CAAC,GAAG,WAAW;EAChC,IAAI,CAAC,SAAS,QACZ,OAAO;EAMT,MAAM,MAAqB;GAAE,UAHD,CAAC,GAAG,WAAW,CAAC,CACzC,KAAK,OAAO,CAAC,CACb,KAAK,aAAa,EAAE,QAAQ,EACK;GAAG;EAAc;EAErD,MAAM,gBAAgB,iBAAiB,QAAQ;EAC/C,MAAM,UAAU,MAAM,KAAK,WACzB,aACA,aACA,eACA,WACF;EAEA,IAAI,SAAS,UACX,IAAI,WAAW,QAAQ;EAGzB,IAAI,SAAS,WACX,IAAI,YAAY,QAAQ;EAG1B,OAAO;CACT;CAEA,MAAM,WACJ,aACA,aACA,SACA,SACyB;;EAEzB,MAAM,cACJ,WACC,MAAMD,IACL,0BACA,uBAAuB,aAAa,WAAW,CACjD;;EAGF,IAAI,CAAC,aAAa,QAChB,OAAO;;EAIT,IAAI,CAAC,SACH,OAAO;EAGT,MAAM,qBAAqB,cACzB,qBACA,aACA,aACA,OACF;EACA,MAAM,kBAAkB,IAAI,IAC1B,MAAMA,IACJ,0BACA,kBACF,CACF;EAEA,eAAe,YAA2B;GACxC,IAAI,gBAAgB,OAAO,GACzB,MAAMC,IACJ,0BACA,oBACA,CAAC,GAAG,eAAe,GACnB,KACF;EAEJ;EAEA,KAAK,MAAM,cAAc,aAAa;GACpC,MAAM,cAAc,MAAM,SAAS,UAAU;GAC7C,MAAM,CAAC,YAAY,YAAY,MAAM,GAAG;GAExC,KAAK,MAAM,iBAAiB,CAAC,aAAa,QAAQ,GAAG;IAEnD,MAAM,SAAS,GAAG,aAAa,QAAQ,GAAG,GADnB,cAAc,GAAG,QAAQ;IAEhD,IAAI,gBAAgB,IAAI,MAAM,GAC5B;IAIF,MAAM,EAAE,SAAQ,MADE,qBAAqB,KAAK,MAAM,MAAM,EAAA,CACpC,OAAO;IAC3B,IAAI,CAAC,KAAK;KACR,gBAAgB,IAAI,MAAM;KAC1B;IACF;IAEA,MAAM,SAAkB,CAAC;IAEzB,MAAM,mBAAmB,YAAY,IAAI,YAAY;IACrD,IAAI,kBACF,OAAO,mBAAmB;IAG5B,MAAM,SAAS,IAAI,YAAY,IAAI,IAAI;IAEvC,MAAM,WAAW,OAAO,cAAc,KAAK;IAC3C,IAAI,UACF,OAAO,WAAW;IAGpB,MAAM,YAAY,OAAO,cAAc,SAAS;IAChD,IAAI,WACF,OAAO,YAAY,UAChB,QAAQ,MAAM,OAAO,GAAG,EAAE,CAAC,CAC3B,QAAQ,MAAM,OAAO,GAAG,EAAE,CAAC,CAC3B,QAAQ,MAAM,kBAAkB,GAAG,qBAAqB,CAAC,CACzD,QAAQ,MAAM,QAAQ,GAAG,EAAE;IAGhC,MAAM,UAAU;IAChB,OAAO;GACT;EACF;EAEA,MAAM,UAAU;EAChB,OAAO;CACT;CAEA,MAAe,YACb,QAC+B;EAC/B,MAAM,EAAE,aAAa,gBAAgB;;EAErC,IAAI,CAAC,aACH,OAAO;EAGT,MAAM,cAAc,MAAM,KAAK,eAAe,aAAa,WAAW;EACtE,IAAI,aACF,OAAO;EAGT,OAAO,MACL,mCAAmC,YAAY,8FACjD;EACA,MAAM,qBAAqB,MAAM,MAAM,YAAY,MAAM;EACzD,IAAI,oBACF,OAAO;EAGT,OAAO,MAAM,+BAA+B,YAAY,EAAE;EAC1D,OAAO;CACT;CAEA,MAAc,oBACZ,QACA,SACmC;;EAEnC,IAAI,CAAC,OAAO,aACV,OAAO;EAGT,MAAM,MAAM,MAAM,KAAK,WACrB,OAAO,aACP,OAAO,aACP,QAAQ,OACV;;EAGA,IAAI,KAAK,kBACP,QAAQ,mBAAmB,IAAI;EAGjC,OAAO;CACT;CAEA,mBACE,QACA,SACmC;EACnC,OAAO,UACL;GACE,WAAW;GACX,KAAK,cACH,sBACA,OAAO,aACP,OAAO,aACP,QAAQ,OACV;GACA,YAAY;EACd,SACM,KAAK,oBAAoB,QAAQ,OAAO,CAChD;CACF;AACF"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { logger } from "../../../logger/index.js";
|
|
2
2
|
import { getQueryString, isHttpUrl, joinUrlParts } from "../../../util/url.js";
|
|
3
|
+
import { buildCacheKey } from "../../../util/cache/package/key.js";
|
|
3
4
|
import { id } from "../../versioning/hashicorp/index.js";
|
|
4
5
|
import { OpenTofuModuleDocsResponse, ProtocolModuleResponse, TerraformModuleResponse, TerraformModuleV2Response } from "./schema.js";
|
|
5
6
|
import { TerraformDatasource } from "./base.js";
|
|
@@ -115,7 +116,7 @@ var TerraformModuleDatasource = class TerraformModuleDatasource extends Terrafor
|
|
|
115
116
|
}
|
|
116
117
|
static getCacheKey({ packageName, registryUrl }) {
|
|
117
118
|
const { registry, repository } = getRegistryRepository(packageName, registryUrl);
|
|
118
|
-
return
|
|
119
|
+
return buildCacheKey(registry, repository);
|
|
119
120
|
}
|
|
120
121
|
};
|
|
121
122
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["hashicorpVersioning.id"],"sources":["../../../../lib/modules/datasource/terraform-module/index.ts"],"sourcesContent":["import { logger } from '../../../logger/index.ts';\nimport { getQueryString, isHttpUrl, joinUrlParts } from '../../../util/url.ts';\nimport * as hashicorpVersioning from '../../versioning/hashicorp/index.ts';\nimport type { GetReleasesConfig, ReleaseResult } from '../types.ts';\nimport { TerraformDatasource } from './base.ts';\nimport {\n OpenTofuModuleDocsResponse,\n ProtocolModuleResponse,\n TerraformModuleResponse,\n TerraformModuleV2Response,\n} from './schema.ts';\nimport { createSDBackendURL, getRegistryRepository } from './utils.ts';\n\nexport class TerraformModuleDatasource extends TerraformDatasource {\n static override readonly id = 'terraform-module';\n\n static readonly terraformCloudUrl = 'https://app.terraform.io';\n\n static readonly defaultRegistryUrls = [\n TerraformModuleDatasource.terraformRegistryUrl,\n ];\n\n constructor() {\n super(TerraformModuleDatasource.id);\n }\n\n override getDefaultRegistryUrls(_packageName: string): string[] {\n return TerraformModuleDatasource.defaultRegistryUrls;\n }\n\n override readonly defaultVersioning = hashicorpVersioning.id;\n\n override readonly releaseTimestampSupport = true;\n override readonly releaseTimestampNote =\n 'For `registry.terraform.io` and `registry.opentofu.org` (via `api.opentofu.org`), release timestamps are available for all versions. For `app.terraform.io`, only the latest version is annotated, using the `published_at` field from the extended module endpoint.';\n override readonly sourceUrlSupport = 'package';\n override readonly sourceUrlNote =\n 'For `registry.terraform.io` and `app.terraform.io`, the source URL is taken from the `source` field of the registry response. For `registry.opentofu.org`, it is derived from the package name following the OpenTofu registry policy of `github.com/NAMESPACE/terraform-TARGETSYSTEM-NAME`.';\n\n /**\n * Resolves a module release list for the configured registry.\n *\n * Requests against the public Terraform registry and Terraform Cloud use the\n * registry-specific module endpoint, while other registries use the generic\n * Module Registry Protocol versions endpoint.\n */\n private async fetchReleases({\n packageName,\n registryUrl,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n /* v8 ignore next -- should never happen */\n if (!registryUrl) {\n return null;\n }\n\n const { registry, repository } = getRegistryRepository(\n packageName,\n registryUrl,\n );\n logger.trace(\n { registryUrlNormalized: registry, terraformRepository: repository },\n 'terraform-module.getReleases()',\n );\n\n try {\n if (registry === TerraformModuleDatasource.terraformRegistryUrl) {\n return await this.queryTerraformRegistryV2(registry, repository);\n }\n if (\n registry === TerraformModuleDatasource.openTofuRegistryUrl ||\n registry === TerraformModuleDatasource.openTofuApiUrl\n ) {\n return await this.queryOpenTofuRegistry(repository);\n }\n if (registry === TerraformModuleDatasource.terraformCloudUrl) {\n return await this.queryTerraformRegistry(registry, repository);\n }\n\n // Use the standard Module Registry Protocol for other conformant registries.\n return await this.queryModuleRegistry(registry, repository);\n } catch (err) {\n this.handleGenericErrors(err);\n }\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n return this.cached(\n {\n key: TerraformModuleDatasource.getCacheKey(config),\n fallback: true,\n },\n () => this.fetchReleases(config),\n );\n }\n\n /**\n * Query the Terraform Registry using the undocumented v2 JSON:API.\n *\n * Returns release timestamps for all versions, unlike the v1 extended\n * module endpoint which only exposed the timestamp for the latest version.\n */\n private async queryTerraformRegistryV2(\n registryUrl: string,\n repository: string,\n ): Promise<ReleaseResult> {\n const moduleUrl = `${joinUrlParts(\n registryUrl,\n 'v2/modules',\n repository,\n )}?${getQueryString({ include: 'module-versions' })}`;\n const { body: res } = await this.http.getJson(\n moduleUrl,\n TerraformModuleV2Response,\n );\n res.homepage = `${registryUrl}/modules/${repository}`;\n return res;\n }\n\n /**\n * Query the OpenTofu registry docs API.\n * https://api.opentofu.org/\n *\n * Used when the registry URL is `registry.opentofu.org`.\n * Queries `api.opentofu.org` for module versions with release timestamps.\n */\n private async queryOpenTofuRegistry(\n repository: string,\n ): Promise<ReleaseResult> {\n const docsUrl = joinUrlParts(\n TerraformModuleDatasource.openTofuApiUrl,\n 'registry/docs/modules',\n repository,\n 'index.json',\n );\n const { body: res } = await this.http.getJson(\n docsUrl,\n OpenTofuModuleDocsResponse,\n );\n res.homepage = `https://search.opentofu.org/module/${repository}`;\n\n // The OpenTofu registry only indexes modules hosted on GitHub under the\n // `NAMESPACE/terraform-TARGETSYSTEM-NAME` repository naming convention, so\n // the source URL can be derived deterministically from the package name.\n // https://github.com/opentofu/registry/blob/main/PROCEDURES.md\n const [namespace, name, target] = repository.split('/');\n res.sourceUrl = `https://github.com/${namespace}/terraform-${target}-${name}`;\n\n return res;\n }\n\n /**\n * Queries the Terraform Registry module endpoint.\n *\n * The response includes the latest published version separately, so only that\n * release can be annotated with `releaseTimestamp`.\n *\n * https://developer.hashicorp.com/terraform/registry/api-docs#get-a-specific-module\n */\n private async queryTerraformRegistry(\n registryUrl: string,\n repository: string,\n ): Promise<ReleaseResult> {\n const serviceDiscovery =\n await this.getTerraformServiceDiscoveryResult(registryUrl);\n const pkgUrl = createSDBackendURL(\n registryUrl,\n 'modules.v1',\n serviceDiscovery,\n repository,\n );\n const { body: res } = await this.http.getJson(\n pkgUrl,\n TerraformModuleResponse,\n );\n return {\n releases: res.versions,\n sourceUrl: res.source,\n homepage: `${registryUrl}/modules/${repository}`,\n };\n }\n\n /**\n * Queries a registry through the Terraform Module Registry Protocol.\n *\n * This is the default path for registries implementing the standard module\n * registry protocol. It returns release versions and, when present and valid,\n * the upstream source URL.\n *\n * https://developer.hashicorp.com/terraform/internals/module-registry-protocol\n */\n private async queryModuleRegistry(\n registryUrl: string,\n repository: string,\n ): Promise<ReleaseResult | null> {\n const serviceDiscovery =\n await this.getTerraformServiceDiscoveryResult(registryUrl);\n const pkgUrl = createSDBackendURL(\n registryUrl,\n 'modules.v1',\n serviceDiscovery,\n `${repository}/versions`,\n );\n const { body: res } = await this.http.getJson(\n pkgUrl,\n ProtocolModuleResponse,\n );\n return {\n releases: res.versions,\n sourceUrl: isHttpUrl(res.source) ? res.source : undefined,\n };\n }\n\n private static getCacheKey({\n packageName,\n registryUrl,\n }: GetReleasesConfig): string {\n const { registry, repository } = getRegistryRepository(\n packageName,\n registryUrl,\n );\n return
|
|
1
|
+
{"version":3,"file":"index.js","names":["hashicorpVersioning.id"],"sources":["../../../../lib/modules/datasource/terraform-module/index.ts"],"sourcesContent":["import { logger } from '../../../logger/index.ts';\nimport { buildCacheKey } from '../../../util/cache/package/key.ts';\nimport { getQueryString, isHttpUrl, joinUrlParts } from '../../../util/url.ts';\nimport * as hashicorpVersioning from '../../versioning/hashicorp/index.ts';\nimport type { GetReleasesConfig, ReleaseResult } from '../types.ts';\nimport { TerraformDatasource } from './base.ts';\nimport {\n OpenTofuModuleDocsResponse,\n ProtocolModuleResponse,\n TerraformModuleResponse,\n TerraformModuleV2Response,\n} from './schema.ts';\nimport { createSDBackendURL, getRegistryRepository } from './utils.ts';\n\nexport class TerraformModuleDatasource extends TerraformDatasource {\n static override readonly id = 'terraform-module';\n\n static readonly terraformCloudUrl = 'https://app.terraform.io';\n\n static readonly defaultRegistryUrls = [\n TerraformModuleDatasource.terraformRegistryUrl,\n ];\n\n constructor() {\n super(TerraformModuleDatasource.id);\n }\n\n override getDefaultRegistryUrls(_packageName: string): string[] {\n return TerraformModuleDatasource.defaultRegistryUrls;\n }\n\n override readonly defaultVersioning = hashicorpVersioning.id;\n\n override readonly releaseTimestampSupport = true;\n override readonly releaseTimestampNote =\n 'For `registry.terraform.io` and `registry.opentofu.org` (via `api.opentofu.org`), release timestamps are available for all versions. For `app.terraform.io`, only the latest version is annotated, using the `published_at` field from the extended module endpoint.';\n override readonly sourceUrlSupport = 'package';\n override readonly sourceUrlNote =\n 'For `registry.terraform.io` and `app.terraform.io`, the source URL is taken from the `source` field of the registry response. For `registry.opentofu.org`, it is derived from the package name following the OpenTofu registry policy of `github.com/NAMESPACE/terraform-TARGETSYSTEM-NAME`.';\n\n /**\n * Resolves a module release list for the configured registry.\n *\n * Requests against the public Terraform registry and Terraform Cloud use the\n * registry-specific module endpoint, while other registries use the generic\n * Module Registry Protocol versions endpoint.\n */\n private async fetchReleases({\n packageName,\n registryUrl,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n /* v8 ignore next -- should never happen */\n if (!registryUrl) {\n return null;\n }\n\n const { registry, repository } = getRegistryRepository(\n packageName,\n registryUrl,\n );\n logger.trace(\n { registryUrlNormalized: registry, terraformRepository: repository },\n 'terraform-module.getReleases()',\n );\n\n try {\n if (registry === TerraformModuleDatasource.terraformRegistryUrl) {\n return await this.queryTerraformRegistryV2(registry, repository);\n }\n if (\n registry === TerraformModuleDatasource.openTofuRegistryUrl ||\n registry === TerraformModuleDatasource.openTofuApiUrl\n ) {\n return await this.queryOpenTofuRegistry(repository);\n }\n if (registry === TerraformModuleDatasource.terraformCloudUrl) {\n return await this.queryTerraformRegistry(registry, repository);\n }\n\n // Use the standard Module Registry Protocol for other conformant registries.\n return await this.queryModuleRegistry(registry, repository);\n } catch (err) {\n this.handleGenericErrors(err);\n }\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n return this.cached(\n {\n key: TerraformModuleDatasource.getCacheKey(config),\n fallback: true,\n },\n () => this.fetchReleases(config),\n );\n }\n\n /**\n * Query the Terraform Registry using the undocumented v2 JSON:API.\n *\n * Returns release timestamps for all versions, unlike the v1 extended\n * module endpoint which only exposed the timestamp for the latest version.\n */\n private async queryTerraformRegistryV2(\n registryUrl: string,\n repository: string,\n ): Promise<ReleaseResult> {\n const moduleUrl = `${joinUrlParts(\n registryUrl,\n 'v2/modules',\n repository,\n )}?${getQueryString({ include: 'module-versions' })}`;\n const { body: res } = await this.http.getJson(\n moduleUrl,\n TerraformModuleV2Response,\n );\n res.homepage = `${registryUrl}/modules/${repository}`;\n return res;\n }\n\n /**\n * Query the OpenTofu registry docs API.\n * https://api.opentofu.org/\n *\n * Used when the registry URL is `registry.opentofu.org`.\n * Queries `api.opentofu.org` for module versions with release timestamps.\n */\n private async queryOpenTofuRegistry(\n repository: string,\n ): Promise<ReleaseResult> {\n const docsUrl = joinUrlParts(\n TerraformModuleDatasource.openTofuApiUrl,\n 'registry/docs/modules',\n repository,\n 'index.json',\n );\n const { body: res } = await this.http.getJson(\n docsUrl,\n OpenTofuModuleDocsResponse,\n );\n res.homepage = `https://search.opentofu.org/module/${repository}`;\n\n // The OpenTofu registry only indexes modules hosted on GitHub under the\n // `NAMESPACE/terraform-TARGETSYSTEM-NAME` repository naming convention, so\n // the source URL can be derived deterministically from the package name.\n // https://github.com/opentofu/registry/blob/main/PROCEDURES.md\n const [namespace, name, target] = repository.split('/');\n res.sourceUrl = `https://github.com/${namespace}/terraform-${target}-${name}`;\n\n return res;\n }\n\n /**\n * Queries the Terraform Registry module endpoint.\n *\n * The response includes the latest published version separately, so only that\n * release can be annotated with `releaseTimestamp`.\n *\n * https://developer.hashicorp.com/terraform/registry/api-docs#get-a-specific-module\n */\n private async queryTerraformRegistry(\n registryUrl: string,\n repository: string,\n ): Promise<ReleaseResult> {\n const serviceDiscovery =\n await this.getTerraformServiceDiscoveryResult(registryUrl);\n const pkgUrl = createSDBackendURL(\n registryUrl,\n 'modules.v1',\n serviceDiscovery,\n repository,\n );\n const { body: res } = await this.http.getJson(\n pkgUrl,\n TerraformModuleResponse,\n );\n return {\n releases: res.versions,\n sourceUrl: res.source,\n homepage: `${registryUrl}/modules/${repository}`,\n };\n }\n\n /**\n * Queries a registry through the Terraform Module Registry Protocol.\n *\n * This is the default path for registries implementing the standard module\n * registry protocol. It returns release versions and, when present and valid,\n * the upstream source URL.\n *\n * https://developer.hashicorp.com/terraform/internals/module-registry-protocol\n */\n private async queryModuleRegistry(\n registryUrl: string,\n repository: string,\n ): Promise<ReleaseResult | null> {\n const serviceDiscovery =\n await this.getTerraformServiceDiscoveryResult(registryUrl);\n const pkgUrl = createSDBackendURL(\n registryUrl,\n 'modules.v1',\n serviceDiscovery,\n `${repository}/versions`,\n );\n const { body: res } = await this.http.getJson(\n pkgUrl,\n ProtocolModuleResponse,\n );\n return {\n releases: res.versions,\n sourceUrl: isHttpUrl(res.source) ? res.source : undefined,\n };\n }\n\n private static getCacheKey({\n packageName,\n registryUrl,\n }: GetReleasesConfig): string {\n const { registry, repository } = getRegistryRepository(\n packageName,\n registryUrl,\n );\n return buildCacheKey(registry, repository);\n }\n}\n"],"mappings":";;;;;;;;AAcA,IAAa,4BAAb,MAAa,kCAAkC,oBAAoB;CACjE,OAAyB,KAAK;CAE9B,OAAgB,oBAAoB;CAEpC,OAAgB,sBAAsB,CACpC,0BAA0B,oBAC5B;CAEA,cAAc;EACZ,MAAM,0BAA0B,EAAE;CACpC;CAEA,uBAAgC,cAAgC;EAC9D,OAAO,0BAA0B;CACnC;CAEA,oBAAsCA;CAEtC,0BAA4C;CAC5C,uBACE;CACF,mBAAqC;CACrC,gBACE;;;;;;;;CASF,MAAc,cAAc,EAC1B,aACA,eACmD;;EAEnD,IAAI,CAAC,aACH,OAAO;EAGT,MAAM,EAAE,UAAU,eAAe,sBAC/B,aACA,WACF;EACA,OAAO,MACL;GAAE,uBAAuB;GAAU,qBAAqB;EAAW,GACnE,gCACF;EAEA,IAAI;GACF,IAAI,aAAa,0BAA0B,sBACzC,OAAO,MAAM,KAAK,yBAAyB,UAAU,UAAU;GAEjE,IACE,aAAa,0BAA0B,uBACvC,aAAa,0BAA0B,gBAEvC,OAAO,MAAM,KAAK,sBAAsB,UAAU;GAEpD,IAAI,aAAa,0BAA0B,mBACzC,OAAO,MAAM,KAAK,uBAAuB,UAAU,UAAU;GAI/D,OAAO,MAAM,KAAK,oBAAoB,UAAU,UAAU;EAC5D,SAAS,KAAK;GACZ,KAAK,oBAAoB,GAAG;EAC9B;CACF;CAEA,YAAY,QAA0D;EACpE,OAAO,KAAK,OACV;GACE,KAAK,0BAA0B,YAAY,MAAM;GACjD,UAAU;EACZ,SACM,KAAK,cAAc,MAAM,CACjC;CACF;;;;;;;CAQA,MAAc,yBACZ,aACA,YACwB;EACxB,MAAM,YAAY,GAAG,aACnB,aACA,cACA,UACF,EAAE,GAAG,eAAe,EAAE,SAAS,kBAAkB,CAAC;EAClD,MAAM,EAAE,MAAM,QAAQ,MAAM,KAAK,KAAK,QACpC,WACA,yBACF;EACA,IAAI,WAAW,GAAG,YAAY,WAAW;EACzC,OAAO;CACT;;;;;;;;CASA,MAAc,sBACZ,YACwB;EACxB,MAAM,UAAU,aACd,0BAA0B,gBAC1B,yBACA,YACA,YACF;EACA,MAAM,EAAE,MAAM,QAAQ,MAAM,KAAK,KAAK,QACpC,SACA,0BACF;EACA,IAAI,WAAW,sCAAsC;EAMrD,MAAM,CAAC,WAAW,MAAM,UAAU,WAAW,MAAM,GAAG;EACtD,IAAI,YAAY,sBAAsB,UAAU,aAAa,OAAO,GAAG;EAEvE,OAAO;CACT;;;;;;;;;CAUA,MAAc,uBACZ,aACA,YACwB;EACxB,MAAM,mBACJ,MAAM,KAAK,mCAAmC,WAAW;EAC3D,MAAM,SAAS,mBACb,aACA,cACA,kBACA,UACF;EACA,MAAM,EAAE,MAAM,QAAQ,MAAM,KAAK,KAAK,QACpC,QACA,uBACF;EACA,OAAO;GACL,UAAU,IAAI;GACd,WAAW,IAAI;GACf,UAAU,GAAG,YAAY,WAAW;EACtC;CACF;;;;;;;;;;CAWA,MAAc,oBACZ,aACA,YAC+B;EAC/B,MAAM,mBACJ,MAAM,KAAK,mCAAmC,WAAW;EAC3D,MAAM,SAAS,mBACb,aACA,cACA,kBACA,GAAG,WAAW,UAChB;EACA,MAAM,EAAE,MAAM,QAAQ,MAAM,KAAK,KAAK,QACpC,QACA,sBACF;EACA,OAAO;GACL,UAAU,IAAI;GACd,WAAW,UAAU,IAAI,MAAM,IAAI,IAAI,SAAS,KAAA;EAClD;CACF;CAEA,OAAe,YAAY,EACzB,aACA,eAC4B;EAC5B,MAAM,EAAE,UAAU,eAAe,sBAC/B,aACA,WACF;EACA,OAAO,cAAc,UAAU,UAAU;CAC3C;AACF"}
|