renovate 44.210.2 → 44.211.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config/options/index.js +1 -1
- package/dist/config/options/index.js.map +1 -1
- package/dist/config/utils.js +2 -1
- package/dist/config/utils.js.map +1 -1
- package/dist/config/validation-helpers/regex-glob-matchers.js.map +1 -1
- package/dist/config/validation-helpers/utils.js +5 -1
- package/dist/config/validation-helpers/utils.js.map +1 -1
- package/dist/config/validation.js +8 -2
- package/dist/config/validation.js.map +1 -1
- package/dist/config-validator.js +1 -1
- package/dist/config-validator.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/instrumentation/index.js +3 -2
- package/dist/instrumentation/index.js.map +1 -1
- package/dist/logger/bunyan.js +3 -2
- package/dist/logger/bunyan.js.map +1 -1
- package/dist/logger/pretty-stdout.js +2 -1
- package/dist/logger/pretty-stdout.js.map +1 -1
- package/dist/modules/datasource/apk/index.js +2 -2
- package/dist/modules/datasource/apk/index.js.map +1 -1
- package/dist/modules/datasource/artifactory/index.js +2 -3
- package/dist/modules/datasource/artifactory/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/cdnjs/index.js +2 -1
- package/dist/modules/datasource/cdnjs/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/crate/index.js +2 -1
- package/dist/modules/datasource/crate/index.js.map +1 -1
- package/dist/modules/datasource/dart-version/index.js +2 -1
- package/dist/modules/datasource/dart-version/index.js.map +1 -1
- package/dist/modules/datasource/datasource.js +2 -2
- package/dist/modules/datasource/datasource.js.map +1 -1
- package/dist/modules/datasource/deb/url.js +3 -1
- package/dist/modules/datasource/deb/url.js.map +1 -1
- package/dist/modules/datasource/devbox/index.js +2 -1
- package/dist/modules/datasource/devbox/index.js.map +1 -1
- package/dist/modules/datasource/docker/common.js +10 -8
- package/dist/modules/datasource/docker/common.js.map +1 -1
- package/dist/modules/datasource/docker/ecr.js +2 -1
- package/dist/modules/datasource/docker/ecr.js.map +1 -1
- package/dist/modules/datasource/docker/index.js +20 -17
- package/dist/modules/datasource/docker/index.js.map +1 -1
- package/dist/modules/datasource/gitea-tags/base.js +2 -3
- package/dist/modules/datasource/gitea-tags/base.js.map +1 -1
- package/dist/modules/datasource/github-tags/index.js +2 -1
- package/dist/modules/datasource/github-tags/index.js.map +1 -1
- package/dist/modules/datasource/gitlab-tags/index.js +2 -3
- package/dist/modules/datasource/gitlab-tags/index.js.map +1 -1
- package/dist/modules/datasource/go/base.js +1 -1
- package/dist/modules/datasource/go/base.js.map +1 -1
- package/dist/modules/datasource/go/common.js +8 -2
- package/dist/modules/datasource/go/common.js.map +1 -1
- package/dist/modules/datasource/go/env.js +2 -1
- package/dist/modules/datasource/go/env.js.map +1 -1
- package/dist/modules/datasource/go/releases-goproxy.js +7 -5
- package/dist/modules/datasource/go/releases-goproxy.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/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/adoptium.js +3 -2
- package/dist/modules/datasource/java-version/adoptium.js.map +1 -1
- package/dist/modules/datasource/java-version/graalvm.js +2 -1
- package/dist/modules/datasource/java-version/graalvm.js.map +1 -1
- package/dist/modules/datasource/maven/index.js.map +1 -1
- package/dist/modules/datasource/maven/util.js +7 -10
- package/dist/modules/datasource/maven/util.js.map +1 -1
- package/dist/modules/datasource/npm/npmrc.js +2 -2
- package/dist/modules/datasource/npm/npmrc.js.map +1 -1
- package/dist/modules/datasource/nuget/v3.js +3 -5
- package/dist/modules/datasource/nuget/v3.js.map +1 -1
- package/dist/modules/datasource/packagist/index.js +4 -5
- package/dist/modules/datasource/packagist/index.js.map +1 -1
- package/dist/modules/datasource/pod/index.js +4 -4
- package/dist/modules/datasource/pod/index.js.map +1 -1
- package/dist/modules/datasource/pypi/index.js +19 -11
- package/dist/modules/datasource/pypi/index.js.map +1 -1
- package/dist/modules/datasource/python-version/index.js +2 -3
- package/dist/modules/datasource/python-version/index.js.map +1 -1
- package/dist/modules/datasource/repology/index.js +4 -3
- package/dist/modules/datasource/repology/index.js.map +1 -1
- package/dist/modules/datasource/rpm/providers/common.js +3 -1
- package/dist/modules/datasource/rpm/providers/common.js.map +1 -1
- package/dist/modules/datasource/rubygems/index.js +2 -5
- package/dist/modules/datasource/rubygems/index.js.map +1 -1
- package/dist/modules/datasource/rubygems/versions-endpoint-cache.js +13 -18
- package/dist/modules/datasource/rubygems/versions-endpoint-cache.js.map +1 -1
- package/dist/modules/datasource/terraform-module/base.js +2 -1
- package/dist/modules/datasource/terraform-module/base.js.map +1 -1
- package/dist/modules/datasource/terraform-provider/index.js +2 -3
- package/dist/modules/datasource/terraform-provider/index.js.map +1 -1
- package/dist/modules/manager/bazel-module/parser/context.js +0 -1
- package/dist/modules/manager/bazel-module/parser/context.js.map +1 -1
- package/dist/modules/manager/bundler/host-rules.js +4 -1
- package/dist/modules/manager/bundler/host-rules.js.map +1 -1
- package/dist/modules/manager/composer/range.js +1 -1
- package/dist/modules/manager/composer/range.js.map +1 -1
- package/dist/modules/manager/custom/utils.js +3 -1
- package/dist/modules/manager/custom/utils.js.map +1 -1
- package/dist/modules/manager/devbox/schema.js +2 -1
- package/dist/modules/manager/devbox/schema.js.map +1 -1
- package/dist/modules/manager/dockerfile/extract.js +10 -2
- package/dist/modules/manager/dockerfile/extract.js.map +1 -1
- package/dist/modules/manager/fingerprint.generated.js +1 -1
- package/dist/modules/manager/fingerprint.generated.js.map +1 -1
- package/dist/modules/manager/github-actions/parse.js +2 -1
- package/dist/modules/manager/github-actions/parse.js.map +1 -1
- package/dist/modules/manager/gomod/artifacts.js +5 -2
- package/dist/modules/manager/gomod/artifacts.js.map +1 -1
- package/dist/modules/manager/gradle/artifacts.js +5 -2
- package/dist/modules/manager/gradle/artifacts.js.map +1 -1
- package/dist/modules/manager/gradle/utils.js +2 -1
- package/dist/modules/manager/gradle/utils.js.map +1 -1
- package/dist/modules/manager/gradle-wrapper/utils.js +9 -5
- package/dist/modules/manager/gradle-wrapper/utils.js.map +1 -1
- package/dist/modules/manager/helm-values/util.js +1 -1
- package/dist/modules/manager/helm-values/util.js.map +1 -1
- package/dist/modules/manager/helmfile/artifacts.js +1 -1
- package/dist/modules/manager/helmfile/artifacts.js.map +1 -1
- package/dist/modules/manager/helmv3/artifacts.js +1 -1
- package/dist/modules/manager/helmv3/artifacts.js.map +1 -1
- package/dist/modules/manager/maven/extract.js +5 -2
- package/dist/modules/manager/maven/extract.js.map +1 -1
- package/dist/modules/manager/maven-wrapper/artifacts.js +3 -2
- package/dist/modules/manager/maven-wrapper/artifacts.js.map +1 -1
- package/dist/modules/manager/mise/artifacts.js +2 -1
- package/dist/modules/manager/mise/artifacts.js.map +1 -1
- package/dist/modules/manager/mise/extract.js.map +1 -1
- package/dist/modules/manager/npm/artifacts.js +3 -1
- package/dist/modules/manager/npm/artifacts.js.map +1 -1
- package/dist/modules/manager/npm/extract/common/dependency.js +4 -1
- package/dist/modules/manager/npm/extract/common/dependency.js.map +1 -1
- package/dist/modules/manager/npm/extract/common/package-file.js +2 -1
- package/dist/modules/manager/npm/extract/common/package-file.js.map +1 -1
- package/dist/modules/manager/npm/extract/index.js.map +1 -1
- package/dist/modules/manager/npm/extract/post/locked-versions.js.map +1 -1
- package/dist/modules/manager/npm/npmrc.js +2 -1
- package/dist/modules/manager/npm/npmrc.js.map +1 -1
- package/dist/modules/manager/npm/post-update/index.js +2 -1
- package/dist/modules/manager/npm/post-update/index.js.map +1 -1
- package/dist/modules/manager/npm/post-update/npm.js +2 -3
- package/dist/modules/manager/npm/post-update/npm.js.map +1 -1
- package/dist/modules/manager/npm/post-update/yarn.js.map +1 -1
- package/dist/modules/manager/npm/schema.js +6 -2
- package/dist/modules/manager/npm/schema.js.map +1 -1
- package/dist/modules/manager/npm/update/dependency/index.js +3 -2
- package/dist/modules/manager/npm/update/dependency/index.js.map +1 -1
- package/dist/modules/manager/npm/update/locked-dependency/common/parent-version.js +6 -2
- package/dist/modules/manager/npm/update/locked-dependency/common/parent-version.js.map +1 -1
- package/dist/modules/manager/npm/update/locked-dependency/package-lock/get-locked.js +2 -1
- package/dist/modules/manager/npm/update/locked-dependency/package-lock/get-locked.js.map +1 -1
- package/dist/modules/manager/nuget/package-tree.js +2 -1
- package/dist/modules/manager/nuget/package-tree.js.map +1 -1
- package/dist/modules/manager/paket/update-locked.js +4 -1
- package/dist/modules/manager/paket/update-locked.js.map +1 -1
- package/dist/modules/manager/pipenv/extract.js +4 -1
- package/dist/modules/manager/pipenv/extract.js.map +1 -1
- package/dist/modules/platform/azure/azure-helper.js +2 -1
- package/dist/modules/platform/azure/azure-helper.js.map +1 -1
- package/dist/modules/platform/azure/index.js +7 -3
- package/dist/modules/platform/azure/index.js.map +1 -1
- package/dist/modules/platform/bitbucket/index.js +13 -6
- package/dist/modules/platform/bitbucket/index.js.map +1 -1
- package/dist/modules/platform/bitbucket-server/index.js +13 -9
- package/dist/modules/platform/bitbucket-server/index.js.map +1 -1
- package/dist/modules/platform/codecommit/index.js +2 -2
- package/dist/modules/platform/codecommit/index.js.map +1 -1
- package/dist/modules/platform/gerrit/client.js +4 -1
- package/dist/modules/platform/gerrit/client.js.map +1 -1
- package/dist/modules/platform/gerrit/index.js +3 -2
- package/dist/modules/platform/gerrit/index.js.map +1 -1
- package/dist/modules/platform/gitea/gitea-helper.js +5 -4
- package/dist/modules/platform/gitea/gitea-helper.js.map +1 -1
- package/dist/modules/platform/gitea/index.js +11 -4
- package/dist/modules/platform/gitea/index.js.map +1 -1
- package/dist/modules/platform/github/index.js +36 -23
- package/dist/modules/platform/github/index.js.map +1 -1
- package/dist/modules/platform/gitlab/index.js +16 -9
- package/dist/modules/platform/gitlab/index.js.map +1 -1
- package/dist/modules/platform/scm-manager/index.js +4 -1
- package/dist/modules/platform/scm-manager/index.js.map +1 -1
- package/dist/modules/platform/scm-manager/utils.js +2 -1
- package/dist/modules/platform/scm-manager/utils.js.map +1 -1
- package/dist/modules/versioning/cargo/index.js.map +1 -1
- package/dist/modules/versioning/composer/index.js +2 -1
- package/dist/modules/versioning/composer/index.js.map +1 -1
- package/dist/modules/versioning/conan/common.js +4 -1
- package/dist/modules/versioning/conan/common.js.map +1 -1
- package/dist/modules/versioning/debian/index.js +2 -1
- package/dist/modules/versioning/debian/index.js.map +1 -1
- package/dist/modules/versioning/gradle/compare.js +2 -1
- package/dist/modules/versioning/gradle/compare.js.map +1 -1
- package/dist/modules/versioning/hex/index.js +2 -1
- package/dist/modules/versioning/hex/index.js.map +1 -1
- package/dist/modules/versioning/julia/index.js.map +1 -1
- package/dist/modules/versioning/maven/compare.js +17 -7
- package/dist/modules/versioning/maven/compare.js.map +1 -1
- package/dist/modules/versioning/npm/index.js +2 -1
- package/dist/modules/versioning/npm/index.js.map +1 -1
- package/dist/modules/versioning/npm/range.js.map +1 -1
- package/dist/modules/versioning/paket/index.js +2 -1
- package/dist/modules/versioning/paket/index.js.map +1 -1
- package/dist/modules/versioning/paket/range.js +7 -3
- package/dist/modules/versioning/paket/range.js.map +1 -1
- package/dist/modules/versioning/pep440/index.js +2 -1
- package/dist/modules/versioning/pep440/index.js.map +1 -1
- package/dist/modules/versioning/poetry/index.js +2 -1
- package/dist/modules/versioning/poetry/index.js.map +1 -1
- package/dist/modules/versioning/regex/index.js +1 -1
- package/dist/modules/versioning/regex/index.js.map +1 -1
- package/dist/modules/versioning/rez/index.js +2 -1
- package/dist/modules/versioning/rez/index.js.map +1 -1
- package/dist/modules/versioning/vcpkg/index.js +2 -1
- package/dist/modules/versioning/vcpkg/index.js.map +1 -1
- package/dist/util/cache/package/impl/base.js +2 -1
- package/dist/util/cache/package/impl/base.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/check-token.js +7 -4
- package/dist/util/check-token.js.map +1 -1
- package/dist/util/exec/utils.js +4 -1
- package/dist/util/exec/utils.js.map +1 -1
- package/dist/util/git/auth.js +5 -1
- package/dist/util/git/auth.js.map +1 -1
- package/dist/util/git/error.js.map +1 -1
- package/dist/util/host-rules.js +5 -3
- package/dist/util/host-rules.js.map +1 -1
- package/dist/util/http/cache/abstract-http-cache-provider.js +3 -2
- package/dist/util/http/cache/abstract-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/http/github.js +15 -11
- package/dist/util/http/github.js.map +1 -1
- package/dist/util/http/gitlab.js +5 -4
- package/dist/util/http/gitlab.js.map +1 -1
- package/dist/util/http/http.js +2 -1
- package/dist/util/http/http.js.map +1 -1
- package/dist/util/http/jwt.js +2 -1
- package/dist/util/http/jwt.js.map +1 -1
- package/dist/util/http/retry-after.js +2 -1
- package/dist/util/http/retry-after.js.map +1 -1
- package/dist/util/http/status.js +95 -0
- package/dist/util/http/status.js.map +1 -0
- package/dist/util/merge-confidence/index.js +3 -2
- package/dist/util/merge-confidence/index.js.map +1 -1
- package/dist/util/package-rules/current-version.js +4 -1
- package/dist/util/package-rules/current-version.js.map +1 -1
- package/dist/util/package-rules/index.js +7 -3
- package/dist/util/package-rules/index.js.map +1 -1
- package/dist/util/schema-utils/index.js +5 -0
- package/dist/util/schema-utils/index.js.map +1 -1
- package/dist/workers/global/config/parse/host-rules-from-env.js +2 -1
- package/dist/workers/global/config/parse/host-rules-from-env.js.map +1 -1
- package/dist/workers/repository/common.js +4 -1
- package/dist/workers/repository/common.js.map +1 -1
- package/dist/workers/repository/config-migration/branch/index.js +2 -1
- package/dist/workers/repository/config-migration/branch/index.js.map +1 -1
- package/dist/workers/repository/dependency-dashboard.js +6 -2
- package/dist/workers/repository/dependency-dashboard.js.map +1 -1
- package/dist/workers/repository/finalize/repository-statistics.js +4 -1
- package/dist/workers/repository/finalize/repository-statistics.js.map +1 -1
- package/dist/workers/repository/init/merge.js +13 -3
- package/dist/workers/repository/init/merge.js.map +1 -1
- package/dist/workers/repository/onboarding/branch/check.js +2 -1
- package/dist/workers/repository/onboarding/branch/check.js.map +1 -1
- package/dist/workers/repository/onboarding/pr/pr-list.js +4 -2
- package/dist/workers/repository/onboarding/pr/pr-list.js.map +1 -1
- package/dist/workers/repository/process/extract-update.js +4 -1
- package/dist/workers/repository/process/extract-update.js.map +1 -1
- package/dist/workers/repository/process/lookup/digest.js +2 -1
- package/dist/workers/repository/process/lookup/digest.js.map +1 -1
- package/dist/workers/repository/process/lookup/generate.js +1 -1
- package/dist/workers/repository/process/lookup/generate.js.map +1 -1
- package/dist/workers/repository/process/lookup/index.js +4 -1
- package/dist/workers/repository/process/lookup/index.js.map +1 -1
- package/dist/workers/repository/process/lookup/utils.js +7 -1
- package/dist/workers/repository/process/lookup/utils.js.map +1 -1
- package/dist/workers/repository/process/vulnerabilities.js +10 -3
- package/dist/workers/repository/process/vulnerabilities.js.map +1 -1
- package/dist/workers/repository/reconfigure/index.js +2 -1
- package/dist/workers/repository/reconfigure/index.js.map +1 -1
- package/dist/workers/repository/update/branch/auto-replace.js +6 -2
- package/dist/workers/repository/update/branch/auto-replace.js.map +1 -1
- package/dist/workers/repository/update/branch/errors.js +2 -1
- package/dist/workers/repository/update/branch/errors.js.map +1 -1
- package/dist/workers/repository/update/branch/execute-post-upgrade-commands.js +2 -2
- package/dist/workers/repository/update/branch/execute-post-upgrade-commands.js.map +1 -1
- package/dist/workers/repository/update/branch/get-updated.js +1 -2
- package/dist/workers/repository/update/branch/get-updated.js.map +1 -1
- package/dist/workers/repository/update/branch/index.js +2 -1
- package/dist/workers/repository/update/branch/index.js.map +1 -1
- package/dist/workers/repository/update/branch/status-checks.js +2 -1
- package/dist/workers/repository/update/branch/status-checks.js.map +1 -1
- package/dist/workers/repository/update/pr/body/index.js.map +1 -1
- package/dist/workers/repository/update/pr/changelog/release-notes.js +3 -2
- package/dist/workers/repository/update/pr/changelog/release-notes.js.map +1 -1
- package/dist/workers/repository/update/pr/index.js +7 -3
- package/dist/workers/repository/update/pr/index.js.map +1 -1
- package/dist/workers/repository/updates/generate.js +6 -3
- package/dist/workers/repository/updates/generate.js.map +1 -1
- package/package.json +2 -2
- package/renovate-schema.json +2 -2
|
@@ -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 { 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
|
+
{"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 {\n isForbidden,\n isMultipleChoices,\n isNotFound,\n} from '../../../util/http/status.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 (isNotFound(err)) {\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 (isForbidden(err)) {\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 (isMultipleChoices(err)) {\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,4 +1,5 @@
|
|
|
1
1
|
import { logger } from "../../../../logger/index.js";
|
|
2
|
+
import { isNotModified } from "../../../../util/http/status.js";
|
|
2
3
|
import { toSha256 } from "../../../../util/hash.js";
|
|
3
4
|
import { cachePathExists, createCacheReadStream, createCacheWriteStream, ensureCacheDir, pipeline, renameCacheFile, rmCache, statCacheFile } from "../../../../util/fs/index.js";
|
|
4
5
|
import { acquireLock } from "../../../../util/mutex.js";
|
|
@@ -25,7 +26,8 @@ async function getFileCreationTime(filePath) {
|
|
|
25
26
|
async function checkIfModified(url, lastDownloadTimestamp, http) {
|
|
26
27
|
const options = { headers: { "If-Modified-Since": lastDownloadTimestamp.toUTCString() } };
|
|
27
28
|
try {
|
|
28
|
-
|
|
29
|
+
const response = await http.head(url, options);
|
|
30
|
+
return !isNotModified(response);
|
|
29
31
|
} catch (err) {
|
|
30
32
|
logger.warn({
|
|
31
33
|
err,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"common.js","names":["fs.statCacheFile","fs.createCacheWriteStream","fs.pipeline","fs.createCacheReadStream","fs.renameCacheFile","fs.rmCache","fs.ensureCacheDir","fs.cachePathExists"],"sources":["../../../../../lib/modules/datasource/rpm/providers/common.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport { isNullOrUndefined } from '@sindresorhus/is';\nimport upath from 'upath';\nimport { logger } from '../../../../logger/index.ts';\nimport { createDecompressStream } from '../../../../util/compress.ts';\nimport * as fs from '../../../../util/fs/index.ts';\nimport { toSha256 } from '../../../../util/hash.ts';\nimport type { Http, HttpOptions } from '../../../../util/http/index.ts';\nimport { acquireLock } from '../../../../util/mutex.ts';\nimport type { ReleaseResult } from '../../types.ts';\nimport { datasource } from '../common.ts';\n\nconst cacheSubDir = datasource;\n\ntype RpmVersionValue = boolean | number | string | null | undefined;\n\nexport function formatRpmVersion(\n ver: RpmVersionValue,\n rel?: RpmVersionValue,\n): string | null {\n if (isNullOrUndefined(ver)) {\n return null;\n }\n\n const version = String(ver);\n\n if (isNullOrUndefined(rel)) {\n return version;\n }\n\n return `${version}-${String(rel)}`;\n}\n\nexport function buildReleaseResult(\n versions: Iterable<string>,\n): ReleaseResult | null {\n const uniqueVersions = [...new Set(versions)];\n\n if (uniqueVersions.length === 0) {\n return null;\n }\n\n return {\n releases: uniqueVersions.map((version) => ({ version })),\n };\n}\n\nasync function getFileCreationTime(\n filePath: string,\n): Promise<Date | undefined> {\n const stats = await fs.statCacheFile(filePath);\n return stats?.ctime;\n}\n\nasync function checkIfModified(\n url: string,\n lastDownloadTimestamp: Date,\n http: Http,\n): Promise<boolean> {\n const options: HttpOptions = {\n headers: {\n 'If-Modified-Since': lastDownloadTimestamp.toUTCString(),\n },\n };\n\n try {\n const response = await http.head(url, options);\n return response
|
|
1
|
+
{"version":3,"file":"common.js","names":["fs.statCacheFile","fs.createCacheWriteStream","fs.pipeline","fs.createCacheReadStream","fs.renameCacheFile","fs.rmCache","fs.ensureCacheDir","fs.cachePathExists"],"sources":["../../../../../lib/modules/datasource/rpm/providers/common.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport { isNullOrUndefined } from '@sindresorhus/is';\nimport upath from 'upath';\nimport { logger } from '../../../../logger/index.ts';\nimport { createDecompressStream } from '../../../../util/compress.ts';\nimport * as fs from '../../../../util/fs/index.ts';\nimport { toSha256 } from '../../../../util/hash.ts';\nimport type { Http, HttpOptions } from '../../../../util/http/index.ts';\nimport { isNotModified } from '../../../../util/http/status.ts';\nimport { acquireLock } from '../../../../util/mutex.ts';\nimport type { ReleaseResult } from '../../types.ts';\nimport { datasource } from '../common.ts';\n\nconst cacheSubDir = datasource;\n\ntype RpmVersionValue = boolean | number | string | null | undefined;\n\nexport function formatRpmVersion(\n ver: RpmVersionValue,\n rel?: RpmVersionValue,\n): string | null {\n if (isNullOrUndefined(ver)) {\n return null;\n }\n\n const version = String(ver);\n\n if (isNullOrUndefined(rel)) {\n return version;\n }\n\n return `${version}-${String(rel)}`;\n}\n\nexport function buildReleaseResult(\n versions: Iterable<string>,\n): ReleaseResult | null {\n const uniqueVersions = [...new Set(versions)];\n\n if (uniqueVersions.length === 0) {\n return null;\n }\n\n return {\n releases: uniqueVersions.map((version) => ({ version })),\n };\n}\n\nasync function getFileCreationTime(\n filePath: string,\n): Promise<Date | undefined> {\n const stats = await fs.statCacheFile(filePath);\n return stats?.ctime;\n}\n\nasync function checkIfModified(\n url: string,\n lastDownloadTimestamp: Date,\n http: Http,\n): Promise<boolean> {\n const options: HttpOptions = {\n headers: {\n 'If-Modified-Since': lastDownloadTimestamp.toUTCString(),\n },\n };\n\n try {\n const response = await http.head(url, options);\n return !isNotModified(response);\n } catch (err) {\n logger.warn(\n {\n err,\n lastDownloadTimestamp,\n url,\n },\n 'Could not determine if metadata file is modified since last download',\n );\n return true;\n }\n}\n\nasync function downloadFileToCache(\n url: string,\n cachePath: string,\n http: Http,\n lastDownloadTimestamp?: Date,\n): Promise<boolean> {\n let needsToDownload = true;\n\n if (lastDownloadTimestamp) {\n needsToDownload = await checkIfModified(url, lastDownloadTimestamp, http);\n }\n\n if (!needsToDownload) {\n logger.debug(`No need to download ${url}, file is up to date.`);\n return false;\n }\n\n const readStream = http.stream(url);\n const writeStream = fs.createCacheWriteStream(cachePath);\n await fs.pipeline(readStream, writeStream);\n\n const compressedStats = await fs.statCacheFile(cachePath);\n if (!compressedStats || compressedStats.size === 0) {\n logger.debug(`Empty response body from getting ${url}.`);\n throw new Error(`Empty response body from getting ${url}.`);\n }\n\n return true;\n}\n\nasync function decompressFile(\n compressedFile: string,\n decompressedFile: string,\n): Promise<void> {\n const readStream = fs.createCacheReadStream(compressedFile);\n const decompressStream = await createDecompressStream(readStream);\n\n if (decompressStream === readStream) {\n // The file is not compressed: rename it into place instead of copying it\n readStream.destroy();\n await fs.renameCacheFile(compressedFile, decompressedFile);\n return;\n }\n\n const tempFile = `${decompressedFile}.${randomUUID()}.tmp`;\n\n try {\n await fs.pipeline(decompressStream, fs.createCacheWriteStream(tempFile));\n // Only replace the shared cache file after a successful decompress.\n await fs.renameCacheFile(tempFile, decompressedFile);\n } finally {\n await fs.rmCache(tempFile);\n }\n}\n\nexport async function getCachedDecompressedFile(\n http: Http,\n url: string,\n extension: 'xml',\n): Promise<string> {\n const releaseLock = await acquireLock(\n `decompressed-file:${url}:${extension}`,\n 'datasource-rpm',\n );\n\n try {\n const cacheDir = await fs.ensureCacheDir(cacheSubDir);\n const urlHash = toSha256(url);\n const decompressedFile = upath.join(cacheDir, `${urlHash}.${extension}`);\n let lastTimestamp = await getFileCreationTime(decompressedFile);\n\n const compressedFile = upath.join(\n cacheDir,\n `${randomUUID()}_${urlHash}.gz`,\n );\n\n try {\n const wasUpdated = await downloadFileToCache(\n url,\n compressedFile,\n http,\n lastTimestamp,\n );\n\n if (wasUpdated || !lastTimestamp) {\n try {\n await decompressFile(compressedFile, decompressedFile);\n lastTimestamp = await getFileCreationTime(decompressedFile);\n } catch (err) {\n logger.warn(\n {\n compressedFile,\n err,\n extension,\n decompressedFile,\n url,\n },\n 'Failed to extract RPM metadata file from compressed file',\n );\n }\n }\n\n if (!lastTimestamp) {\n throw new Error('Missing metadata in extracted RPM metadata file!');\n }\n\n return decompressedFile;\n } finally {\n if (await fs.cachePathExists(compressedFile)) {\n await fs.rmCache(compressedFile);\n }\n }\n } finally {\n releaseLock();\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { logger } from "../../../logger/index.js";
|
|
2
2
|
import { buildCacheKey } from "../../../util/cache/package/key.js";
|
|
3
3
|
import { getQueryString, joinUrlParts, parseUrl } from "../../../util/url.js";
|
|
4
|
+
import { isServerError } from "../../../util/http/status.js";
|
|
4
5
|
import { id } from "../../versioning/ruby/index.js";
|
|
5
|
-
import { RequestError } from "../../../util/http/got.js";
|
|
6
6
|
import { AsyncResult, Result } from "../../../util/result.js";
|
|
7
7
|
import { Http } from "../../../util/http/index.js";
|
|
8
8
|
import { Datasource } from "../datasource.js";
|
|
@@ -13,10 +13,7 @@ import { VersionsEndpointCache } from "./versions-endpoint-cache.js";
|
|
|
13
13
|
import { Marshal } from "@qnighy/marshal";
|
|
14
14
|
// lib/modules/datasource/rubygems/index.ts
|
|
15
15
|
function unlessServerSide(err, cb) {
|
|
16
|
-
if (err
|
|
17
|
-
const code = err.response.statusCode;
|
|
18
|
-
if (code >= 500 && code <= 599) return AsyncResult.err(err);
|
|
19
|
-
}
|
|
16
|
+
if (isServerError(err)) return AsyncResult.err(err);
|
|
20
17
|
return cb();
|
|
21
18
|
}
|
|
22
19
|
var RubygemsDatasource = class RubygemsDatasource extends Datasource {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["
|
|
1
|
+
{"version":3,"file":"index.js","names":["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 } from '../../../util/http/index.ts';\nimport { isServerError } from '../../../util/http/status.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 (isServerError(err)) {\n return AsyncResult.err(err);\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 public: 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":";;;;;;;;;;;;;;AAgBA,SAAS,iBAGP,KAAQ,IAAgD;CACxD,IAAI,cAAc,GAAG,GACnB,OAAO,YAAY,IAAI,GAAG;CAE5B,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,oBAAsCA;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,QAAQ,qBAAqB;EAC/B,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"}
|
|
@@ -3,10 +3,9 @@ import { coerceArray } from "../../../util/array.js";
|
|
|
3
3
|
import { copystr } from "../../../util/string.js";
|
|
4
4
|
import { logger } from "../../../logger/index.js";
|
|
5
5
|
import { parseUrl } from "../../../util/url.js";
|
|
6
|
+
import { isNotFound, isOk, isRangeNotSatisfiable } from "../../../util/http/status.js";
|
|
6
7
|
import { LooseArray } from "../../../util/schema-utils/index.js";
|
|
7
|
-
import { RequestError } from "../../../util/http/got.js";
|
|
8
8
|
import { Result } from "../../../util/result.js";
|
|
9
|
-
import "../../../util/http/index.js";
|
|
10
9
|
import { getElapsedMinutes } from "../../../util/date.js";
|
|
11
10
|
import { DateTime } from "luxon";
|
|
12
11
|
import { z } from "zod/v4";
|
|
@@ -136,7 +135,7 @@ var VersionsEndpointCache = class {
|
|
|
136
135
|
const { body } = await this.http.getText(url, { headers: { "Accept-Encoding": "gzip" } });
|
|
137
136
|
return parseFullBody(body);
|
|
138
137
|
} catch (err) {
|
|
139
|
-
if (err
|
|
138
|
+
if (isNotFound(err)) return Result.err("unsupported-api");
|
|
140
139
|
throw err;
|
|
141
140
|
}
|
|
142
141
|
}
|
|
@@ -153,7 +152,7 @@ var VersionsEndpointCache = class {
|
|
|
153
152
|
* Rubygems will return the full body instead of `416 Range Not Satisfiable`.
|
|
154
153
|
* In this case, status code will be 200 instead of 206.
|
|
155
154
|
*/
|
|
156
|
-
if (statusCode
|
|
155
|
+
if (isOk(statusCode)) return parseFullBody(body);
|
|
157
156
|
if (getContentHead(body) !== oldCache.contentTail) return this.fullSync(registryUrl);
|
|
158
157
|
/**
|
|
159
158
|
* Update the cache with the new data.
|
|
@@ -170,20 +169,16 @@ var VersionsEndpointCache = class {
|
|
|
170
169
|
contentTail
|
|
171
170
|
});
|
|
172
171
|
} catch (err) {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
* This is unlikely to happen in real life, but still.
|
|
184
|
-
*/
|
|
185
|
-
if (responseStatus === 404) return Result.err("unsupported-api");
|
|
186
|
-
}
|
|
172
|
+
/**
|
|
173
|
+
* In case of `416 Range Not Satisfiable` we do a full sync.
|
|
174
|
+
* This is unlikely to happen in real life, but anyway.
|
|
175
|
+
*/
|
|
176
|
+
if (isRangeNotSatisfiable(err)) return this.fullSync(registryUrl);
|
|
177
|
+
/**
|
|
178
|
+
* If the endpoint is not supported, we stop trying.
|
|
179
|
+
* This is unlikely to happen in real life, but still.
|
|
180
|
+
*/
|
|
181
|
+
if (isNotFound(err)) return Result.err("unsupported-api");
|
|
187
182
|
throw err;
|
|
188
183
|
}
|
|
189
184
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"versions-endpoint-cache.js","names":["HttpError"],"sources":["../../../../lib/modules/datasource/rubygems/versions-endpoint-cache.ts"],"sourcesContent":["import { DateTime } from 'luxon';\nimport { z } from 'zod/v4';\nimport { logger } from '../../../logger/index.ts';\nimport { coerceArray } from '../../../util/array.ts';\nimport { getElapsedMinutes } from '../../../util/date.ts';\nimport type { Http } from '../../../util/http/index.ts';\nimport { HttpError } from '../../../util/http/index.ts';\nimport type { HttpOptions } from '../../../util/http/types.ts';\nimport { newlineRegex } from '../../../util/regex.ts';\nimport { Result } from '../../../util/result.ts';\nimport { LooseArray } from '../../../util/schema-utils/index.ts';\nimport { copystr } from '../../../util/string.ts';\nimport { parseUrl } from '../../../util/url.ts';\nimport type { VersionsResult } from './types.ts';\n\ntype PackageVersions = Map<string, string[]>;\n\ninterface VersionsEndpointData {\n packageVersions: PackageVersions;\n syncedAt: Date;\n contentLength: number;\n\n /**\n * Last 33 characters of the response (32 hex digits + newline)\n */\n contentTail: string;\n}\n\nfunction getContentTail(content: string): string {\n return content.slice(-33);\n}\n\nfunction getContentHead(content: string): string {\n return content.slice(0, 33);\n}\n\nfunction stripContentHead(content: string): string {\n return content.slice(33);\n}\n\nfunction reconcilePackageVersions(\n packageVersions: PackageVersions,\n versionLines: VersionLines,\n): PackageVersions {\n for (const line of versionLines) {\n const packageName = copystr(line.packageName);\n let versions = coerceArray(packageVersions.get(packageName));\n\n const { deletedVersions, addedVersions } = line;\n\n if (deletedVersions.size > 0) {\n versions = versions.filter((v) => !deletedVersions.has(v));\n }\n\n // v8 ignore else -- needs a delta that only removes versions\n if (addedVersions.length > 0) {\n const existingVersions = new Set(versions);\n for (const addedVersion of addedVersions) {\n // v8 ignore else -- needs a delta re-adding a version already present\n if (!existingVersions.has(addedVersion)) {\n const version = copystr(addedVersion);\n versions.push(version);\n }\n }\n }\n\n packageVersions.set(packageName, versions);\n }\n\n return packageVersions;\n}\n\nfunction parseFullBody(body: string): VersionsEndpointResult {\n const packageVersions = reconcilePackageVersions(\n new Map<string, string[]>(),\n VersionLines.parse(body),\n );\n const syncedAt = DateTime.now().toJSDate();\n const contentLength = body.length;\n const contentTail = getContentTail(body);\n\n return Result.ok({\n packageVersions,\n syncedAt,\n contentLength,\n contentTail,\n });\n}\n\ntype VersionsEndpointResult = Result<VersionsEndpointData, 'unsupported-api'>;\n\nexport const memCache = new Map<string, VersionsEndpointResult>();\n\nfunction cacheResult(\n registryUrl: string,\n result: VersionsEndpointResult,\n): void {\n const registryHostname = parseUrl(registryUrl)?.hostname;\n // v8 ignore else -- needs a result cached for a non-rubygems.org host\n if (registryHostname === 'rubygems.org') {\n memCache.set(registryUrl, result);\n }\n}\n\nconst VersionLines = z\n .string()\n .transform((x) => x.split(newlineRegex))\n .pipe(\n LooseArray(\n z\n .string()\n .transform((line) => line.trim())\n .refine((line) => line.length > 0)\n .refine((line) => !line.startsWith('created_at:'))\n .refine((line) => line !== '---')\n .transform((line) => line.split(' '))\n .pipe(z.tuple([z.string(), z.string()]).rest(z.string()))\n .transform(([packageName, versions]) => {\n const deletedVersions = new Set<string>();\n const addedVersions: string[] = [];\n for (const version of versions.split(',')) {\n if (version.startsWith('-')) {\n deletedVersions.add(version.slice(1));\n } else {\n addedVersions.push(version);\n }\n }\n return { packageName, deletedVersions, addedVersions };\n }),\n ),\n );\ntype VersionLines = z.infer<typeof VersionLines>;\n\nfunction isStale(regCache: VersionsEndpointData): boolean {\n return getElapsedMinutes(regCache.syncedAt) >= 15;\n}\n\nexport class VersionsEndpointCache {\n private readonly http: Http;\n\n constructor(http: Http) {\n this.http = http;\n }\n\n private cacheRequests = new Map<string, Promise<VersionsEndpointResult>>();\n\n /**\n * At any given time, there should only be one request for a given registryUrl.\n */\n private async getCache(registryUrl: string): Promise<VersionsEndpointResult> {\n const oldResult = memCache.get(registryUrl);\n if (!oldResult) {\n const newResult = await this.fullSync(registryUrl);\n cacheResult(registryUrl, newResult);\n return newResult;\n }\n\n const { val: data } = oldResult.unwrap();\n if (!data) {\n return oldResult;\n }\n\n if (isStale(data)) {\n memCache.delete(registryUrl); // If no error is thrown, we'll re-set the cache\n const newResult = await this.deltaSync(data, registryUrl);\n cacheResult(registryUrl, newResult);\n return newResult;\n }\n\n return oldResult;\n }\n\n async getVersions(\n registryUrl: string,\n packageName: string,\n ): Promise<VersionsResult> {\n /**\n * Ensure that only one request for a given registryUrl is in flight at a time.\n */\n let cacheRequest = this.cacheRequests.get(registryUrl);\n if (!cacheRequest) {\n cacheRequest = this.getCache(registryUrl);\n this.cacheRequests.set(registryUrl, cacheRequest);\n }\n let cachedResult: VersionsEndpointResult;\n try {\n cachedResult = await cacheRequest;\n } finally {\n this.cacheRequests.delete(registryUrl);\n }\n\n const { val: cachedData } = cachedResult.unwrap();\n if (!cachedData) {\n logger.debug(\n { packageName, registryUrl },\n 'Rubygems: endpoint not supported',\n );\n return Result.err('unsupported-api');\n }\n\n const versions = cachedData.packageVersions.get(packageName);\n if (!versions?.length) {\n logger.debug(\n { packageName, registryUrl },\n 'Rubygems: versions not found',\n );\n return Result.err('package-not-found');\n }\n\n return Result.ok(versions);\n }\n\n private async fullSync(registryUrl: string): Promise<VersionsEndpointResult> {\n try {\n const url = `${registryUrl}/versions`;\n const opts: HttpOptions = { headers: { 'Accept-Encoding': 'gzip' } };\n const { body } = await this.http.getText(url, opts);\n return parseFullBody(body);\n } catch (err) {\n if (err instanceof HttpError && err.response?.statusCode === 404) {\n return Result.err('unsupported-api');\n }\n\n throw err;\n }\n }\n\n private async deltaSync(\n oldCache: VersionsEndpointData,\n registryUrl: string,\n ): Promise<VersionsEndpointResult> {\n try {\n const url = `${registryUrl}/versions`;\n const startByte = oldCache.contentLength - oldCache.contentTail.length;\n const opts: HttpOptions = {\n headers: {\n ['Accept-Encoding']: 'deflate, compress, br', // Note: `gzip` usage breaks http client, when used with `Range` header\n ['Range']: `bytes=${startByte}-`,\n },\n };\n const { statusCode, body } = await this.http.getText(url, opts);\n\n /**\n * Rubygems will return the full body instead of `416 Range Not Satisfiable`.\n * In this case, status code will be 200 instead of 206.\n */\n if (statusCode === 200) {\n return parseFullBody(body);\n }\n\n /**\n * We request data in range that overlaps previously fetched data.\n * If the head of the response doesn't match the tail of the previous response,\n * it means that the data we have is no longer valid.\n * In this case we start over with a full sync.\n */\n const contentHead = getContentHead(body);\n if (contentHead !== oldCache.contentTail) {\n return this.fullSync(registryUrl);\n }\n\n /**\n * Update the cache with the new data.\n */\n const delta = stripContentHead(body);\n const packageVersions = reconcilePackageVersions(\n oldCache.packageVersions,\n VersionLines.parse(delta),\n );\n const syncedAt = DateTime.now().toJSDate();\n const contentLength = oldCache.contentLength + delta.length;\n const contentTail = getContentTail(body);\n\n return Result.ok({\n packageVersions,\n syncedAt,\n contentLength,\n contentTail,\n });\n } catch (err) {\n // v8 ignore else -- needs a non-http failure during the sync\n if (err instanceof HttpError) {\n const responseStatus = err.response?.statusCode;\n\n /**\n * In case of `416 Range Not Satisfiable` we do a full sync.\n * This is unlikely to happen in real life, but anyway.\n */\n if (responseStatus === 416) {\n return this.fullSync(registryUrl);\n }\n\n /**\n * If the endpoint is not supported, we stop trying.\n * This is unlikely to happen in real life, but still.\n */\n if (responseStatus === 404) {\n return Result.err('unsupported-api');\n }\n }\n\n throw err;\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"versions-endpoint-cache.js","names":[],"sources":["../../../../lib/modules/datasource/rubygems/versions-endpoint-cache.ts"],"sourcesContent":["import { DateTime } from 'luxon';\nimport { z } from 'zod/v4';\nimport { logger } from '../../../logger/index.ts';\nimport { coerceArray } from '../../../util/array.ts';\nimport { getElapsedMinutes } from '../../../util/date.ts';\nimport type { Http } from '../../../util/http/index.ts';\nimport {\n isNotFound,\n isOk,\n isRangeNotSatisfiable,\n} from '../../../util/http/status.ts';\nimport type { HttpOptions } from '../../../util/http/types.ts';\nimport { newlineRegex } from '../../../util/regex.ts';\nimport { Result } from '../../../util/result.ts';\nimport { LooseArray } from '../../../util/schema-utils/index.ts';\nimport { copystr } from '../../../util/string.ts';\nimport { parseUrl } from '../../../util/url.ts';\nimport type { VersionsResult } from './types.ts';\n\ntype PackageVersions = Map<string, string[]>;\n\ninterface VersionsEndpointData {\n packageVersions: PackageVersions;\n syncedAt: Date;\n contentLength: number;\n\n /**\n * Last 33 characters of the response (32 hex digits + newline)\n */\n contentTail: string;\n}\n\nfunction getContentTail(content: string): string {\n return content.slice(-33);\n}\n\nfunction getContentHead(content: string): string {\n return content.slice(0, 33);\n}\n\nfunction stripContentHead(content: string): string {\n return content.slice(33);\n}\n\nfunction reconcilePackageVersions(\n packageVersions: PackageVersions,\n versionLines: VersionLines,\n): PackageVersions {\n for (const line of versionLines) {\n const packageName = copystr(line.packageName);\n let versions = coerceArray(packageVersions.get(packageName));\n\n const { deletedVersions, addedVersions } = line;\n\n if (deletedVersions.size > 0) {\n versions = versions.filter((v) => !deletedVersions.has(v));\n }\n\n // v8 ignore else -- needs a delta that only removes versions\n if (addedVersions.length > 0) {\n const existingVersions = new Set(versions);\n for (const addedVersion of addedVersions) {\n // v8 ignore else -- needs a delta re-adding a version already present\n if (!existingVersions.has(addedVersion)) {\n const version = copystr(addedVersion);\n versions.push(version);\n }\n }\n }\n\n packageVersions.set(packageName, versions);\n }\n\n return packageVersions;\n}\n\nfunction parseFullBody(body: string): VersionsEndpointResult {\n const packageVersions = reconcilePackageVersions(\n new Map<string, string[]>(),\n VersionLines.parse(body),\n );\n const syncedAt = DateTime.now().toJSDate();\n const contentLength = body.length;\n const contentTail = getContentTail(body);\n\n return Result.ok({\n packageVersions,\n syncedAt,\n contentLength,\n contentTail,\n });\n}\n\ntype VersionsEndpointResult = Result<VersionsEndpointData, 'unsupported-api'>;\n\nexport const memCache = new Map<string, VersionsEndpointResult>();\n\nfunction cacheResult(\n registryUrl: string,\n result: VersionsEndpointResult,\n): void {\n const registryHostname = parseUrl(registryUrl)?.hostname;\n // v8 ignore else -- needs a result cached for a non-rubygems.org host\n if (registryHostname === 'rubygems.org') {\n memCache.set(registryUrl, result);\n }\n}\n\nconst VersionLines = z\n .string()\n .transform((x) => x.split(newlineRegex))\n .pipe(\n LooseArray(\n z\n .string()\n .transform((line) => line.trim())\n .refine((line) => line.length > 0)\n .refine((line) => !line.startsWith('created_at:'))\n .refine((line) => line !== '---')\n .transform((line) => line.split(' '))\n .pipe(z.tuple([z.string(), z.string()]).rest(z.string()))\n .transform(([packageName, versions]) => {\n const deletedVersions = new Set<string>();\n const addedVersions: string[] = [];\n for (const version of versions.split(',')) {\n if (version.startsWith('-')) {\n deletedVersions.add(version.slice(1));\n } else {\n addedVersions.push(version);\n }\n }\n return { packageName, deletedVersions, addedVersions };\n }),\n ),\n );\ntype VersionLines = z.infer<typeof VersionLines>;\n\nfunction isStale(regCache: VersionsEndpointData): boolean {\n return getElapsedMinutes(regCache.syncedAt) >= 15;\n}\n\nexport class VersionsEndpointCache {\n private readonly http: Http;\n\n constructor(http: Http) {\n this.http = http;\n }\n\n private cacheRequests = new Map<string, Promise<VersionsEndpointResult>>();\n\n /**\n * At any given time, there should only be one request for a given registryUrl.\n */\n private async getCache(registryUrl: string): Promise<VersionsEndpointResult> {\n const oldResult = memCache.get(registryUrl);\n if (!oldResult) {\n const newResult = await this.fullSync(registryUrl);\n cacheResult(registryUrl, newResult);\n return newResult;\n }\n\n const { val: data } = oldResult.unwrap();\n if (!data) {\n return oldResult;\n }\n\n if (isStale(data)) {\n memCache.delete(registryUrl); // If no error is thrown, we'll re-set the cache\n const newResult = await this.deltaSync(data, registryUrl);\n cacheResult(registryUrl, newResult);\n return newResult;\n }\n\n return oldResult;\n }\n\n async getVersions(\n registryUrl: string,\n packageName: string,\n ): Promise<VersionsResult> {\n /**\n * Ensure that only one request for a given registryUrl is in flight at a time.\n */\n let cacheRequest = this.cacheRequests.get(registryUrl);\n if (!cacheRequest) {\n cacheRequest = this.getCache(registryUrl);\n this.cacheRequests.set(registryUrl, cacheRequest);\n }\n let cachedResult: VersionsEndpointResult;\n try {\n cachedResult = await cacheRequest;\n } finally {\n this.cacheRequests.delete(registryUrl);\n }\n\n const { val: cachedData } = cachedResult.unwrap();\n if (!cachedData) {\n logger.debug(\n { packageName, registryUrl },\n 'Rubygems: endpoint not supported',\n );\n return Result.err('unsupported-api');\n }\n\n const versions = cachedData.packageVersions.get(packageName);\n if (!versions?.length) {\n logger.debug(\n { packageName, registryUrl },\n 'Rubygems: versions not found',\n );\n return Result.err('package-not-found');\n }\n\n return Result.ok(versions);\n }\n\n private async fullSync(registryUrl: string): Promise<VersionsEndpointResult> {\n try {\n const url = `${registryUrl}/versions`;\n const opts: HttpOptions = { headers: { 'Accept-Encoding': 'gzip' } };\n const { body } = await this.http.getText(url, opts);\n return parseFullBody(body);\n } catch (err) {\n if (isNotFound(err)) {\n return Result.err('unsupported-api');\n }\n\n throw err;\n }\n }\n\n private async deltaSync(\n oldCache: VersionsEndpointData,\n registryUrl: string,\n ): Promise<VersionsEndpointResult> {\n try {\n const url = `${registryUrl}/versions`;\n const startByte = oldCache.contentLength - oldCache.contentTail.length;\n const opts: HttpOptions = {\n headers: {\n ['Accept-Encoding']: 'deflate, compress, br', // Note: `gzip` usage breaks http client, when used with `Range` header\n ['Range']: `bytes=${startByte}-`,\n },\n };\n const { statusCode, body } = await this.http.getText(url, opts);\n\n /**\n * Rubygems will return the full body instead of `416 Range Not Satisfiable`.\n * In this case, status code will be 200 instead of 206.\n */\n if (isOk(statusCode)) {\n return parseFullBody(body);\n }\n\n /**\n * We request data in range that overlaps previously fetched data.\n * If the head of the response doesn't match the tail of the previous response,\n * it means that the data we have is no longer valid.\n * In this case we start over with a full sync.\n */\n const contentHead = getContentHead(body);\n if (contentHead !== oldCache.contentTail) {\n return this.fullSync(registryUrl);\n }\n\n /**\n * Update the cache with the new data.\n */\n const delta = stripContentHead(body);\n const packageVersions = reconcilePackageVersions(\n oldCache.packageVersions,\n VersionLines.parse(delta),\n );\n const syncedAt = DateTime.now().toJSDate();\n const contentLength = oldCache.contentLength + delta.length;\n const contentTail = getContentTail(body);\n\n return Result.ok({\n packageVersions,\n syncedAt,\n contentLength,\n contentTail,\n });\n } catch (err) {\n /**\n * In case of `416 Range Not Satisfiable` we do a full sync.\n * This is unlikely to happen in real life, but anyway.\n */\n if (isRangeNotSatisfiable(err)) {\n return this.fullSync(registryUrl);\n }\n\n /**\n * If the endpoint is not supported, we stop trying.\n * This is unlikely to happen in real life, but still.\n */\n if (isNotFound(err)) {\n return Result.err('unsupported-api');\n }\n\n throw err;\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ensureTrailingSlash } from "../../../util/url.js";
|
|
2
|
+
import { isServiceUnavailable } from "../../../util/http/status.js";
|
|
2
3
|
import { ExternalHostError } from "../../../types/errors/external-host-error.js";
|
|
3
4
|
import { withCache } from "../../../util/cache/package/with-cache.js";
|
|
4
5
|
import { Datasource } from "../datasource.js";
|
|
@@ -27,7 +28,7 @@ var TerraformDatasource = class TerraformDatasource extends Datasource {
|
|
|
27
28
|
}
|
|
28
29
|
handleHttpErrors(err) {
|
|
29
30
|
if (["EAI_AGAIN"].includes(err.code)) throw new ExternalHostError(err);
|
|
30
|
-
if (err
|
|
31
|
+
if (isServiceUnavailable(err)) throw new ExternalHostError(err);
|
|
31
32
|
}
|
|
32
33
|
};
|
|
33
34
|
export { TerraformDatasource };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.js","names":[],"sources":["../../../../lib/modules/datasource/terraform-module/base.ts"],"sourcesContent":["import { ExternalHostError } from '../../../types/errors/external-host-error.ts';\nimport { withCache } from '../../../util/cache/package/with-cache.ts';\nimport type { HttpError } from '../../../util/http/index.ts';\nimport { ensureTrailingSlash } from '../../../util/url.ts';\nimport { Datasource } from '../datasource.ts';\nimport { ServiceDiscoveryResponse } from './schema.ts';\n\nconst terraformId = 'terraform';\n\n// TODO: extract to a separate directory structure (#10532)\nexport abstract class TerraformDatasource extends Datasource {\n static id = terraformId;\n\n static readonly terraformRegistryUrl = 'https://registry.terraform.io';\n static readonly openTofuApiUrl = 'https://api.opentofu.org';\n static readonly openTofuRegistryUrl = 'https://registry.opentofu.org';\n\n private async _getTerraformServiceDiscoveryResult(\n registryUrl: string,\n ): Promise<ServiceDiscoveryResponse> {\n const discoveryURL = TerraformDatasource.getDiscoveryUrl(registryUrl);\n const { body: res } = await this.http.getJson(\n discoveryURL,\n ServiceDiscoveryResponse,\n );\n return res;\n }\n\n getTerraformServiceDiscoveryResult(\n registryUrl: string,\n ): Promise<ServiceDiscoveryResponse> {\n return withCache(\n {\n namespace: `datasource-${terraformId}`,\n key: TerraformDatasource.getDiscoveryUrl(registryUrl),\n ttlMinutes: 1440,\n },\n () => this._getTerraformServiceDiscoveryResult(registryUrl),\n );\n }\n\n private static getDiscoveryUrl(registryUrl: string): string {\n return `${ensureTrailingSlash(registryUrl)}.well-known/terraform.json`;\n }\n\n override handleHttpErrors(err: HttpError): void {\n const failureCodes = ['EAI_AGAIN'];\n if (failureCodes.includes(err.code)) {\n throw new ExternalHostError(err);\n }\n if (err
|
|
1
|
+
{"version":3,"file":"base.js","names":[],"sources":["../../../../lib/modules/datasource/terraform-module/base.ts"],"sourcesContent":["import { ExternalHostError } from '../../../types/errors/external-host-error.ts';\nimport { withCache } from '../../../util/cache/package/with-cache.ts';\nimport type { HttpError } from '../../../util/http/index.ts';\nimport { isServiceUnavailable } from '../../../util/http/status.ts';\nimport { ensureTrailingSlash } from '../../../util/url.ts';\nimport { Datasource } from '../datasource.ts';\nimport { ServiceDiscoveryResponse } from './schema.ts';\n\nconst terraformId = 'terraform';\n\n// TODO: extract to a separate directory structure (#10532)\nexport abstract class TerraformDatasource extends Datasource {\n static id = terraformId;\n\n static readonly terraformRegistryUrl = 'https://registry.terraform.io';\n static readonly openTofuApiUrl = 'https://api.opentofu.org';\n static readonly openTofuRegistryUrl = 'https://registry.opentofu.org';\n\n private async _getTerraformServiceDiscoveryResult(\n registryUrl: string,\n ): Promise<ServiceDiscoveryResponse> {\n const discoveryURL = TerraformDatasource.getDiscoveryUrl(registryUrl);\n const { body: res } = await this.http.getJson(\n discoveryURL,\n ServiceDiscoveryResponse,\n );\n return res;\n }\n\n getTerraformServiceDiscoveryResult(\n registryUrl: string,\n ): Promise<ServiceDiscoveryResponse> {\n return withCache(\n {\n namespace: `datasource-${terraformId}`,\n key: TerraformDatasource.getDiscoveryUrl(registryUrl),\n ttlMinutes: 1440,\n },\n () => this._getTerraformServiceDiscoveryResult(registryUrl),\n );\n }\n\n private static getDiscoveryUrl(registryUrl: string): string {\n return `${ensureTrailingSlash(registryUrl)}.well-known/terraform.json`;\n }\n\n override handleHttpErrors(err: HttpError): void {\n const failureCodes = ['EAI_AGAIN'];\n if (failureCodes.includes(err.code)) {\n throw new ExternalHostError(err);\n }\n if (isServiceUnavailable(err)) {\n throw new ExternalHostError(err);\n }\n }\n}\n"],"mappings":";;;;;;;AAQA,MAAM,cAAc;AAGpB,IAAsB,sBAAtB,MAAsB,4BAA4B,WAAW;CAC3D,OAAO,KAAK;CAEZ,OAAgB,uBAAuB;CACvC,OAAgB,iBAAiB;CACjC,OAAgB,sBAAsB;CAEtC,MAAc,oCACZ,aACmC;EACnC,MAAM,eAAe,oBAAoB,gBAAgB,WAAW;EACpE,MAAM,EAAE,MAAM,QAAQ,MAAM,KAAK,KAAK,QACpC,cACA,wBACF;EACA,OAAO;CACT;CAEA,mCACE,aACmC;EACnC,OAAO,UACL;GACE,WAAW,cAAc;GACzB,KAAK,oBAAoB,gBAAgB,WAAW;GACpD,YAAY;EACd,SACM,KAAK,oCAAoC,WAAW,CAC5D;CACF;CAEA,OAAe,gBAAgB,aAA6B;EAC1D,OAAO,GAAG,oBAAoB,WAAW,EAAE;CAC7C;CAEA,iBAA0B,KAAsB;EAE9C,IAAI,CADkB,WACP,CAAC,CAAC,SAAS,IAAI,IAAI,GAChC,MAAM,IAAI,kBAAkB,GAAG;EAEjC,IAAI,qBAAqB,GAAG,GAC1B,MAAM,IAAI,kBAAkB,GAAG;CAEnC;AACF"}
|
|
@@ -2,10 +2,9 @@ import { regEx } from "../../../util/regex.js";
|
|
|
2
2
|
import { logger } from "../../../logger/index.js";
|
|
3
3
|
import { buildCacheKey } from "../../../util/cache/package/key.js";
|
|
4
4
|
import { getQueryString, joinUrlParts } from "../../../util/url.js";
|
|
5
|
+
import { isNotFound } from "../../../util/http/status.js";
|
|
5
6
|
import { ExternalHostError } from "../../../types/errors/external-host-error.js";
|
|
6
7
|
import { id } from "../../versioning/hashicorp/index.js";
|
|
7
|
-
import { RequestError } from "../../../util/http/got.js";
|
|
8
|
-
import "../../../util/http/index.js";
|
|
9
8
|
import { map } from "../../../util/promises.js";
|
|
10
9
|
import { TerraformDatasource } from "../terraform-module/base.js";
|
|
11
10
|
import { createSDBackendURL } from "../terraform-module/utils.js";
|
|
@@ -163,7 +162,7 @@ var TerraformProviderDatasource = class TerraformProviderDatasource extends Terr
|
|
|
163
162
|
const { body } = await this.http.getJson(`${baseUrl}/${version}/download/linux/amd64`, OpenTofuProviderPackagesResponse);
|
|
164
163
|
return body;
|
|
165
164
|
} catch (err) {
|
|
166
|
-
if (!(err
|
|
165
|
+
if (!isNotFound(err)) throw err;
|
|
167
166
|
}
|
|
168
167
|
return await this.fetchProviderPackagesForAvailablePlatform(baseUrl, version);
|
|
169
168
|
} catch (err) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["hashicorpVersioning.id","p.map","HttpError"],"sources":["../../../../lib/modules/datasource/terraform-provider/index.ts"],"sourcesContent":["import { logger } from '../../../logger/index.ts';\nimport { ExternalHostError } from '../../../types/errors/external-host-error.ts';\nimport { buildCacheKey } from '../../../util/cache/package/key.ts';\nimport { HttpError } from '../../../util/http/index.ts';\nimport * as p from '../../../util/promises.ts';\nimport { regEx } from '../../../util/regex.ts';\nimport { getQueryString, joinUrlParts } from '../../../util/url.ts';\nimport * as hashicorpVersioning from '../../versioning/hashicorp/index.ts';\nimport { TerraformDatasource } from '../terraform-module/base.ts';\nimport { createSDBackendURL } from '../terraform-module/utils.ts';\nimport type { GetReleasesConfig, ReleaseResult } from '../types.ts';\nimport {\n OpenTofuProviderDocsResponse,\n OpenTofuProviderPackagesResponse,\n type TerraformBuild,\n TerraformProviderReleaseBackend,\n TerraformProviderV2Response,\n TerraformProviderVersions,\n TerraformRegistryBuildResponse,\n TerraformRegistryVersions,\n VersionDetailResponse,\n} from './schema.ts';\n\nexport class TerraformProviderDatasource extends TerraformDatasource {\n static override readonly id = 'terraform-provider';\n\n static readonly hashicorpReleaseUrl = 'https://releases.hashicorp.com';\n\n private static readonly defaultRegistryUrls = [\n TerraformProviderDatasource.terraformRegistryUrl,\n TerraformProviderDatasource.hashicorpReleaseUrl,\n ];\n\n static getDefaultRegistryUrls(): string[] {\n return TerraformProviderDatasource.defaultRegistryUrls;\n }\n\n static repositoryRegex = regEx(/^hashicorp\\/(?<packageName>\\S+)$/);\n\n constructor() {\n super(TerraformProviderDatasource.id);\n }\n\n override getDefaultRegistryUrls(_packageName: string): string[] {\n return TerraformProviderDatasource.defaultRegistryUrls;\n }\n\n override readonly defaultVersioning = hashicorpVersioning.id;\n\n override readonly registryStrategy = 'hunt';\n\n override readonly releaseTimestampSupport = true;\n override readonly releaseTimestampNote =\n 'The release timestamp is only available for `registry.terraform.io` (v2 API) and `registry.opentofu.org` (via `api.opentofu.org`). Other registries using the Provider Registry Protocol do not provide timestamps.';\n override readonly sourceUrlSupport = 'package';\n override readonly sourceUrlNote =\n 'For `registry.terraform.io`, the source URL is taken from the `source` field of the v2 API response. For `registry.opentofu.org`, it is derived from the package name following the OpenTofu registry policy of `github.com/NAMESPACE/terraform-provider-NAME`.';\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 logger.trace(\n `terraform-provider.getDependencies() packageName: ${packageName}`,\n );\n\n if (registryUrl === TerraformProviderDatasource.terraformRegistryUrl) {\n return await this.queryTerraformRegistryV2(registryUrl, packageName);\n }\n if (\n registryUrl === TerraformProviderDatasource.openTofuRegistryUrl ||\n registryUrl === TerraformProviderDatasource.openTofuApiUrl\n ) {\n return await this.queryOpenTofuRegistry(packageName);\n }\n if (registryUrl === TerraformProviderDatasource.hashicorpReleaseUrl) {\n return await this.queryReleaseBackend(packageName, registryUrl);\n }\n\n // Fall back to the standard Provider Registry Protocol for other registries.\n return await this.queryProviderRegistry(registryUrl, packageName);\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n const url = config.registryUrl;\n const repo = TerraformProviderDatasource.getRepository(config);\n return this.cached(\n {\n key: buildCacheKey('getReleases', url, repo),\n fallback: true,\n },\n () => this.fetchReleases(config),\n );\n }\n\n private static getRepository({ packageName }: GetReleasesConfig): string {\n return packageName.includes('/') ? packageName : `hashicorp/${packageName}`;\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 API\n * which only exposed the timestamp for the latest version.\n */\n private async queryTerraformRegistryV2(\n registryUrl: string,\n packageName: string,\n ): Promise<ReleaseResult> {\n const repository = TerraformProviderDatasource.getRepository({\n packageName,\n });\n const providerUrl = `${joinUrlParts(\n registryUrl,\n 'v2/providers',\n repository,\n )}?${getQueryString({ include: 'provider-versions' })}`;\n const { body: res } = await this.http.getJson(\n providerUrl,\n TerraformProviderV2Response,\n );\n res.homepage = `${registryUrl}/providers/${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 provider versions with release timestamps.\n */\n private async queryOpenTofuRegistry(\n packageName: string,\n ): Promise<ReleaseResult> {\n const repository = TerraformProviderDatasource.getRepository({\n packageName,\n });\n const docsUrl = joinUrlParts(\n TerraformProviderDatasource.openTofuApiUrl,\n 'registry/docs/providers',\n repository,\n 'index.json',\n );\n const { body: res } = await this.http.getJson(\n docsUrl,\n OpenTofuProviderDocsResponse,\n );\n res.homepage = `https://search.opentofu.org/provider/${repository}`;\n\n // The OpenTofu registry only indexes providers hosted on GitHub under the\n // `NAMESPACE/terraform-provider-NAME` repository naming convention, so the\n // source URL can be derived deterministically from the package name.\n // https://github.com/opentofu/registry/blob/main/PROCEDURES.md\n const [namespace, name] = repository.split('/');\n res.sourceUrl = `https://github.com/${namespace}/terraform-provider-${name}`;\n\n return res;\n }\n\n /**\n * Query a registry using the Provider Registry Protocol that all registries\n * are required to implement.\n * https://www.terraform.io/internals/provider-registry-protocol\n */\n private async queryProviderRegistry(\n registryUrl: string,\n packageName: string,\n ): Promise<ReleaseResult> {\n const repository = TerraformProviderDatasource.getRepository({\n packageName,\n });\n const serviceDiscovery =\n await this.getTerraformServiceDiscoveryResult(registryUrl);\n const backendURL = createSDBackendURL(\n registryUrl,\n 'providers.v1',\n serviceDiscovery,\n `${repository}/versions`,\n );\n const res = (await this.http.getJson(backendURL, TerraformProviderVersions))\n .body;\n const dep: ReleaseResult = {\n releases: res.versions.map(({ version }) => ({\n version,\n })),\n };\n return dep;\n }\n\n private async queryReleaseBackend(\n packageName: string,\n registryURL: string,\n ): Promise<ReleaseResult | null> {\n const hashicorpPackage = packageName.replace('hashicorp/', '');\n const backendLookUpName = `terraform-provider-${hashicorpPackage}`;\n const backendURL = joinUrlParts(\n registryURL,\n backendLookUpName,\n `index.json`,\n );\n const res = (\n await this.http.getJson(backendURL, TerraformProviderReleaseBackend)\n ).body;\n\n const dep: ReleaseResult = {\n releases: Object.keys(res.versions).map((version) => ({\n version,\n })),\n sourceUrl: joinUrlParts(\n 'https://github.com/terraform-providers',\n backendLookUpName,\n ),\n };\n return dep;\n }\n\n private async fetchBuilds(\n registryURL: string,\n repository: string,\n version: string,\n ): Promise<TerraformBuild[] | null> {\n if (registryURL === TerraformProviderDatasource.hashicorpReleaseUrl) {\n // check if registryURL === secondary backend\n const repositoryRegexResult =\n TerraformProviderDatasource.repositoryRegex.exec(repository)?.groups;\n if (!repositoryRegexResult) {\n // non hashicorp builds are not supported with releases.hashicorp.com\n return null;\n }\n const packageName = repositoryRegexResult.packageName;\n const backendLookUpName = `terraform-provider-${packageName}`;\n let versionReleaseBackend: VersionDetailResponse;\n try {\n versionReleaseBackend = await this.getReleaseBackendIndex(\n backendLookUpName,\n version,\n );\n } catch (err) {\n if (err instanceof ExternalHostError) {\n throw err;\n }\n logger.debug(\n { err, backendLookUpName, version },\n `Failed to retrieve builds for ${backendLookUpName} ${version}`,\n );\n // throw an error to disable caching\n throw new ExternalHostError(err);\n }\n return versionReleaseBackend.builds;\n }\n\n // check public or private Terraform registry\n const serviceDiscovery =\n await this.getTerraformServiceDiscoveryResult(registryURL);\n if (!serviceDiscovery) {\n // throw an error to disable caching\n throw new ExternalHostError(\n new Error(`Service discovery not found for ${registryURL}`),\n );\n }\n const backendURL = createSDBackendURL(\n registryURL,\n 'providers.v1',\n serviceDiscovery,\n repository,\n );\n const versionsResponse = (\n await this.http.getJson(\n `${backendURL}/versions`,\n TerraformRegistryVersions,\n )\n ).body;\n if (!versionsResponse.versions) {\n // throw an error to disable caching\n throw new ExternalHostError(\n new Error(`Failed to retrieve version list for ${backendURL}`),\n );\n }\n const builds = versionsResponse.versions.find(\n (value) => value.version === version,\n );\n if (!builds) {\n // should never happen, but just in case\n // throw an error to disable caching\n throw new ExternalHostError(\n new Error(\n `No builds found for ${repository}:${version} on ${registryURL}`,\n ),\n );\n }\n const result = await p.map(\n builds.platforms,\n async (platform) => {\n const buildURL = `${backendURL}/${version}/download/${platform.os}/${platform.arch}`;\n try {\n const res = (\n await this.http.getJson(buildURL, TerraformRegistryBuildResponse)\n ).body;\n const newBuild: TerraformBuild = {\n name: repository,\n url: res.download_url,\n version,\n ...res,\n };\n return newBuild;\n } catch (err) {\n /* v8 ignore next -- hard to test */\n if (err instanceof ExternalHostError) {\n throw err;\n }\n logger.debug({ err, url: buildURL }, 'Failed to retrieve build');\n // throw an error to disable caching\n throw new ExternalHostError(err);\n }\n },\n { concurrency: 4 },\n );\n\n return result;\n }\n\n getBuilds(\n registryURL: string,\n repository: string,\n version: string,\n ): Promise<TerraformBuild[] | null> {\n return this.cached(\n {\n key: buildCacheKey('getBuilds', registryURL, repository, version),\n },\n () => this.fetchBuilds(registryURL, repository, version),\n );\n }\n\n /**\n * A single platform's download endpoint returns the hashes for all platforms,\n * so we query `linux/amd64` and fall back to `/versions` discovery only when a\n * provider lacks that platform (404).\n * See https://github.com/opentofu/opentofu/pull/3434\n */\n private async fetchProviderPackages(\n repository: string,\n version: string,\n ): Promise<string[] | null> {\n const baseUrl = joinUrlParts(\n TerraformProviderDatasource.openTofuRegistryUrl,\n 'v1/providers',\n repository,\n );\n\n try {\n try {\n const { body } = await this.http.getJson(\n `${baseUrl}/${version}/download/linux/amd64`,\n OpenTofuProviderPackagesResponse,\n );\n return body;\n } catch (err) {\n if (!(err instanceof HttpError) || err.response?.statusCode !== 404) {\n throw err;\n }\n }\n return await this.fetchProviderPackagesForAvailablePlatform(\n baseUrl,\n version,\n );\n } catch (err) {\n if (err instanceof ExternalHostError) {\n throw err;\n }\n logger.debug(\n { err, repository, version },\n `Failed to retrieve provider packages for ${repository}@${version}`,\n );\n throw new ExternalHostError(err);\n }\n }\n\n /**\n * Some providers do not publish a `linux/amd64` build, so discover an\n * available platform via `/versions` and fetch its download endpoint.\n */\n private async fetchProviderPackagesForAvailablePlatform(\n baseUrl: string,\n version: string,\n ): Promise<string[] | null> {\n const { body: versionsResponse } = await this.http.getJson(\n `${baseUrl}/versions`,\n TerraformRegistryVersions,\n );\n const platform = versionsResponse.versions?.find(\n (entry) => entry.version === version,\n )?.platforms?.[0];\n if (!platform) {\n return null;\n }\n const { body: hashes } = await this.http.getJson(\n `${baseUrl}/${version}/download/${platform.os}/${platform.arch}`,\n OpenTofuProviderPackagesResponse,\n );\n return hashes;\n }\n\n getProviderPackages(\n repository: string,\n version: string,\n ): Promise<string[] | null> {\n return this.cached(\n {\n key: buildCacheKey('getProviderPackages', repository, version),\n },\n () => this.fetchProviderPackages(repository, version),\n );\n }\n\n private async fetchZipHashes(\n zipHashUrl: string,\n ): Promise<string[] | undefined> {\n // The hashes are formatted as the result of sha256sum in plain text, each line: <hash>\\t<filename>\n let rawHashData: string;\n try {\n rawHashData = (await this.http.getText(zipHashUrl)).body;\n } catch (err) {\n /* v8 ignore next -- hard to test */\n if (err instanceof ExternalHostError) {\n throw err;\n }\n logger.debug(\n { err, zipHashUrl },\n `Failed to retrieve zip hashes from ${zipHashUrl}`,\n );\n return undefined;\n }\n\n return rawHashData\n .trimEnd()\n .split('\\n')\n .map((line) => line.split(regEx(/\\s/))[0]);\n }\n\n getZipHashes(zipHashUrl: string): Promise<string[] | undefined> {\n return this.cached(\n {\n key: buildCacheKey('getZipHashes', zipHashUrl),\n },\n () => this.fetchZipHashes(zipHashUrl),\n );\n }\n\n private async fetchReleaseBackendIndex(\n backendLookUpName: string,\n version: string,\n ): Promise<VersionDetailResponse> {\n return (\n await this.http.getJson(\n `${TerraformProviderDatasource.hashicorpReleaseUrl}/${backendLookUpName}/${version}/index.json`,\n VersionDetailResponse,\n )\n ).body;\n }\n\n getReleaseBackendIndex(\n backendLookUpName: string,\n version: string,\n ): Promise<VersionDetailResponse> {\n return this.cached(\n {\n key: buildCacheKey(\n 'getReleaseBackendIndex',\n backendLookUpName,\n version,\n ),\n },\n () => this.fetchReleaseBackendIndex(backendLookUpName, version),\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;;AAuBA,IAAa,8BAAb,MAAa,oCAAoC,oBAAoB;CACnE,OAAyB,KAAK;CAE9B,OAAgB,sBAAsB;CAEtC,OAAwB,sBAAsB,CAC5C,4BAA4B,sBAC5B,4BAA4B,mBAC9B;CAEA,OAAO,yBAAmC;EACxC,OAAO,4BAA4B;CACrC;CAEA,OAAO,kBAAkB,MAAM,kCAAkC;CAEjE,cAAc;EACZ,MAAM,4BAA4B,EAAE;CACtC;CAEA,uBAAgC,cAAgC;EAC9D,OAAO,4BAA4B;CACrC;CAEA,oBAAsCA;CAEtC,mBAAqC;CAErC,0BAA4C;CAC5C,uBACE;CACF,mBAAqC;CACrC,gBACE;CAEF,MAAc,cAAc,EAC1B,aACA,eACmD;;EAEnD,IAAI,CAAC,aACH,OAAO;EAET,OAAO,MACL,qDAAqD,aACvD;EAEA,IAAI,gBAAgB,4BAA4B,sBAC9C,OAAO,MAAM,KAAK,yBAAyB,aAAa,WAAW;EAErE,IACE,gBAAgB,4BAA4B,uBAC5C,gBAAgB,4BAA4B,gBAE5C,OAAO,MAAM,KAAK,sBAAsB,WAAW;EAErD,IAAI,gBAAgB,4BAA4B,qBAC9C,OAAO,MAAM,KAAK,oBAAoB,aAAa,WAAW;EAIhE,OAAO,MAAM,KAAK,sBAAsB,aAAa,WAAW;CAClE;CAEA,YAAY,QAA0D;EACpE,MAAM,MAAM,OAAO;EACnB,MAAM,OAAO,4BAA4B,cAAc,MAAM;EAC7D,OAAO,KAAK,OACV;GACE,KAAK,cAAc,eAAe,KAAK,IAAI;GAC3C,UAAU;EACZ,SACM,KAAK,cAAc,MAAM,CACjC;CACF;CAEA,OAAe,cAAc,EAAE,eAA0C;EACvE,OAAO,YAAY,SAAS,GAAG,IAAI,cAAc,aAAa;CAChE;;;;;;;CAQA,MAAc,yBACZ,aACA,aACwB;EACxB,MAAM,aAAa,4BAA4B,cAAc,EAC3D,YACF,CAAC;EACD,MAAM,cAAc,GAAG,aACrB,aACA,gBACA,UACF,EAAE,GAAG,eAAe,EAAE,SAAS,oBAAoB,CAAC;EACpD,MAAM,EAAE,MAAM,QAAQ,MAAM,KAAK,KAAK,QACpC,aACA,2BACF;EACA,IAAI,WAAW,GAAG,YAAY,aAAa;EAC3C,OAAO;CACT;;;;;;;;CASA,MAAc,sBACZ,aACwB;EACxB,MAAM,aAAa,4BAA4B,cAAc,EAC3D,YACF,CAAC;EACD,MAAM,UAAU,aACd,4BAA4B,gBAC5B,2BACA,YACA,YACF;EACA,MAAM,EAAE,MAAM,QAAQ,MAAM,KAAK,KAAK,QACpC,SACA,4BACF;EACA,IAAI,WAAW,wCAAwC;EAMvD,MAAM,CAAC,WAAW,QAAQ,WAAW,MAAM,GAAG;EAC9C,IAAI,YAAY,sBAAsB,UAAU,sBAAsB;EAEtE,OAAO;CACT;;;;;;CAOA,MAAc,sBACZ,aACA,aACwB;EACxB,MAAM,aAAa,4BAA4B,cAAc,EAC3D,YACF,CAAC;EACD,MAAM,mBACJ,MAAM,KAAK,mCAAmC,WAAW;EAC3D,MAAM,aAAa,mBACjB,aACA,gBACA,kBACA,GAAG,WAAW,UAChB;EAQA,OAAO,EAJL,WAHW,MAAM,KAAK,KAAK,QAAQ,YAAY,yBAAyB,EAAA,CACvE,KAEa,SAAS,KAAK,EAAE,eAAe,EAC3C,QACF,EAAE,EAEK;CACX;CAEA,MAAc,oBACZ,aACA,aAC+B;EAE/B,MAAM,oBAAoB,sBADD,YAAY,QAAQ,cAAc,EACI;EAC/D,MAAM,aAAa,aACjB,aACA,mBACA,YACF;EACA,MAAM,OACJ,MAAM,KAAK,KAAK,QAAQ,YAAY,+BAA+B,EAAA,CACnE;EAWF,OAAO;GARL,UAAU,OAAO,KAAK,IAAI,QAAQ,CAAC,CAAC,KAAK,aAAa,EACpD,QACF,EAAE;GACF,WAAW,aACT,0CACA,iBACF;EAEO;CACX;CAEA,MAAc,YACZ,aACA,YACA,SACkC;EAClC,IAAI,gBAAgB,4BAA4B,qBAAqB;GAEnE,MAAM,wBACJ,4BAA4B,gBAAgB,KAAK,UAAU,CAAC,EAAE;GAChE,IAAI,CAAC,uBAEH,OAAO;GAGT,MAAM,oBAAoB,sBADN,sBAAsB;GAE1C,IAAI;GACJ,IAAI;IACF,wBAAwB,MAAM,KAAK,uBACjC,mBACA,OACF;GACF,SAAS,KAAK;IACZ,IAAI,eAAe,mBACjB,MAAM;IAER,OAAO,MACL;KAAE;KAAK;KAAmB;IAAQ,GAClC,iCAAiC,kBAAkB,GAAG,SACxD;IAEA,MAAM,IAAI,kBAAkB,GAAG;GACjC;GACA,OAAO,sBAAsB;EAC/B;EAGA,MAAM,mBACJ,MAAM,KAAK,mCAAmC,WAAW;EAC3D,IAAI,CAAC,kBAEH,MAAM,IAAI,kCACR,IAAI,MAAM,mCAAmC,aAAa,CAC5D;EAEF,MAAM,aAAa,mBACjB,aACA,gBACA,kBACA,UACF;EACA,MAAM,oBACJ,MAAM,KAAK,KAAK,QACd,GAAG,WAAW,YACd,yBACF,EAAA,CACA;EACF,IAAI,CAAC,iBAAiB,UAEpB,MAAM,IAAI,kCACR,IAAI,MAAM,uCAAuC,YAAY,CAC/D;EAEF,MAAM,SAAS,iBAAiB,SAAS,MACtC,UAAU,MAAM,YAAY,OAC/B;EACA,IAAI,CAAC,QAGH,MAAM,IAAI,kCACR,IAAI,MACF,uBAAuB,WAAW,GAAG,QAAQ,MAAM,aACrD,CACF;EA8BF,OAAO,MA5BcC,IACnB,OAAO,WACP,OAAO,aAAa;GAClB,MAAM,WAAW,GAAG,WAAW,GAAG,QAAQ,YAAY,SAAS,GAAG,GAAG,SAAS;GAC9E,IAAI;IACF,MAAM,OACJ,MAAM,KAAK,KAAK,QAAQ,UAAU,8BAA8B,EAAA,CAChE;IAOF,OAAO;KALL,MAAM;KACN,KAAK,IAAI;KACT;KACA,GAAG;IAES;GAChB,SAAS,KAAK;;IAEZ,IAAI,eAAe,mBACjB,MAAM;IAER,OAAO,MAAM;KAAE;KAAK,KAAK;IAAS,GAAG,0BAA0B;IAE/D,MAAM,IAAI,kBAAkB,GAAG;GACjC;EACF,GACA,EAAE,aAAa,EAAE,CACnB;CAGF;CAEA,UACE,aACA,YACA,SACkC;EAClC,OAAO,KAAK,OACV,EACE,KAAK,cAAc,aAAa,aAAa,YAAY,OAAO,EAClE,SACM,KAAK,YAAY,aAAa,YAAY,OAAO,CACzD;CACF;;;;;;;CAQA,MAAc,sBACZ,YACA,SAC0B;EAC1B,MAAM,UAAU,aACd,4BAA4B,qBAC5B,gBACA,UACF;EAEA,IAAI;GACF,IAAI;IACF,MAAM,EAAE,SAAS,MAAM,KAAK,KAAK,QAC/B,GAAG,QAAQ,GAAG,QAAQ,wBACtB,gCACF;IACA,OAAO;GACT,SAAS,KAAK;IACZ,IAAI,EAAE,eAAeC,iBAAc,IAAI,UAAU,eAAe,KAC9D,MAAM;GAEV;GACA,OAAO,MAAM,KAAK,0CAChB,SACA,OACF;EACF,SAAS,KAAK;GACZ,IAAI,eAAe,mBACjB,MAAM;GAER,OAAO,MACL;IAAE;IAAK;IAAY;GAAQ,GAC3B,4CAA4C,WAAW,GAAG,SAC5D;GACA,MAAM,IAAI,kBAAkB,GAAG;EACjC;CACF;;;;;CAMA,MAAc,0CACZ,SACA,SAC0B;EAC1B,MAAM,EAAE,MAAM,qBAAqB,MAAM,KAAK,KAAK,QACjD,GAAG,QAAQ,YACX,yBACF;EACA,MAAM,WAAW,iBAAiB,UAAU,MACzC,UAAU,MAAM,YAAY,OAC/B,CAAC,EAAE,YAAY;EACf,IAAI,CAAC,UACH,OAAO;EAET,MAAM,EAAE,MAAM,WAAW,MAAM,KAAK,KAAK,QACvC,GAAG,QAAQ,GAAG,QAAQ,YAAY,SAAS,GAAG,GAAG,SAAS,QAC1D,gCACF;EACA,OAAO;CACT;CAEA,oBACE,YACA,SAC0B;EAC1B,OAAO,KAAK,OACV,EACE,KAAK,cAAc,uBAAuB,YAAY,OAAO,EAC/D,SACM,KAAK,sBAAsB,YAAY,OAAO,CACtD;CACF;CAEA,MAAc,eACZ,YAC+B;EAE/B,IAAI;EACJ,IAAI;GACF,eAAe,MAAM,KAAK,KAAK,QAAQ,UAAU,EAAA,CAAG;EACtD,SAAS,KAAK;;GAEZ,IAAI,eAAe,mBACjB,MAAM;GAER,OAAO,MACL;IAAE;IAAK;GAAW,GAClB,sCAAsC,YACxC;GACA;EACF;EAEA,OAAO,YACJ,QAAQ,CAAC,CACT,MAAM,IAAI,CAAC,CACX,KAAK,SAAS,KAAK,MAAM,MAAM,IAAI,CAAC,CAAC,CAAC,EAAE;CAC7C;CAEA,aAAa,YAAmD;EAC9D,OAAO,KAAK,OACV,EACE,KAAK,cAAc,gBAAgB,UAAU,EAC/C,SACM,KAAK,eAAe,UAAU,CACtC;CACF;CAEA,MAAc,yBACZ,mBACA,SACgC;EAChC,QACE,MAAM,KAAK,KAAK,QACd,GAAG,4BAA4B,oBAAoB,GAAG,kBAAkB,GAAG,QAAQ,cACnF,qBACF,EAAA,CACA;CACJ;CAEA,uBACE,mBACA,SACgC;EAChC,OAAO,KAAK,OACV,EACE,KAAK,cACH,0BACA,mBACA,OACF,EACF,SACM,KAAK,yBAAyB,mBAAmB,OAAO,CAChE;CACF;AACF"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["hashicorpVersioning.id","p.map"],"sources":["../../../../lib/modules/datasource/terraform-provider/index.ts"],"sourcesContent":["import { logger } from '../../../logger/index.ts';\nimport { ExternalHostError } from '../../../types/errors/external-host-error.ts';\nimport { buildCacheKey } from '../../../util/cache/package/key.ts';\nimport { isNotFound } from '../../../util/http/status.ts';\nimport * as p from '../../../util/promises.ts';\nimport { regEx } from '../../../util/regex.ts';\nimport { getQueryString, joinUrlParts } from '../../../util/url.ts';\nimport * as hashicorpVersioning from '../../versioning/hashicorp/index.ts';\nimport { TerraformDatasource } from '../terraform-module/base.ts';\nimport { createSDBackendURL } from '../terraform-module/utils.ts';\nimport type { GetReleasesConfig, ReleaseResult } from '../types.ts';\nimport {\n OpenTofuProviderDocsResponse,\n OpenTofuProviderPackagesResponse,\n type TerraformBuild,\n TerraformProviderReleaseBackend,\n TerraformProviderV2Response,\n TerraformProviderVersions,\n TerraformRegistryBuildResponse,\n TerraformRegistryVersions,\n VersionDetailResponse,\n} from './schema.ts';\n\nexport class TerraformProviderDatasource extends TerraformDatasource {\n static override readonly id = 'terraform-provider';\n\n static readonly hashicorpReleaseUrl = 'https://releases.hashicorp.com';\n\n private static readonly defaultRegistryUrls = [\n TerraformProviderDatasource.terraformRegistryUrl,\n TerraformProviderDatasource.hashicorpReleaseUrl,\n ];\n\n static getDefaultRegistryUrls(): string[] {\n return TerraformProviderDatasource.defaultRegistryUrls;\n }\n\n static repositoryRegex = regEx(/^hashicorp\\/(?<packageName>\\S+)$/);\n\n constructor() {\n super(TerraformProviderDatasource.id);\n }\n\n override getDefaultRegistryUrls(_packageName: string): string[] {\n return TerraformProviderDatasource.defaultRegistryUrls;\n }\n\n override readonly defaultVersioning = hashicorpVersioning.id;\n\n override readonly registryStrategy = 'hunt';\n\n override readonly releaseTimestampSupport = true;\n override readonly releaseTimestampNote =\n 'The release timestamp is only available for `registry.terraform.io` (v2 API) and `registry.opentofu.org` (via `api.opentofu.org`). Other registries using the Provider Registry Protocol do not provide timestamps.';\n override readonly sourceUrlSupport = 'package';\n override readonly sourceUrlNote =\n 'For `registry.terraform.io`, the source URL is taken from the `source` field of the v2 API response. For `registry.opentofu.org`, it is derived from the package name following the OpenTofu registry policy of `github.com/NAMESPACE/terraform-provider-NAME`.';\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 logger.trace(\n `terraform-provider.getDependencies() packageName: ${packageName}`,\n );\n\n if (registryUrl === TerraformProviderDatasource.terraformRegistryUrl) {\n return await this.queryTerraformRegistryV2(registryUrl, packageName);\n }\n if (\n registryUrl === TerraformProviderDatasource.openTofuRegistryUrl ||\n registryUrl === TerraformProviderDatasource.openTofuApiUrl\n ) {\n return await this.queryOpenTofuRegistry(packageName);\n }\n if (registryUrl === TerraformProviderDatasource.hashicorpReleaseUrl) {\n return await this.queryReleaseBackend(packageName, registryUrl);\n }\n\n // Fall back to the standard Provider Registry Protocol for other registries.\n return await this.queryProviderRegistry(registryUrl, packageName);\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n const url = config.registryUrl;\n const repo = TerraformProviderDatasource.getRepository(config);\n return this.cached(\n {\n key: buildCacheKey('getReleases', url, repo),\n fallback: true,\n },\n () => this.fetchReleases(config),\n );\n }\n\n private static getRepository({ packageName }: GetReleasesConfig): string {\n return packageName.includes('/') ? packageName : `hashicorp/${packageName}`;\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 API\n * which only exposed the timestamp for the latest version.\n */\n private async queryTerraformRegistryV2(\n registryUrl: string,\n packageName: string,\n ): Promise<ReleaseResult> {\n const repository = TerraformProviderDatasource.getRepository({\n packageName,\n });\n const providerUrl = `${joinUrlParts(\n registryUrl,\n 'v2/providers',\n repository,\n )}?${getQueryString({ include: 'provider-versions' })}`;\n const { body: res } = await this.http.getJson(\n providerUrl,\n TerraformProviderV2Response,\n );\n res.homepage = `${registryUrl}/providers/${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 provider versions with release timestamps.\n */\n private async queryOpenTofuRegistry(\n packageName: string,\n ): Promise<ReleaseResult> {\n const repository = TerraformProviderDatasource.getRepository({\n packageName,\n });\n const docsUrl = joinUrlParts(\n TerraformProviderDatasource.openTofuApiUrl,\n 'registry/docs/providers',\n repository,\n 'index.json',\n );\n const { body: res } = await this.http.getJson(\n docsUrl,\n OpenTofuProviderDocsResponse,\n );\n res.homepage = `https://search.opentofu.org/provider/${repository}`;\n\n // The OpenTofu registry only indexes providers hosted on GitHub under the\n // `NAMESPACE/terraform-provider-NAME` repository naming convention, so the\n // source URL can be derived deterministically from the package name.\n // https://github.com/opentofu/registry/blob/main/PROCEDURES.md\n const [namespace, name] = repository.split('/');\n res.sourceUrl = `https://github.com/${namespace}/terraform-provider-${name}`;\n\n return res;\n }\n\n /**\n * Query a registry using the Provider Registry Protocol that all registries\n * are required to implement.\n * https://www.terraform.io/internals/provider-registry-protocol\n */\n private async queryProviderRegistry(\n registryUrl: string,\n packageName: string,\n ): Promise<ReleaseResult> {\n const repository = TerraformProviderDatasource.getRepository({\n packageName,\n });\n const serviceDiscovery =\n await this.getTerraformServiceDiscoveryResult(registryUrl);\n const backendURL = createSDBackendURL(\n registryUrl,\n 'providers.v1',\n serviceDiscovery,\n `${repository}/versions`,\n );\n const res = (await this.http.getJson(backendURL, TerraformProviderVersions))\n .body;\n const dep: ReleaseResult = {\n releases: res.versions.map(({ version }) => ({\n version,\n })),\n };\n return dep;\n }\n\n private async queryReleaseBackend(\n packageName: string,\n registryURL: string,\n ): Promise<ReleaseResult | null> {\n const hashicorpPackage = packageName.replace('hashicorp/', '');\n const backendLookUpName = `terraform-provider-${hashicorpPackage}`;\n const backendURL = joinUrlParts(\n registryURL,\n backendLookUpName,\n `index.json`,\n );\n const res = (\n await this.http.getJson(backendURL, TerraformProviderReleaseBackend)\n ).body;\n\n const dep: ReleaseResult = {\n releases: Object.keys(res.versions).map((version) => ({\n version,\n })),\n sourceUrl: joinUrlParts(\n 'https://github.com/terraform-providers',\n backendLookUpName,\n ),\n };\n return dep;\n }\n\n private async fetchBuilds(\n registryURL: string,\n repository: string,\n version: string,\n ): Promise<TerraformBuild[] | null> {\n if (registryURL === TerraformProviderDatasource.hashicorpReleaseUrl) {\n // check if registryURL === secondary backend\n const repositoryRegexResult =\n TerraformProviderDatasource.repositoryRegex.exec(repository)?.groups;\n if (!repositoryRegexResult) {\n // non hashicorp builds are not supported with releases.hashicorp.com\n return null;\n }\n const packageName = repositoryRegexResult.packageName;\n const backendLookUpName = `terraform-provider-${packageName}`;\n let versionReleaseBackend: VersionDetailResponse;\n try {\n versionReleaseBackend = await this.getReleaseBackendIndex(\n backendLookUpName,\n version,\n );\n } catch (err) {\n if (err instanceof ExternalHostError) {\n throw err;\n }\n logger.debug(\n { err, backendLookUpName, version },\n `Failed to retrieve builds for ${backendLookUpName} ${version}`,\n );\n // throw an error to disable caching\n throw new ExternalHostError(err);\n }\n return versionReleaseBackend.builds;\n }\n\n // check public or private Terraform registry\n const serviceDiscovery =\n await this.getTerraformServiceDiscoveryResult(registryURL);\n if (!serviceDiscovery) {\n // throw an error to disable caching\n throw new ExternalHostError(\n new Error(`Service discovery not found for ${registryURL}`),\n );\n }\n const backendURL = createSDBackendURL(\n registryURL,\n 'providers.v1',\n serviceDiscovery,\n repository,\n );\n const versionsResponse = (\n await this.http.getJson(\n `${backendURL}/versions`,\n TerraformRegistryVersions,\n )\n ).body;\n if (!versionsResponse.versions) {\n // throw an error to disable caching\n throw new ExternalHostError(\n new Error(`Failed to retrieve version list for ${backendURL}`),\n );\n }\n const builds = versionsResponse.versions.find(\n (value) => value.version === version,\n );\n if (!builds) {\n // should never happen, but just in case\n // throw an error to disable caching\n throw new ExternalHostError(\n new Error(\n `No builds found for ${repository}:${version} on ${registryURL}`,\n ),\n );\n }\n const result = await p.map(\n builds.platforms,\n async (platform) => {\n const buildURL = `${backendURL}/${version}/download/${platform.os}/${platform.arch}`;\n try {\n const res = (\n await this.http.getJson(buildURL, TerraformRegistryBuildResponse)\n ).body;\n const newBuild: TerraformBuild = {\n name: repository,\n url: res.download_url,\n version,\n ...res,\n };\n return newBuild;\n } catch (err) {\n /* v8 ignore next -- hard to test */\n if (err instanceof ExternalHostError) {\n throw err;\n }\n logger.debug({ err, url: buildURL }, 'Failed to retrieve build');\n // throw an error to disable caching\n throw new ExternalHostError(err);\n }\n },\n { concurrency: 4 },\n );\n\n return result;\n }\n\n getBuilds(\n registryURL: string,\n repository: string,\n version: string,\n ): Promise<TerraformBuild[] | null> {\n return this.cached(\n {\n key: buildCacheKey('getBuilds', registryURL, repository, version),\n },\n () => this.fetchBuilds(registryURL, repository, version),\n );\n }\n\n /**\n * A single platform's download endpoint returns the hashes for all platforms,\n * so we query `linux/amd64` and fall back to `/versions` discovery only when a\n * provider lacks that platform (404).\n * See https://github.com/opentofu/opentofu/pull/3434\n */\n private async fetchProviderPackages(\n repository: string,\n version: string,\n ): Promise<string[] | null> {\n const baseUrl = joinUrlParts(\n TerraformProviderDatasource.openTofuRegistryUrl,\n 'v1/providers',\n repository,\n );\n\n try {\n try {\n const { body } = await this.http.getJson(\n `${baseUrl}/${version}/download/linux/amd64`,\n OpenTofuProviderPackagesResponse,\n );\n return body;\n } catch (err) {\n if (!isNotFound(err)) {\n throw err;\n }\n }\n return await this.fetchProviderPackagesForAvailablePlatform(\n baseUrl,\n version,\n );\n } catch (err) {\n if (err instanceof ExternalHostError) {\n throw err;\n }\n logger.debug(\n { err, repository, version },\n `Failed to retrieve provider packages for ${repository}@${version}`,\n );\n throw new ExternalHostError(err);\n }\n }\n\n /**\n * Some providers do not publish a `linux/amd64` build, so discover an\n * available platform via `/versions` and fetch its download endpoint.\n */\n private async fetchProviderPackagesForAvailablePlatform(\n baseUrl: string,\n version: string,\n ): Promise<string[] | null> {\n const { body: versionsResponse } = await this.http.getJson(\n `${baseUrl}/versions`,\n TerraformRegistryVersions,\n );\n const platform = versionsResponse.versions?.find(\n (entry) => entry.version === version,\n )?.platforms?.[0];\n if (!platform) {\n return null;\n }\n const { body: hashes } = await this.http.getJson(\n `${baseUrl}/${version}/download/${platform.os}/${platform.arch}`,\n OpenTofuProviderPackagesResponse,\n );\n return hashes;\n }\n\n getProviderPackages(\n repository: string,\n version: string,\n ): Promise<string[] | null> {\n return this.cached(\n {\n key: buildCacheKey('getProviderPackages', repository, version),\n },\n () => this.fetchProviderPackages(repository, version),\n );\n }\n\n private async fetchZipHashes(\n zipHashUrl: string,\n ): Promise<string[] | undefined> {\n // The hashes are formatted as the result of sha256sum in plain text, each line: <hash>\\t<filename>\n let rawHashData: string;\n try {\n rawHashData = (await this.http.getText(zipHashUrl)).body;\n } catch (err) {\n /* v8 ignore next -- hard to test */\n if (err instanceof ExternalHostError) {\n throw err;\n }\n logger.debug(\n { err, zipHashUrl },\n `Failed to retrieve zip hashes from ${zipHashUrl}`,\n );\n return undefined;\n }\n\n return rawHashData\n .trimEnd()\n .split('\\n')\n .map((line) => line.split(regEx(/\\s/))[0]);\n }\n\n getZipHashes(zipHashUrl: string): Promise<string[] | undefined> {\n return this.cached(\n {\n key: buildCacheKey('getZipHashes', zipHashUrl),\n },\n () => this.fetchZipHashes(zipHashUrl),\n );\n }\n\n private async fetchReleaseBackendIndex(\n backendLookUpName: string,\n version: string,\n ): Promise<VersionDetailResponse> {\n return (\n await this.http.getJson(\n `${TerraformProviderDatasource.hashicorpReleaseUrl}/${backendLookUpName}/${version}/index.json`,\n VersionDetailResponse,\n )\n ).body;\n }\n\n getReleaseBackendIndex(\n backendLookUpName: string,\n version: string,\n ): Promise<VersionDetailResponse> {\n return this.cached(\n {\n key: buildCacheKey(\n 'getReleaseBackendIndex',\n backendLookUpName,\n version,\n ),\n },\n () => this.fetchReleaseBackendIndex(backendLookUpName, version),\n );\n }\n}\n"],"mappings":"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"}
|
|
@@ -79,7 +79,6 @@ var Ctx = class {
|
|
|
79
79
|
parent.items.push(current);
|
|
80
80
|
return true;
|
|
81
81
|
}
|
|
82
|
-
// v8 ignore else -- TODO: add test #40625
|
|
83
82
|
if ((parent.type === "rule" || parent.type === "extensionTag" || parent.type === "repoRuleCall") && current.type === "attribute" && current.value !== void 0) {
|
|
84
83
|
parent.children[current.name] = current.value;
|
|
85
84
|
return true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.js","names":["fragments.isValue","fragments.isPrimitive","fragments.string","fragments.boolean","rule","fragments.rule","fragments.preparedExtensionTag","extensionTag","fragments.extensionTag","useRepoRule","fragments.useRepoRule","repoRuleCall","fragments.repoRuleCall","fragments.attribute","fragments.array"],"sources":["../../../../../lib/modules/manager/bazel-module/parser/context.ts"],"sourcesContent":["import * as fragments from './fragments.ts';\nimport type {\n AllFragments,\n ArrayFragment,\n CtxCompatible,\n ExtensionTagFragment,\n PreparedExtensionTagFragment,\n RepoRuleCallFragment,\n ResultFragment,\n RuleFragment,\n UseRepoRuleFragment,\n} from './types.ts';\n\nexport class CtxProcessingError extends Error {\n readonly current: AllFragments;\n readonly parent?: AllFragments;\n constructor(current: AllFragments, parent?: AllFragments) {\n const msg = `Invalid context state. current: ${current.type}, parent: ${\n parent?.type ?? 'none'\n }`;\n super(msg);\n this.name = 'CtxProcessingError';\n this.current = current;\n this.parent = parent;\n }\n}\n\nexport class Ctx implements CtxCompatible {\n readonly source: string;\n results: ResultFragment[];\n stack: AllFragments[];\n\n // Temporary state for use_repo_rule parsing\n _tempVariableName?: string;\n _tempStrings?: string[];\n\n constructor(source: string) {\n this.source = source;\n this.results = [];\n this.stack = [];\n }\n\n private get safeCurrent(): AllFragments | undefined {\n return this.stack.at(-1);\n }\n\n private get current(): AllFragments {\n const c = this.safeCurrent;\n if (c === undefined) {\n throw new Error('Requested current, but no value.');\n }\n return c;\n }\n\n private get currentRule(): RuleFragment {\n const current = this.current;\n if (current.type === 'rule') {\n return current;\n }\n throw new Error('Requested current rule, but does not exist.');\n }\n\n private get currentExtensionTag(): ExtensionTagFragment {\n const current = this.current;\n if (current.type === 'extensionTag') {\n return current;\n }\n throw new Error('Requested current extension tag, but does not exist.');\n }\n\n private get currentUseRepoRule(): UseRepoRuleFragment {\n const current = this.current;\n if (current.type === 'useRepoRule') {\n return current;\n }\n throw new Error('Requested current use repo rule, but does not exist.');\n }\n\n private get currentRepoRuleCall(): RepoRuleCallFragment {\n const current = this.current;\n if (current.type === 'repoRuleCall') {\n return current;\n }\n throw new Error('Requested current repo rule call, but does not exist.');\n }\n\n private get currentArray(): ArrayFragment {\n const current = this.current;\n if (current.type === 'array') {\n return current;\n }\n throw new Error('Requested current array, but does not exist.');\n }\n\n private popPreparedExtensionTag(): PreparedExtensionTagFragment {\n const c = this.stack.pop();\n if (c === undefined) {\n throw new Error('Requested current, but no value.');\n }\n if (c.type === 'preparedExtensionTag') {\n return c;\n }\n throw new Error(\n 'Requested current prepared extension tag, but does not exist.',\n );\n }\n\n private popStack(): boolean {\n const current = this.stack.pop();\n if (!current) {\n return false;\n }\n if (!current.isComplete) {\n this.stack.push(current);\n return false;\n }\n const parent = this.safeCurrent;\n\n if (parent) {\n if (parent.type === 'attribute' && fragments.isValue(current)) {\n parent.value = current;\n parent.isComplete = true;\n return true;\n }\n if (parent.type === 'array' && fragments.isPrimitive(current)) {\n parent.items.push(current);\n return true;\n }\n // v8 ignore else -- TODO: add test #40625\n if (\n (parent.type === 'rule' ||\n parent.type === 'extensionTag' ||\n parent.type === 'repoRuleCall') &&\n current.type === 'attribute' &&\n current.value !== undefined\n ) {\n parent.children[current.name] = current.value;\n return true;\n }\n } else if (\n current.type === 'rule' ||\n current.type === 'extensionTag' ||\n current.type === 'useRepoRule' ||\n current.type === 'repoRuleCall'\n ) {\n this.results.push(current);\n return true;\n }\n\n throw new CtxProcessingError(current, parent);\n }\n\n private processStack(): Ctx {\n while (this.popStack()) {\n // Nothing to do\n }\n return this;\n }\n\n addString(value: string): Ctx {\n this.stack.push(fragments.string(value));\n return this.processStack();\n }\n\n addBoolean(value: string | boolean): Ctx {\n this.stack.push(fragments.boolean(value));\n return this.processStack();\n }\n\n startRule(name: string): Ctx {\n const rule = fragments.rule(name);\n this.stack.push(rule);\n return this;\n }\n\n endRule(): Ctx {\n const rule = this.currentRule;\n rule.isComplete = true;\n return this.processStack();\n }\n\n prepareExtensionTag(\n extension: string,\n rawExtension: string,\n offset: number,\n ): Ctx {\n const preppedTag = fragments.preparedExtensionTag(\n extension,\n rawExtension,\n offset,\n );\n this.stack.push(preppedTag);\n return this;\n }\n\n startExtensionTag(tag: string): Ctx {\n const { extension, rawExtension, offset } = this.popPreparedExtensionTag();\n\n const extensionTag = fragments.extensionTag(\n extension,\n rawExtension,\n tag,\n offset,\n );\n this.stack.push(extensionTag);\n return this;\n }\n\n endExtensionTag(offset: number): Ctx {\n const tag = this.currentExtensionTag;\n tag.isComplete = true;\n tag.rawString = this.source.slice(tag.offset, offset);\n return this.processStack();\n }\n\n startUseRepoRule(\n variableName: string,\n bzlFile: string,\n ruleName: string,\n ): Ctx {\n const useRepoRule = fragments.useRepoRule(variableName, bzlFile, ruleName);\n this.stack.push(useRepoRule);\n return this;\n }\n\n endUseRepoRule(): Ctx {\n const useRepoRule = this.currentUseRepoRule;\n useRepoRule.isComplete = true;\n return this.processStack();\n }\n\n startRepoRuleCall(functionName: string, offset: number): Ctx {\n const repoRuleCall = fragments.repoRuleCall(functionName, offset);\n this.stack.push(repoRuleCall);\n return this;\n }\n\n endRepoRuleCall(offset: number): Ctx {\n const repoRuleCall = this.currentRepoRuleCall;\n repoRuleCall.isComplete = true;\n repoRuleCall.rawString = this.source.slice(repoRuleCall.offset, offset);\n return this.processStack();\n }\n\n startAttribute(name: string): Ctx {\n this.stack.push(fragments.attribute(name));\n return this.processStack();\n }\n\n startArray(): Ctx {\n this.stack.push(fragments.array());\n return this.processStack();\n }\n\n endArray(): Ctx {\n const array = this.currentArray;\n array.isComplete = true;\n return this.processStack();\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"context.js","names":["fragments.isValue","fragments.isPrimitive","fragments.string","fragments.boolean","rule","fragments.rule","fragments.preparedExtensionTag","extensionTag","fragments.extensionTag","useRepoRule","fragments.useRepoRule","repoRuleCall","fragments.repoRuleCall","fragments.attribute","fragments.array"],"sources":["../../../../../lib/modules/manager/bazel-module/parser/context.ts"],"sourcesContent":["import * as fragments from './fragments.ts';\nimport type {\n AllFragments,\n ArrayFragment,\n CtxCompatible,\n ExtensionTagFragment,\n PreparedExtensionTagFragment,\n RepoRuleCallFragment,\n ResultFragment,\n RuleFragment,\n UseRepoRuleFragment,\n} from './types.ts';\n\nexport class CtxProcessingError extends Error {\n readonly current: AllFragments;\n readonly parent?: AllFragments;\n constructor(current: AllFragments, parent?: AllFragments) {\n const msg = `Invalid context state. current: ${current.type}, parent: ${\n parent?.type ?? 'none'\n }`;\n super(msg);\n this.name = 'CtxProcessingError';\n this.current = current;\n this.parent = parent;\n }\n}\n\nexport class Ctx implements CtxCompatible {\n readonly source: string;\n results: ResultFragment[];\n stack: AllFragments[];\n\n // Temporary state for use_repo_rule parsing\n _tempVariableName?: string;\n _tempStrings?: string[];\n\n constructor(source: string) {\n this.source = source;\n this.results = [];\n this.stack = [];\n }\n\n private get safeCurrent(): AllFragments | undefined {\n return this.stack.at(-1);\n }\n\n private get current(): AllFragments {\n const c = this.safeCurrent;\n if (c === undefined) {\n throw new Error('Requested current, but no value.');\n }\n return c;\n }\n\n private get currentRule(): RuleFragment {\n const current = this.current;\n if (current.type === 'rule') {\n return current;\n }\n throw new Error('Requested current rule, but does not exist.');\n }\n\n private get currentExtensionTag(): ExtensionTagFragment {\n const current = this.current;\n if (current.type === 'extensionTag') {\n return current;\n }\n throw new Error('Requested current extension tag, but does not exist.');\n }\n\n private get currentUseRepoRule(): UseRepoRuleFragment {\n const current = this.current;\n if (current.type === 'useRepoRule') {\n return current;\n }\n throw new Error('Requested current use repo rule, but does not exist.');\n }\n\n private get currentRepoRuleCall(): RepoRuleCallFragment {\n const current = this.current;\n if (current.type === 'repoRuleCall') {\n return current;\n }\n throw new Error('Requested current repo rule call, but does not exist.');\n }\n\n private get currentArray(): ArrayFragment {\n const current = this.current;\n if (current.type === 'array') {\n return current;\n }\n throw new Error('Requested current array, but does not exist.');\n }\n\n private popPreparedExtensionTag(): PreparedExtensionTagFragment {\n const c = this.stack.pop();\n if (c === undefined) {\n throw new Error('Requested current, but no value.');\n }\n if (c.type === 'preparedExtensionTag') {\n return c;\n }\n throw new Error(\n 'Requested current prepared extension tag, but does not exist.',\n );\n }\n\n private popStack(): boolean {\n const current = this.stack.pop();\n if (!current) {\n return false;\n }\n if (!current.isComplete) {\n this.stack.push(current);\n return false;\n }\n const parent = this.safeCurrent;\n\n if (parent) {\n if (parent.type === 'attribute' && fragments.isValue(current)) {\n parent.value = current;\n parent.isComplete = true;\n return true;\n }\n if (parent.type === 'array' && fragments.isPrimitive(current)) {\n parent.items.push(current);\n return true;\n }\n // v8 ignore else -- TODO: add test #40625\n const parentHasAttributes =\n parent.type === 'rule' ||\n parent.type === 'extensionTag' ||\n parent.type === 'repoRuleCall';\n if (\n parentHasAttributes &&\n current.type === 'attribute' &&\n current.value !== undefined\n ) {\n parent.children[current.name] = current.value;\n return true;\n }\n } else if (\n current.type === 'rule' ||\n current.type === 'extensionTag' ||\n current.type === 'useRepoRule' ||\n current.type === 'repoRuleCall'\n ) {\n this.results.push(current);\n return true;\n }\n\n throw new CtxProcessingError(current, parent);\n }\n\n private processStack(): Ctx {\n while (this.popStack()) {\n // Nothing to do\n }\n return this;\n }\n\n addString(value: string): Ctx {\n this.stack.push(fragments.string(value));\n return this.processStack();\n }\n\n addBoolean(value: string | boolean): Ctx {\n this.stack.push(fragments.boolean(value));\n return this.processStack();\n }\n\n startRule(name: string): Ctx {\n const rule = fragments.rule(name);\n this.stack.push(rule);\n return this;\n }\n\n endRule(): Ctx {\n const rule = this.currentRule;\n rule.isComplete = true;\n return this.processStack();\n }\n\n prepareExtensionTag(\n extension: string,\n rawExtension: string,\n offset: number,\n ): Ctx {\n const preppedTag = fragments.preparedExtensionTag(\n extension,\n rawExtension,\n offset,\n );\n this.stack.push(preppedTag);\n return this;\n }\n\n startExtensionTag(tag: string): Ctx {\n const { extension, rawExtension, offset } = this.popPreparedExtensionTag();\n\n const extensionTag = fragments.extensionTag(\n extension,\n rawExtension,\n tag,\n offset,\n );\n this.stack.push(extensionTag);\n return this;\n }\n\n endExtensionTag(offset: number): Ctx {\n const tag = this.currentExtensionTag;\n tag.isComplete = true;\n tag.rawString = this.source.slice(tag.offset, offset);\n return this.processStack();\n }\n\n startUseRepoRule(\n variableName: string,\n bzlFile: string,\n ruleName: string,\n ): Ctx {\n const useRepoRule = fragments.useRepoRule(variableName, bzlFile, ruleName);\n this.stack.push(useRepoRule);\n return this;\n }\n\n endUseRepoRule(): Ctx {\n const useRepoRule = this.currentUseRepoRule;\n useRepoRule.isComplete = true;\n return this.processStack();\n }\n\n startRepoRuleCall(functionName: string, offset: number): Ctx {\n const repoRuleCall = fragments.repoRuleCall(functionName, offset);\n this.stack.push(repoRuleCall);\n return this;\n }\n\n endRepoRuleCall(offset: number): Ctx {\n const repoRuleCall = this.currentRepoRuleCall;\n repoRuleCall.isComplete = true;\n repoRuleCall.rawString = this.source.slice(repoRuleCall.offset, offset);\n return this.processStack();\n }\n\n startAttribute(name: string): Ctx {\n this.stack.push(fragments.attribute(name));\n return this.processStack();\n }\n\n startArray(): Ctx {\n this.stack.push(fragments.array());\n return this.processStack();\n }\n\n endArray(): Ctx {\n const array = this.currentArray;\n array.isComplete = true;\n return this.processStack();\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { findAll } from "../../../util/host-rules.js";
|
|
2
2
|
// lib/modules/manager/bundler/host-rules.ts
|
|
3
|
+
function hasCredentials(rule) {
|
|
4
|
+
return !!rule.username && !!rule.password || !!rule.token;
|
|
5
|
+
}
|
|
3
6
|
function isAuthenticatable(rule) {
|
|
4
|
-
return !!rule.resolvedHost && (
|
|
7
|
+
return !!rule.resolvedHost && hasCredentials(rule);
|
|
5
8
|
}
|
|
6
9
|
function findAllAuthenticatable({ hostType }) {
|
|
7
10
|
return findAll({ hostType }).filter(isAuthenticatable);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host-rules.js","names":[],"sources":["../../../../lib/modules/manager/bundler/host-rules.ts"],"sourcesContent":["import type { HostRule } from '../../../types/index.ts';\nimport { findAll } from '../../../util/host-rules.ts';\n\nfunction
|
|
1
|
+
{"version":3,"file":"host-rules.js","names":[],"sources":["../../../../lib/modules/manager/bundler/host-rules.ts"],"sourcesContent":["import type { HostRule } from '../../../types/index.ts';\nimport { findAll } from '../../../util/host-rules.ts';\n\nfunction hasCredentials(rule: HostRule): boolean {\n const hasBasicAuth = !!rule.username && !!rule.password;\n return hasBasicAuth || !!rule.token;\n}\n\nfunction isAuthenticatable(rule: HostRule): boolean {\n return !!rule.resolvedHost && hasCredentials(rule);\n}\n\nexport function findAllAuthenticatable({\n hostType,\n}: {\n hostType: string;\n}): HostRule[] {\n return findAll({ hostType }).filter(isAuthenticatable);\n}\n\nexport function getAuthenticationHeaderValue(hostRule: HostRule): string {\n if (hostRule.username) {\n const username = encodeURIComponent(hostRule.username);\n // TODO: types (#22198)\n return `${username}:${hostRule.password!}`;\n }\n\n // TODO: types (#22198)\n return `${hostRule.token!}`;\n}\n"],"mappings":";;AAGA,SAAS,eAAe,MAAyB;CAE/C,OADqB,CAAC,CAAC,KAAK,YAAY,CAAC,CAAC,KAAK,YACxB,CAAC,CAAC,KAAK;AAChC;AAEA,SAAS,kBAAkB,MAAyB;CAClD,OAAO,CAAC,CAAC,KAAK,gBAAgB,eAAe,IAAI;AACnD;AAEA,SAAgB,uBAAuB,EACrC,YAGa;CACb,OAAO,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC,OAAO,iBAAiB;AACvD;AAEA,SAAgB,6BAA6B,UAA4B;CACvE,IAAI,SAAS,UAGX,OAAO,GAFU,mBAAmB,SAAS,QAE5B,EAAE,GAAG,SAAS;CAIjC,OAAO,GAAG,SAAS;AACrB"}
|
|
@@ -9,7 +9,7 @@ function getRangeStrategy(config) {
|
|
|
9
9
|
return "widen";
|
|
10
10
|
}
|
|
11
11
|
if (rangeStrategy !== "auto") return rangeStrategy;
|
|
12
|
-
if (isComplexRange || composerJsonType
|
|
12
|
+
if (isComplexRange || composerJsonType === "typo3-cms-extension") return "widen";
|
|
13
13
|
return "update-lockfile";
|
|
14
14
|
}
|
|
15
15
|
export { getRangeStrategy };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"range.js","names":[],"sources":["../../../../lib/modules/manager/composer/range.ts"],"sourcesContent":["import { logger } from '../../../logger/index.ts';\nimport type { RangeStrategy } from '../../../types/index.ts';\nimport type { RangeConfig } from '../types.ts';\nimport type { ComposerManagerData } from './types.ts';\n\nexport function getRangeStrategy(config: RangeConfig): RangeStrategy {\n const { managerData = {}, currentValue, rangeStrategy } = config;\n const { composerJsonType } = managerData as ComposerManagerData;\n const isComplexRange = currentValue?.includes(' || ') ?? false;\n if (rangeStrategy === 'bump' && isComplexRange) {\n logger.debug(\n { currentValue },\n 'Replacing bump strategy for complex range with widen',\n );\n return 'widen';\n }\n if (rangeStrategy !== 'auto') {\n return rangeStrategy!;\n }\n if (
|
|
1
|
+
{"version":3,"file":"range.js","names":[],"sources":["../../../../lib/modules/manager/composer/range.ts"],"sourcesContent":["import { logger } from '../../../logger/index.ts';\nimport type { RangeStrategy } from '../../../types/index.ts';\nimport type { RangeConfig } from '../types.ts';\nimport type { ComposerManagerData } from './types.ts';\n\nexport function getRangeStrategy(config: RangeConfig): RangeStrategy {\n const { managerData = {}, currentValue, rangeStrategy } = config;\n const { composerJsonType } = managerData as ComposerManagerData;\n const isComplexRange = currentValue?.includes(' || ') ?? false;\n if (rangeStrategy === 'bump' && isComplexRange) {\n logger.debug(\n { currentValue },\n 'Replacing bump strategy for complex range with widen',\n );\n return 'widen';\n }\n if (rangeStrategy !== 'auto') {\n return rangeStrategy!;\n }\n if (isComplexRange || composerJsonType === 'typo3-cms-extension') {\n return 'widen';\n }\n return 'update-lockfile';\n}\n"],"mappings":";;AAKA,SAAgB,iBAAiB,QAAoC;CACnE,MAAM,EAAE,cAAc,CAAC,GAAG,cAAc,kBAAkB;CAC1D,MAAM,EAAE,qBAAqB;CAC7B,MAAM,iBAAiB,cAAc,SAAS,MAAM,KAAK;CACzD,IAAI,kBAAkB,UAAU,gBAAgB;EAC9C,OAAO,MACL,EAAE,aAAa,GACf,sDACF;EACA,OAAO;CACT;CACA,IAAI,kBAAkB,QACpB,OAAO;CAET,IAAI,kBAAkB,qBAAqB,uBACzC,OAAO;CAET,OAAO;AACT"}
|
|
@@ -14,7 +14,9 @@ const validMatchFields = [
|
|
|
14
14
|
"indentation"
|
|
15
15
|
];
|
|
16
16
|
function isValidDependency({ depName, currentValue, currentDigest, packageName, datasource }) {
|
|
17
|
-
|
|
17
|
+
const hasName = isNonEmptyStringAndNotWhitespace(depName) || isNonEmptyStringAndNotWhitespace(packageName);
|
|
18
|
+
const hasVersion = isNonEmptyStringAndNotWhitespace(currentDigest) || isNonEmptyStringAndNotWhitespace(currentValue);
|
|
19
|
+
return hasName && hasVersion && isNonEmptyStringAndNotWhitespace(datasource);
|
|
18
20
|
}
|
|
19
21
|
function checkIsValidDependency(dep, packageFile, manager) {
|
|
20
22
|
const isValid = isValidDependency(dep);
|