renovate 44.149.0 → 44.149.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. package/dist/config/options/index.js +1 -1
  2. package/dist/config/options/index.js.map +1 -1
  3. package/dist/config/presets/index.js +2 -1
  4. package/dist/config/presets/index.js.map +1 -1
  5. package/dist/global-config-option-defaults.generated.js +1 -1
  6. package/dist/global-config-option-defaults.generated.js.map +1 -1
  7. package/dist/modules/datasource/artifactory/index.js +2 -1
  8. package/dist/modules/datasource/artifactory/index.js.map +1 -1
  9. package/dist/modules/datasource/aws-eks-addon/index.js +6 -5
  10. package/dist/modules/datasource/aws-eks-addon/index.js.map +1 -1
  11. package/dist/modules/datasource/aws-machine-image/index.js +4 -3
  12. package/dist/modules/datasource/aws-machine-image/index.js.map +1 -1
  13. package/dist/modules/datasource/aws-rds/index.js +2 -1
  14. package/dist/modules/datasource/aws-rds/index.js.map +1 -1
  15. package/dist/modules/datasource/azure-bicep-resource/index.js +2 -1
  16. package/dist/modules/datasource/azure-bicep-resource/index.js.map +1 -1
  17. package/dist/modules/datasource/bazel/index.js +2 -1
  18. package/dist/modules/datasource/bazel/index.js.map +1 -1
  19. package/dist/modules/datasource/bitrise/index.js +2 -1
  20. package/dist/modules/datasource/bitrise/index.js.map +1 -1
  21. package/dist/modules/datasource/buildpacks-registry/index.js +2 -1
  22. package/dist/modules/datasource/buildpacks-registry/index.js.map +1 -1
  23. package/dist/modules/datasource/cdnjs/index.js +3 -2
  24. package/dist/modules/datasource/cdnjs/index.js.map +1 -1
  25. package/dist/modules/datasource/conan/index.js +3 -2
  26. package/dist/modules/datasource/conan/index.js.map +1 -1
  27. package/dist/modules/datasource/conda/index.js +2 -1
  28. package/dist/modules/datasource/conda/index.js.map +1 -1
  29. package/dist/modules/datasource/cpan/index.js +1 -1
  30. package/dist/modules/datasource/cpan/index.js.map +1 -1
  31. package/dist/modules/datasource/crate/index.js +17 -9
  32. package/dist/modules/datasource/crate/index.js.map +1 -1
  33. package/dist/modules/datasource/deb/index.js +3 -2
  34. package/dist/modules/datasource/deb/index.js.map +1 -1
  35. package/dist/modules/datasource/deno/index.js +4 -3
  36. package/dist/modules/datasource/deno/index.js.map +1 -1
  37. package/dist/modules/datasource/docker/index.js +9 -9
  38. package/dist/modules/datasource/docker/index.js.map +1 -1
  39. package/dist/modules/datasource/dotnet-version/index.js +2 -1
  40. package/dist/modules/datasource/dotnet-version/index.js.map +1 -1
  41. package/dist/modules/datasource/elm-package/index.js +2 -1
  42. package/dist/modules/datasource/elm-package/index.js.map +1 -1
  43. package/dist/modules/datasource/endoflife-date/index.js +2 -1
  44. package/dist/modules/datasource/endoflife-date/index.js.map +1 -1
  45. package/dist/modules/datasource/galaxy-collection/index.js +3 -2
  46. package/dist/modules/datasource/galaxy-collection/index.js.map +1 -1
  47. package/dist/modules/datasource/git-host-tags.js +6 -5
  48. package/dist/modules/datasource/git-host-tags.js.map +1 -1
  49. package/dist/modules/datasource/gitea-tags/base.js +5 -4
  50. package/dist/modules/datasource/gitea-tags/base.js.map +1 -1
  51. package/dist/modules/datasource/gitea-tags/util.js +2 -5
  52. package/dist/modules/datasource/gitea-tags/util.js.map +1 -1
  53. package/dist/modules/datasource/github-digest/index.js +3 -5
  54. package/dist/modules/datasource/github-digest/index.js.map +1 -1
  55. package/dist/modules/datasource/github-release-attachments/index.js +3 -2
  56. package/dist/modules/datasource/github-release-attachments/index.js.map +1 -1
  57. package/dist/modules/datasource/gitlab-packages/index.js +2 -1
  58. package/dist/modules/datasource/gitlab-packages/index.js.map +1 -1
  59. package/dist/modules/datasource/gitlab-releases/index.js +2 -1
  60. package/dist/modules/datasource/gitlab-releases/index.js.map +1 -1
  61. package/dist/modules/datasource/gitlab-tags/index.js +3 -2
  62. package/dist/modules/datasource/gitlab-tags/index.js.map +1 -1
  63. package/dist/modules/datasource/glasskube-packages/index.js +2 -1
  64. package/dist/modules/datasource/glasskube-packages/index.js.map +1 -1
  65. package/dist/modules/datasource/go/goproxy-parser.js +3 -2
  66. package/dist/modules/datasource/go/goproxy-parser.js.map +1 -1
  67. package/dist/modules/datasource/go/index.js +4 -3
  68. package/dist/modules/datasource/go/index.js.map +1 -1
  69. package/dist/modules/datasource/go/releases-goproxy.js +6 -3
  70. package/dist/modules/datasource/go/releases-goproxy.js.map +1 -1
  71. package/dist/modules/datasource/go/timestamp-cache.js +3 -1
  72. package/dist/modules/datasource/go/timestamp-cache.js.map +1 -1
  73. package/dist/modules/datasource/golang-version/index.js +1 -1
  74. package/dist/modules/datasource/golang-version/index.js.map +1 -1
  75. package/dist/modules/datasource/gradle-version/index.js +1 -1
  76. package/dist/modules/datasource/gradle-version/index.js.map +1 -1
  77. package/dist/modules/datasource/helm/index.js +2 -1
  78. package/dist/modules/datasource/helm/index.js.map +1 -1
  79. package/dist/modules/datasource/hermit/index.js +3 -2
  80. package/dist/modules/datasource/hermit/index.js.map +1 -1
  81. package/dist/modules/datasource/hex/index.js +3 -2
  82. package/dist/modules/datasource/hex/index.js.map +1 -1
  83. package/dist/modules/datasource/hexpm-bob/index.js +2 -1
  84. package/dist/modules/datasource/hexpm-bob/index.js.map +1 -1
  85. package/dist/modules/datasource/index.js +4 -3
  86. package/dist/modules/datasource/index.js.map +1 -1
  87. package/dist/modules/datasource/java-version/index.js +2 -1
  88. package/dist/modules/datasource/java-version/index.js.map +1 -1
  89. package/dist/modules/datasource/jsr/index.js +2 -1
  90. package/dist/modules/datasource/jsr/index.js.map +1 -1
  91. package/dist/modules/datasource/maven/index.js +2 -1
  92. package/dist/modules/datasource/maven/index.js.map +1 -1
  93. package/dist/modules/datasource/nextcloud/index.js +2 -1
  94. package/dist/modules/datasource/nextcloud/index.js.map +1 -1
  95. package/dist/modules/datasource/node-version/index.js +1 -1
  96. package/dist/modules/datasource/node-version/index.js.map +1 -1
  97. package/dist/modules/datasource/nuget/v3.js +3 -2
  98. package/dist/modules/datasource/nuget/v3.js.map +1 -1
  99. package/dist/modules/datasource/orb/index.js +2 -1
  100. package/dist/modules/datasource/orb/index.js.map +1 -1
  101. package/dist/modules/datasource/packagist/index.js +4 -3
  102. package/dist/modules/datasource/packagist/index.js.map +1 -1
  103. package/dist/modules/datasource/pod/index.js +2 -1
  104. package/dist/modules/datasource/pod/index.js.map +1 -1
  105. package/dist/modules/datasource/python-version/index.js +1 -1
  106. package/dist/modules/datasource/python-version/index.js.map +1 -1
  107. package/dist/modules/datasource/repology/index.js +2 -1
  108. package/dist/modules/datasource/repology/index.js.map +1 -1
  109. package/dist/modules/datasource/rpm/index.js +2 -1
  110. package/dist/modules/datasource/rpm/index.js.map +1 -1
  111. package/dist/modules/datasource/rubygems/index.js +2 -1
  112. package/dist/modules/datasource/rubygems/index.js.map +1 -1
  113. package/dist/modules/datasource/rubygems/metadata-cache.js +2 -1
  114. package/dist/modules/datasource/rubygems/metadata-cache.js.map +1 -1
  115. package/dist/modules/datasource/sbt-package/index.js +11 -10
  116. package/dist/modules/datasource/sbt-package/index.js.map +1 -1
  117. package/dist/modules/datasource/terraform-module/index.js +2 -1
  118. package/dist/modules/datasource/terraform-module/index.js.map +1 -1
  119. package/dist/modules/datasource/terraform-provider/index.js +6 -5
  120. package/dist/modules/datasource/terraform-provider/index.js.map +1 -1
  121. package/dist/modules/datasource/unity3d/index.js +2 -1
  122. package/dist/modules/datasource/unity3d/index.js.map +1 -1
  123. package/dist/modules/datasource/unity3d-packages/index.js +2 -1
  124. package/dist/modules/datasource/unity3d-packages/index.js.map +1 -1
  125. package/dist/modules/manager/bazel/parser.js +3 -1
  126. package/dist/modules/manager/bazel/parser.js.map +1 -1
  127. package/dist/modules/manager/github-actions/extract.js +2 -1
  128. package/dist/modules/manager/github-actions/extract.js.map +1 -1
  129. package/dist/modules/manager/terraform/lockfile/hash.js +2 -1
  130. package/dist/modules/manager/terraform/lockfile/hash.js.map +1 -1
  131. package/dist/modules/platform/bitbucket/pr-cache.js +3 -2
  132. package/dist/modules/platform/bitbucket/pr-cache.js.map +1 -1
  133. package/dist/modules/platform/bitbucket-server/pr-cache.js +3 -2
  134. package/dist/modules/platform/bitbucket-server/pr-cache.js.map +1 -1
  135. package/dist/modules/platform/gitea/pr-cache.js +7 -6
  136. package/dist/modules/platform/gitea/pr-cache.js.map +1 -1
  137. package/dist/modules/platform/gitlab/pr-cache.js +3 -2
  138. package/dist/modules/platform/gitlab/pr-cache.js.map +1 -1
  139. package/dist/util/cache/package/index.js +1 -1
  140. package/dist/util/cache/package/key.js +8 -1
  141. package/dist/util/cache/package/key.js.map +1 -1
  142. package/dist/util/cache/package/with-cache.js +2 -1
  143. package/dist/util/cache/package/with-cache.js.map +1 -1
  144. package/dist/util/exec/containerbase.js +2 -1
  145. package/dist/util/exec/containerbase.js.map +1 -1
  146. package/dist/util/github/graphql/cache-strategies/memory-cache-strategy.js +2 -1
  147. package/dist/util/github/graphql/cache-strategies/memory-cache-strategy.js.map +1 -1
  148. package/dist/util/github/graphql/datasource-fetcher.js +5 -8
  149. package/dist/util/github/graphql/datasource-fetcher.js.map +1 -1
  150. package/dist/util/http/cache/memory-http-cache-provider.js +2 -1
  151. package/dist/util/http/cache/memory-http-cache-provider.js.map +1 -1
  152. package/dist/util/http/cache/package-http-cache-provider.js +2 -1
  153. package/dist/util/http/cache/package-http-cache-provider.js.map +1 -1
  154. package/dist/util/jsonata.js +2 -1
  155. package/dist/util/jsonata.js.map +1 -1
  156. package/dist/util/merge-confidence/index.js +10 -7
  157. package/dist/util/merge-confidence/index.js.map +1 -1
  158. package/dist/workers/repository/update/pr/changelog/release-notes.js +4 -5
  159. package/dist/workers/repository/update/pr/changelog/release-notes.js.map +1 -1
  160. package/dist/workers/repository/update/pr/changelog/source.js +2 -1
  161. package/dist/workers/repository/update/pr/changelog/source.js.map +1 -1
  162. package/package.json +2 -2
  163. package/renovate-schema.json +2 -2
@@ -1,6 +1,7 @@
1
1
  import { regEx } from "../../../util/regex.js";
2
2
  import { logger } from "../../../logger/index.js";
3
3
  import { getQueryString, joinUrlParts } from "../../../util/url.js";
4
+ import { buildCacheKey } from "../../../util/cache/package/key.js";
4
5
  import { ExternalHostError } from "../../../types/errors/external-host-error.js";
5
6
  import { id } from "../../versioning/hashicorp/index.js";
6
7
  import { RequestError } from "../../../util/http/got.js";
@@ -43,7 +44,7 @@ var TerraformProviderDatasource = class TerraformProviderDatasource extends Terr
43
44
  const url = config.registryUrl;
44
45
  const repo = TerraformProviderDatasource.getRepository(config);
45
46
  return this.cached({
46
- key: `getReleases:${url}/${repo}`,
47
+ key: buildCacheKey("getReleases", url, repo),
47
48
  fallback: true
48
49
  }, () => this.fetchReleases(config));
49
50
  }
@@ -147,7 +148,7 @@ var TerraformProviderDatasource = class TerraformProviderDatasource extends Terr
147
148
  }, { concurrency: 4 });
148
149
  }
149
150
  getBuilds(registryURL, repository, version) {
150
- return this.cached({ key: `getBuilds:${registryURL}/${repository}/${version}` }, () => this.fetchBuilds(registryURL, repository, version));
151
+ return this.cached({ key: buildCacheKey("getBuilds", registryURL, repository, version) }, () => this.fetchBuilds(registryURL, repository, version));
151
152
  }
152
153
  /**
153
154
  * A single platform's download endpoint returns the hashes for all platforms,
@@ -187,7 +188,7 @@ var TerraformProviderDatasource = class TerraformProviderDatasource extends Terr
187
188
  return hashes;
188
189
  }
189
190
  getProviderPackages(repository, version) {
190
- return this.cached({ key: `getProviderPackages:${repository}/${version}` }, () => this.fetchProviderPackages(repository, version));
191
+ return this.cached({ key: buildCacheKey("getProviderPackages", repository, version) }, () => this.fetchProviderPackages(repository, version));
191
192
  }
192
193
  async fetchZipHashes(zipHashUrl) {
193
194
  let rawHashData;
@@ -205,13 +206,13 @@ var TerraformProviderDatasource = class TerraformProviderDatasource extends Terr
205
206
  return rawHashData.trimEnd().split("\n").map((line) => line.split(regEx(/\s/))[0]);
206
207
  }
207
208
  getZipHashes(zipHashUrl) {
208
- return this.cached({ key: `getZipHashes:${zipHashUrl}` }, () => this.fetchZipHashes(zipHashUrl));
209
+ return this.cached({ key: buildCacheKey("getZipHashes", zipHashUrl) }, () => this.fetchZipHashes(zipHashUrl));
209
210
  }
210
211
  async fetchReleaseBackendIndex(backendLookUpName, version) {
211
212
  return (await this.http.getJson(`${TerraformProviderDatasource.hashicorpReleaseUrl}/${backendLookUpName}/${version}/index.json`, VersionDetailResponse)).body;
212
213
  }
213
214
  getReleaseBackendIndex(backendLookUpName, version) {
214
- return this.cached({ key: `getReleaseBackendIndex:${backendLookUpName}/${version}` }, () => this.fetchReleaseBackendIndex(backendLookUpName, version));
215
+ return this.cached({ key: buildCacheKey("getReleaseBackendIndex", backendLookUpName, version) }, () => this.fetchReleaseBackendIndex(backendLookUpName, version));
215
216
  }
216
217
  };
217
218
  //#endregion
@@ -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 { 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: `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: `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: `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: `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: `getReleaseBackendIndex:${backendLookUpName}/${version}`,\n },\n () => this.fetchReleaseBackendIndex(backendLookUpName, version),\n );\n }\n}\n"],"mappings":"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"}
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":"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"}
@@ -1,3 +1,4 @@
1
+ import { buildCacheKey } from "../../../util/cache/package/key.js";
1
2
  import { id } from "../../versioning/unity3d/index.js";
2
3
  import { asTimestamp } from "../../../util/timestamp.js";
3
4
  import { Datasource } from "../datasource.js";
@@ -63,7 +64,7 @@ var Unity3dDatasource = class Unity3dDatasource extends Datasource {
63
64
  }
64
65
  getReleases(config) {
65
66
  return this.cached({
66
- key: `${config.registryUrl}:${config.packageName}`,
67
+ key: buildCacheKey(config.registryUrl, config.packageName),
67
68
  fallback: true
68
69
  }, () => this.fetchReleases(config));
69
70
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["Unity3dVersioning.id"],"sources":["../../../../lib/modules/datasource/unity3d/index.ts"],"sourcesContent":["import { asTimestamp } from '../../../util/timestamp.ts';\nimport * as Unity3dVersioning from '../../versioning/unity3d/index.ts';\nimport { Datasource } from '../datasource.ts';\nimport type { GetReleasesConfig, ReleaseResult } from '../types.ts';\nimport { UnityReleasesJSON } from './schema.ts';\n\nexport class Unity3dDatasource extends Datasource {\n static readonly baseUrl =\n 'https://services.api.unity.com/unity/editor/release/v1/releases';\n static readonly homepage = 'https://unity.com/';\n static readonly streams: Record<string, string> = {\n lts: `${Unity3dDatasource.baseUrl}?stream=LTS`,\n tech: `${Unity3dDatasource.baseUrl}?stream=TECH`,\n alpha: `${Unity3dDatasource.baseUrl}?stream=ALPHA`,\n beta: `${Unity3dDatasource.baseUrl}?stream=BETA`,\n };\n static readonly legacyStreams: Record<string, string> = {\n lts: `${Unity3dDatasource.homepage}releases/editor/lts-releases.xml`,\n stable: `${Unity3dDatasource.homepage}releases/editor/releases.xml`,\n beta: `${Unity3dDatasource.homepage}releases/editor/beta/latest.xml`,\n };\n static readonly limit: number = 25;\n\n static readonly id = 'unity3d';\n\n override getDefaultRegistryUrls(_packageName: string): string[] {\n return [Unity3dDatasource.streams.lts];\n }\n\n override readonly defaultVersioning = Unity3dVersioning.id;\n\n override readonly registryStrategy = 'merge';\n\n override readonly releaseTimestampSupport = true;\n override readonly releaseTimestampNote =\n 'The release timestamp is determined from the `releaseDate` field in the results.';\n\n constructor() {\n super(Unity3dDatasource.id);\n }\n\n translateStream(registryUrl: string): string {\n const legacyKey = Object.keys(Unity3dDatasource.legacyStreams).find(\n (key) => Unity3dDatasource.legacyStreams[key] === registryUrl,\n );\n\n if (legacyKey) {\n if (legacyKey === 'stable') {\n return Unity3dDatasource.streams.lts;\n }\n\n return Unity3dDatasource.streams[legacyKey];\n }\n\n return registryUrl;\n }\n\n async getByStream(\n registryUrl: string | undefined,\n withHash: boolean,\n ): Promise<ReleaseResult | null> {\n const translatedRegistryUrl = this.translateStream(registryUrl!);\n\n const isStable: boolean =\n translatedRegistryUrl === Unity3dDatasource.streams.lts;\n\n let total: number | null = null;\n\n const result: ReleaseResult = {\n releases: [],\n homepage: Unity3dDatasource.homepage,\n registryUrl: translatedRegistryUrl,\n };\n\n for (\n let offset = 0;\n total === null || offset < total;\n offset += Unity3dDatasource.limit\n ) {\n const response = await this.http.getJson(\n `${translatedRegistryUrl}&limit=${Unity3dDatasource.limit}&offset=${offset}`,\n UnityReleasesJSON,\n );\n\n for (const release of response.body.results) {\n result.releases.push({\n version: withHash\n ? `${release.version} (${release.shortRevision})`\n : release.version,\n releaseTimestamp: asTimestamp(release.releaseDate),\n changelogUrl: release.releaseNotes.url,\n isStable,\n });\n }\n\n total ??= response.body.total;\n }\n\n return result;\n }\n\n private async fetchReleases({\n packageName,\n registryUrl,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n return await this.getByStream(\n registryUrl,\n packageName === 'm_EditorVersionWithRevision',\n );\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n return this.cached(\n {\n key: `${config.registryUrl}:${config.packageName}`,\n fallback: true,\n },\n () => this.fetchReleases(config),\n );\n }\n}\n"],"mappings":";;;;;AAMA,IAAa,oBAAb,MAAa,0BAA0B,WAAW;CAChD,OAAgB,UACd;CACF,OAAgB,WAAW;CAC3B,OAAgB,UAAkC;EAChD,KAAK,GAAG,kBAAkB,QAAQ;EAClC,MAAM,GAAG,kBAAkB,QAAQ;EACnC,OAAO,GAAG,kBAAkB,QAAQ;EACpC,MAAM,GAAG,kBAAkB,QAAQ;CACrC;CACA,OAAgB,gBAAwC;EACtD,KAAK,GAAG,kBAAkB,SAAS;EACnC,QAAQ,GAAG,kBAAkB,SAAS;EACtC,MAAM,GAAG,kBAAkB,SAAS;CACtC;CACA,OAAgB,QAAgB;CAEhC,OAAgB,KAAK;CAErB,uBAAgC,cAAgC;EAC9D,OAAO,CAAC,kBAAkB,QAAQ,GAAG;CACvC;CAEA,oBAAsCA;CAEtC,mBAAqC;CAErC,0BAA4C;CAC5C,uBACE;CAEF,cAAc;EACZ,MAAM,kBAAkB,EAAE;CAC5B;CAEA,gBAAgB,aAA6B;EAC3C,MAAM,YAAY,OAAO,KAAK,kBAAkB,aAAa,CAAC,CAAC,MAC5D,QAAQ,kBAAkB,cAAc,SAAS,WACpD;EAEA,IAAI,WAAW;GACb,IAAI,cAAc,UAChB,OAAO,kBAAkB,QAAQ;GAGnC,OAAO,kBAAkB,QAAQ;EACnC;EAEA,OAAO;CACT;CAEA,MAAM,YACJ,aACA,UAC+B;EAC/B,MAAM,wBAAwB,KAAK,gBAAgB,WAAY;EAE/D,MAAM,WACJ,0BAA0B,kBAAkB,QAAQ;EAEtD,IAAI,QAAuB;EAE3B,MAAM,SAAwB;GAC5B,UAAU,CAAC;GACX,UAAU,kBAAkB;GAC5B,aAAa;EACf;EAEA,KACE,IAAI,SAAS,GACb,UAAU,QAAQ,SAAS,OAC3B,UAAU,kBAAkB,OAC5B;GACA,MAAM,WAAW,MAAM,KAAK,KAAK,QAC/B,GAAG,sBAAsB,SAAS,kBAAkB,MAAM,UAAU,UACpE,iBACF;GAEA,KAAK,MAAM,WAAW,SAAS,KAAK,SAClC,OAAO,SAAS,KAAK;IACnB,SAAS,WACL,GAAG,QAAQ,QAAQ,IAAI,QAAQ,cAAc,KAC7C,QAAQ;IACZ,kBAAkB,YAAY,QAAQ,WAAW;IACjD,cAAc,QAAQ,aAAa;IACnC;GACF,CAAC;GAGH,UAAU,SAAS,KAAK;EAC1B;EAEA,OAAO;CACT;CAEA,MAAc,cAAc,EAC1B,aACA,eACmD;EACnD,OAAO,MAAM,KAAK,YAChB,aACA,gBAAgB,6BAClB;CACF;CAEA,YAAY,QAA0D;EACpE,OAAO,KAAK,OACV;GACE,KAAK,GAAG,OAAO,YAAY,GAAG,OAAO;GACrC,UAAU;EACZ,SACM,KAAK,cAAc,MAAM,CACjC;CACF;AACF"}
1
+ {"version":3,"file":"index.js","names":["Unity3dVersioning.id"],"sources":["../../../../lib/modules/datasource/unity3d/index.ts"],"sourcesContent":["import { buildCacheKey } from '../../../util/cache/package/key.ts';\nimport { asTimestamp } from '../../../util/timestamp.ts';\nimport * as Unity3dVersioning from '../../versioning/unity3d/index.ts';\nimport { Datasource } from '../datasource.ts';\nimport type { GetReleasesConfig, ReleaseResult } from '../types.ts';\nimport { UnityReleasesJSON } from './schema.ts';\n\nexport class Unity3dDatasource extends Datasource {\n static readonly baseUrl =\n 'https://services.api.unity.com/unity/editor/release/v1/releases';\n static readonly homepage = 'https://unity.com/';\n static readonly streams: Record<string, string> = {\n lts: `${Unity3dDatasource.baseUrl}?stream=LTS`,\n tech: `${Unity3dDatasource.baseUrl}?stream=TECH`,\n alpha: `${Unity3dDatasource.baseUrl}?stream=ALPHA`,\n beta: `${Unity3dDatasource.baseUrl}?stream=BETA`,\n };\n static readonly legacyStreams: Record<string, string> = {\n lts: `${Unity3dDatasource.homepage}releases/editor/lts-releases.xml`,\n stable: `${Unity3dDatasource.homepage}releases/editor/releases.xml`,\n beta: `${Unity3dDatasource.homepage}releases/editor/beta/latest.xml`,\n };\n static readonly limit: number = 25;\n\n static readonly id = 'unity3d';\n\n override getDefaultRegistryUrls(_packageName: string): string[] {\n return [Unity3dDatasource.streams.lts];\n }\n\n override readonly defaultVersioning = Unity3dVersioning.id;\n\n override readonly registryStrategy = 'merge';\n\n override readonly releaseTimestampSupport = true;\n override readonly releaseTimestampNote =\n 'The release timestamp is determined from the `releaseDate` field in the results.';\n\n constructor() {\n super(Unity3dDatasource.id);\n }\n\n translateStream(registryUrl: string): string {\n const legacyKey = Object.keys(Unity3dDatasource.legacyStreams).find(\n (key) => Unity3dDatasource.legacyStreams[key] === registryUrl,\n );\n\n if (legacyKey) {\n if (legacyKey === 'stable') {\n return Unity3dDatasource.streams.lts;\n }\n\n return Unity3dDatasource.streams[legacyKey];\n }\n\n return registryUrl;\n }\n\n async getByStream(\n registryUrl: string | undefined,\n withHash: boolean,\n ): Promise<ReleaseResult | null> {\n const translatedRegistryUrl = this.translateStream(registryUrl!);\n\n const isStable: boolean =\n translatedRegistryUrl === Unity3dDatasource.streams.lts;\n\n let total: number | null = null;\n\n const result: ReleaseResult = {\n releases: [],\n homepage: Unity3dDatasource.homepage,\n registryUrl: translatedRegistryUrl,\n };\n\n for (\n let offset = 0;\n total === null || offset < total;\n offset += Unity3dDatasource.limit\n ) {\n const response = await this.http.getJson(\n `${translatedRegistryUrl}&limit=${Unity3dDatasource.limit}&offset=${offset}`,\n UnityReleasesJSON,\n );\n\n for (const release of response.body.results) {\n result.releases.push({\n version: withHash\n ? `${release.version} (${release.shortRevision})`\n : release.version,\n releaseTimestamp: asTimestamp(release.releaseDate),\n changelogUrl: release.releaseNotes.url,\n isStable,\n });\n }\n\n total ??= response.body.total;\n }\n\n return result;\n }\n\n private async fetchReleases({\n packageName,\n registryUrl,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n return await this.getByStream(\n registryUrl,\n packageName === 'm_EditorVersionWithRevision',\n );\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n return this.cached(\n {\n key: buildCacheKey(config.registryUrl, config.packageName),\n fallback: true,\n },\n () => this.fetchReleases(config),\n );\n }\n}\n"],"mappings":";;;;;;AAOA,IAAa,oBAAb,MAAa,0BAA0B,WAAW;CAChD,OAAgB,UACd;CACF,OAAgB,WAAW;CAC3B,OAAgB,UAAkC;EAChD,KAAK,GAAG,kBAAkB,QAAQ;EAClC,MAAM,GAAG,kBAAkB,QAAQ;EACnC,OAAO,GAAG,kBAAkB,QAAQ;EACpC,MAAM,GAAG,kBAAkB,QAAQ;CACrC;CACA,OAAgB,gBAAwC;EACtD,KAAK,GAAG,kBAAkB,SAAS;EACnC,QAAQ,GAAG,kBAAkB,SAAS;EACtC,MAAM,GAAG,kBAAkB,SAAS;CACtC;CACA,OAAgB,QAAgB;CAEhC,OAAgB,KAAK;CAErB,uBAAgC,cAAgC;EAC9D,OAAO,CAAC,kBAAkB,QAAQ,GAAG;CACvC;CAEA,oBAAsCA;CAEtC,mBAAqC;CAErC,0BAA4C;CAC5C,uBACE;CAEF,cAAc;EACZ,MAAM,kBAAkB,EAAE;CAC5B;CAEA,gBAAgB,aAA6B;EAC3C,MAAM,YAAY,OAAO,KAAK,kBAAkB,aAAa,CAAC,CAAC,MAC5D,QAAQ,kBAAkB,cAAc,SAAS,WACpD;EAEA,IAAI,WAAW;GACb,IAAI,cAAc,UAChB,OAAO,kBAAkB,QAAQ;GAGnC,OAAO,kBAAkB,QAAQ;EACnC;EAEA,OAAO;CACT;CAEA,MAAM,YACJ,aACA,UAC+B;EAC/B,MAAM,wBAAwB,KAAK,gBAAgB,WAAY;EAE/D,MAAM,WACJ,0BAA0B,kBAAkB,QAAQ;EAEtD,IAAI,QAAuB;EAE3B,MAAM,SAAwB;GAC5B,UAAU,CAAC;GACX,UAAU,kBAAkB;GAC5B,aAAa;EACf;EAEA,KACE,IAAI,SAAS,GACb,UAAU,QAAQ,SAAS,OAC3B,UAAU,kBAAkB,OAC5B;GACA,MAAM,WAAW,MAAM,KAAK,KAAK,QAC/B,GAAG,sBAAsB,SAAS,kBAAkB,MAAM,UAAU,UACpE,iBACF;GAEA,KAAK,MAAM,WAAW,SAAS,KAAK,SAClC,OAAO,SAAS,KAAK;IACnB,SAAS,WACL,GAAG,QAAQ,QAAQ,IAAI,QAAQ,cAAc,KAC7C,QAAQ;IACZ,kBAAkB,YAAY,QAAQ,WAAW;IACjD,cAAc,QAAQ,aAAa;IACnC;GACF,CAAC;GAGH,UAAU,SAAS,KAAK;EAC1B;EAEA,OAAO;CACT;CAEA,MAAc,cAAc,EAC1B,aACA,eACmD;EACnD,OAAO,MAAM,KAAK,YAChB,aACA,gBAAgB,6BAClB;CACF;CAEA,YAAY,QAA0D;EACpE,OAAO,KAAK,OACV;GACE,KAAK,cAAc,OAAO,aAAa,OAAO,WAAW;GACzD,UAAU;EACZ,SACM,KAAK,cAAc,MAAM,CACjC;CACF;AACF"}
@@ -1,3 +1,4 @@
1
+ import { buildCacheKey } from "../../../util/cache/package/key.js";
1
2
  import { api, id } from "../../versioning/unity3d-packages/index.js";
2
3
  import { withCache } from "../../../util/cache/package/with-cache.js";
3
4
  import { asTimestamp } from "../../../util/timestamp.js";
@@ -37,7 +38,7 @@ var Unity3dPackagesDatasource = class Unity3dPackagesDatasource extends Datasour
37
38
  getReleases(config) {
38
39
  return withCache({
39
40
  namespace: `datasource-${Unity3dPackagesDatasource.id}`,
40
- key: `${config.registryUrl}:${config.packageName}`,
41
+ key: buildCacheKey(config.registryUrl, config.packageName),
41
42
  fallback: true
42
43
  }, () => this._getReleases(config));
43
44
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["Unity3dPackagesVersioning.id"],"sources":["../../../../lib/modules/datasource/unity3d-packages/index.ts"],"sourcesContent":["import { withCache } from '../../../util/cache/package/with-cache.ts';\nimport { asTimestamp } from '../../../util/timestamp.ts';\nimport * as Unity3dPackagesVersioning from '../../versioning/unity3d-packages/index.ts';\nimport { Datasource } from '../datasource.ts';\nimport type { GetReleasesConfig, ReleaseResult } from '../types.ts';\nimport { UnityPackageReleasesJSON } from './schema.ts';\n\nexport class Unity3dPackagesDatasource extends Datasource {\n static readonly id = 'unity3d-packages';\n\n static readonly defaultRegistryUrl = 'https://packages.unity.com';\n\n override getDefaultRegistryUrls(_packageName: string): string[] {\n return [Unity3dPackagesDatasource.defaultRegistryUrl];\n }\n\n override readonly defaultVersioning = Unity3dPackagesVersioning.id;\n\n constructor() {\n super(Unity3dPackagesDatasource.id);\n }\n\n private async _getReleases({\n packageName,\n registryUrl,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n const response = await this.http.getJson(\n `${registryUrl}/${packageName}`,\n UnityPackageReleasesJSON,\n );\n\n const usingDefaultRegistry =\n registryUrl === Unity3dPackagesDatasource.defaultRegistryUrl;\n const versions = Object.values(response.body.versions);\n\n const result: ReleaseResult = {\n releases: [],\n homepage: versions?.[0]?.documentationUrl,\n registryUrl,\n sourceUrl: versions?.[0]?.repository?.url,\n };\n\n for (const release of versions) {\n result.releases.push({\n version: release.version,\n releaseTimestamp: asTimestamp(response.body.time[release.version]),\n changelogContent: release._upm?.changelog,\n changelogUrl: usingDefaultRegistry\n ? release.documentationUrl?.replace(\n 'manual/index.html',\n 'changelog/CHANGELOG.html',\n )\n : undefined,\n isStable: Unity3dPackagesVersioning.default.isStable(release.version),\n registryUrl,\n });\n }\n\n return result;\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n return withCache(\n {\n namespace: `datasource-${Unity3dPackagesDatasource.id}`,\n key: `${config.registryUrl}:${config.packageName}`,\n fallback: true,\n },\n () => this._getReleases(config),\n );\n }\n}\n"],"mappings":";;;;;;AAOA,IAAa,4BAAb,MAAa,kCAAkC,WAAW;CACxD,OAAgB,KAAK;CAErB,OAAgB,qBAAqB;CAErC,uBAAgC,cAAgC;EAC9D,OAAO,CAAC,0BAA0B,kBAAkB;CACtD;CAEA,oBAAsCA;CAEtC,cAAc;EACZ,MAAM,0BAA0B,EAAE;CACpC;CAEA,MAAc,aAAa,EACzB,aACA,eACmD;EACnD,MAAM,WAAW,MAAM,KAAK,KAAK,QAC/B,GAAG,YAAY,GAAG,eAClB,wBACF;EAEA,MAAM,uBACJ,gBAAgB,0BAA0B;EAC5C,MAAM,WAAW,OAAO,OAAO,SAAS,KAAK,QAAQ;EAErD,MAAM,SAAwB;GAC5B,UAAU,CAAC;GACX,UAAU,WAAW,EAAE,EAAE;GACzB;GACA,WAAW,WAAW,EAAE,EAAE,YAAY;EACxC;EAEA,KAAK,MAAM,WAAW,UACpB,OAAO,SAAS,KAAK;GACnB,SAAS,QAAQ;GACjB,kBAAkB,YAAY,SAAS,KAAK,KAAK,QAAQ,QAAQ;GACjE,kBAAkB,QAAQ,MAAM;GAChC,cAAc,uBACV,QAAQ,kBAAkB,QACxB,qBACA,0BACF,IACA,KAAA;GACJ,UAAA,IAA4C,SAAS,QAAQ,OAAO;GACpE;EACF,CAAC;EAGH,OAAO;CACT;CAEA,YAAY,QAA0D;EACpE,OAAO,UACL;GACE,WAAW,cAAc,0BAA0B;GACnD,KAAK,GAAG,OAAO,YAAY,GAAG,OAAO;GACrC,UAAU;EACZ,SACM,KAAK,aAAa,MAAM,CAChC;CACF;AACF"}
1
+ {"version":3,"file":"index.js","names":["Unity3dPackagesVersioning.id"],"sources":["../../../../lib/modules/datasource/unity3d-packages/index.ts"],"sourcesContent":["import { buildCacheKey } from '../../../util/cache/package/key.ts';\nimport { withCache } from '../../../util/cache/package/with-cache.ts';\nimport { asTimestamp } from '../../../util/timestamp.ts';\nimport * as Unity3dPackagesVersioning from '../../versioning/unity3d-packages/index.ts';\nimport { Datasource } from '../datasource.ts';\nimport type { GetReleasesConfig, ReleaseResult } from '../types.ts';\nimport { UnityPackageReleasesJSON } from './schema.ts';\n\nexport class Unity3dPackagesDatasource extends Datasource {\n static readonly id = 'unity3d-packages';\n\n static readonly defaultRegistryUrl = 'https://packages.unity.com';\n\n override getDefaultRegistryUrls(_packageName: string): string[] {\n return [Unity3dPackagesDatasource.defaultRegistryUrl];\n }\n\n override readonly defaultVersioning = Unity3dPackagesVersioning.id;\n\n constructor() {\n super(Unity3dPackagesDatasource.id);\n }\n\n private async _getReleases({\n packageName,\n registryUrl,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n const response = await this.http.getJson(\n `${registryUrl}/${packageName}`,\n UnityPackageReleasesJSON,\n );\n\n const usingDefaultRegistry =\n registryUrl === Unity3dPackagesDatasource.defaultRegistryUrl;\n const versions = Object.values(response.body.versions);\n\n const result: ReleaseResult = {\n releases: [],\n homepage: versions?.[0]?.documentationUrl,\n registryUrl,\n sourceUrl: versions?.[0]?.repository?.url,\n };\n\n for (const release of versions) {\n result.releases.push({\n version: release.version,\n releaseTimestamp: asTimestamp(response.body.time[release.version]),\n changelogContent: release._upm?.changelog,\n changelogUrl: usingDefaultRegistry\n ? release.documentationUrl?.replace(\n 'manual/index.html',\n 'changelog/CHANGELOG.html',\n )\n : undefined,\n isStable: Unity3dPackagesVersioning.default.isStable(release.version),\n registryUrl,\n });\n }\n\n return result;\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n return withCache(\n {\n namespace: `datasource-${Unity3dPackagesDatasource.id}`,\n key: buildCacheKey(config.registryUrl, config.packageName),\n fallback: true,\n },\n () => this._getReleases(config),\n );\n }\n}\n"],"mappings":";;;;;;;AAQA,IAAa,4BAAb,MAAa,kCAAkC,WAAW;CACxD,OAAgB,KAAK;CAErB,OAAgB,qBAAqB;CAErC,uBAAgC,cAAgC;EAC9D,OAAO,CAAC,0BAA0B,kBAAkB;CACtD;CAEA,oBAAsCA;CAEtC,cAAc;EACZ,MAAM,0BAA0B,EAAE;CACpC;CAEA,MAAc,aAAa,EACzB,aACA,eACmD;EACnD,MAAM,WAAW,MAAM,KAAK,KAAK,QAC/B,GAAG,YAAY,GAAG,eAClB,wBACF;EAEA,MAAM,uBACJ,gBAAgB,0BAA0B;EAC5C,MAAM,WAAW,OAAO,OAAO,SAAS,KAAK,QAAQ;EAErD,MAAM,SAAwB;GAC5B,UAAU,CAAC;GACX,UAAU,WAAW,EAAE,EAAE;GACzB;GACA,WAAW,WAAW,EAAE,EAAE,YAAY;EACxC;EAEA,KAAK,MAAM,WAAW,UACpB,OAAO,SAAS,KAAK;GACnB,SAAS,QAAQ;GACjB,kBAAkB,YAAY,SAAS,KAAK,KAAK,QAAQ,QAAQ;GACjE,kBAAkB,QAAQ,MAAM;GAChC,cAAc,uBACV,QAAQ,kBAAkB,QACxB,qBACA,0BACF,IACA,KAAA;GACJ,UAAA,IAA4C,SAAS,QAAQ,OAAO;GACpE;EACF,CAAC;EAGH,OAAO;CACT;CAEA,YAAY,QAA0D;EACpE,OAAO,UACL;GACE,WAAW,cAAc,0BAA0B;GACnD,KAAK,cAAc,OAAO,aAAa,OAAO,WAAW;GACzD,UAAU;EACZ,SACM,KAAK,aAAa,MAAM,CAChC;CACF;AACF"}
@@ -1,5 +1,6 @@
1
1
  import { get, set } from "../../../util/cache/memory/index.js";
2
2
  import { logger } from "../../../logger/index.js";
3
+ import { buildCacheKey } from "../../../util/cache/package/key.js";
3
4
  import { hash } from "../../../util/hash.js";
4
5
  import { supportedRulesRegex } from "./rules/index.js";
5
6
  import { lang, query } from "@renovatebot/good-enough-parser";
@@ -221,7 +222,8 @@ const query$1 = query.tree({
221
222
  search: rule
222
223
  });
223
224
  function getCacheKey(input) {
224
- return `bazel-parser-${hash(input)}`;
225
+ const hashedInput = hash(input);
226
+ return buildCacheKey("bazel-parser", hashedInput);
225
227
  }
226
228
  const starlark = lang.createLang("starlark");
227
229
  function parse(input, packageFile) {
@@ -1 +1 @@
1
- {"version":3,"file":"parser.js","names":["q","query","memCache.get"],"sources":["../../../../lib/modules/manager/bazel/parser.ts"],"sourcesContent":["import type { lexer, parser } from '@renovatebot/good-enough-parser';\nimport { lang, query as q } from '@renovatebot/good-enough-parser';\nimport { logger } from '../../../logger/index.ts';\nimport * as memCache from '../../../util/cache/memory/index.ts';\nimport { hash } from '../../../util/hash.ts';\nimport { supportedRulesRegex } from './rules/index.ts';\nimport type { NestedFragment, RecordFragment } from './types.ts';\n\ninterface Ctx {\n readonly source: string;\n results: RecordFragment[];\n stack: NestedFragment[];\n recordKey?: string;\n subRecordKey?: string;\n argIndex?: number;\n}\n\nfunction emptyCtx(source: string): Ctx {\n return {\n source,\n results: [],\n stack: [],\n };\n}\n\nfunction currentFragment(ctx: Ctx): NestedFragment {\n const deepestFragment = ctx.stack.at(-1)!;\n return deepestFragment;\n}\n\nfunction extractTreeValue(\n source: string,\n tree: parser.Tree,\n offset: number,\n): string {\n // v8 ignore else -- type narrowing only\n if (tree.type === 'wrapped-tree') {\n const { endsWith } = tree;\n const to = endsWith.offset + endsWith.value.length;\n return source.slice(offset, to);\n }\n\n // istanbul ignore next\n return '';\n}\n\n/**\n * Matches key-value pairs:\n * - `tag = \"1.2.3\"`\n * - `name = \"foobar\"`\n * - `deps = [\"foo\", \"bar\"]`\n * - `\n * artifacts = [\n maven.artifact(\n group = \"com.example1\",\n artifact = \"foobar\",\n version = \"1.2.3\",\n )\n ]\n `\n **/\nconst kwParams = q\n .sym<Ctx>((ctx, { value: recordKey }) => {\n return { ...ctx, recordKey };\n })\n .op('=')\n .alt(\n // string\n q.str((ctx, { offset, value }) => {\n const frag = currentFragment(ctx);\n // v8 ignore else -- type narrowing only\n if (frag.type === 'record' && ctx.recordKey) {\n const key = ctx.recordKey;\n frag.children[key] = { type: 'string', value, offset };\n }\n return ctx;\n }),\n // array of strings or calls\n q.tree({\n type: 'wrapped-tree',\n maxDepth: 1,\n startsWith: '[',\n endsWith: ']',\n preHandler: (ctx, tree) => {\n const parentRecord = currentFragment(ctx) as RecordFragment;\n // v8 ignore else -- type narrowing only\n if (\n parentRecord.type === 'record' &&\n ctx.recordKey &&\n tree.type === 'wrapped-tree'\n ) {\n const key = ctx.recordKey;\n parentRecord.children[key] = {\n type: 'array',\n value: '',\n offset: tree.startsWith.offset,\n children: [],\n };\n }\n return ctx;\n },\n search: q.alt(\n q.str<Ctx>((ctx, { value, offset }) => {\n const parentRecord = currentFragment(ctx);\n // v8 ignore else -- type narrowing only\n if (parentRecord.type === 'record' && ctx.recordKey) {\n const key = ctx.recordKey;\n const array = parentRecord.children[key];\n // v8 ignore else -- type narrowing only\n if (array.type === 'array') {\n array.children.push({ type: 'string', value, offset });\n }\n }\n return ctx;\n }),\n q\n .sym<Ctx>()\n .handler(recordStartHandler)\n .handler((ctx, { value, offset }) => {\n const ruleFragment = currentFragment(ctx);\n // v8 ignore else -- type narrowing only\n if (ruleFragment.type === 'record') {\n ruleFragment.children._function = {\n type: 'string',\n value,\n offset,\n };\n }\n return ctx;\n })\n .many(\n q.op<Ctx>('.').sym((ctx, { value }) => {\n const ruleFragment = currentFragment(ctx);\n // v8 ignore else -- type narrowing only\n if (\n ruleFragment.type === 'record' &&\n ruleFragment.children._function\n ) {\n ruleFragment.children._function.value += `.${value}`;\n }\n return ctx;\n }),\n 0,\n 3,\n )\n .tree({\n type: 'wrapped-tree',\n maxDepth: 1,\n startsWith: '(',\n endsWith: ')',\n search: q\n .opt(\n q\n .sym<Ctx>((ctx, { value: subRecordKey }) => ({\n ...ctx,\n subRecordKey,\n }))\n .op('='),\n )\n .str((ctx, { value: subRecordValue, offset }) => {\n const argIndex = ctx.argIndex ?? 0;\n\n const subRecordKey = ctx.subRecordKey! ?? argIndex.toString();\n const ruleFragment = currentFragment(ctx);\n // v8 ignore else -- type narrowing only\n if (ruleFragment.type === 'record') {\n ruleFragment.children[subRecordKey] = {\n type: 'string',\n value: subRecordValue,\n offset,\n };\n }\n delete ctx.subRecordKey;\n ctx.argIndex = argIndex + 1;\n return ctx;\n }),\n postHandler: (ctx, tree) => {\n delete ctx.argIndex;\n\n const callFrag = currentFragment(ctx);\n ctx.stack.pop();\n // v8 ignore else -- type narrowing only\n if (callFrag.type === 'record' && tree.type === 'wrapped-tree') {\n callFrag.value = extractTreeValue(\n ctx.source,\n tree,\n callFrag.offset,\n );\n\n const parentRecord = currentFragment(ctx);\n // v8 ignore else -- type narrowing only\n if (parentRecord.type === 'record' && ctx.recordKey) {\n const key = ctx.recordKey;\n const array = parentRecord.children[key];\n // v8 ignore else -- type narrowing only\n if (array.type === 'array') {\n array.children.push(callFrag);\n }\n }\n }\n return ctx;\n },\n }),\n ),\n postHandler: (ctx, tree) => {\n const parentRecord = currentFragment(ctx);\n // v8 ignore else -- type narrowing only\n if (\n parentRecord.type === 'record' &&\n ctx.recordKey &&\n tree.type === 'wrapped-tree'\n ) {\n const key = ctx.recordKey;\n const array = parentRecord.children[key];\n // v8 ignore else -- type narrowing only\n if (array.type === 'array') {\n array.value = extractTreeValue(ctx.source, tree, array.offset);\n }\n }\n return ctx;\n },\n }),\n )\n .handler((ctx) => {\n delete ctx.recordKey;\n return ctx;\n });\n\n/**\n * Matches rule signature:\n * `git_repository(......)`\n * ^^^^^^^^\n *\n * @param search something to match inside parens\n */\nfunction ruleCall(\n search: q.QueryBuilder<Ctx, parser.Node>,\n): q.QueryBuilder<Ctx, parser.Node> {\n return q.tree({\n type: 'wrapped-tree',\n maxDepth: 1,\n search,\n postHandler: (ctx, tree) => {\n const frag = currentFragment(ctx);\n // v8 ignore else -- type narrowing only\n if (frag.type === 'record' && tree.type === 'wrapped-tree') {\n frag.value = extractTreeValue(ctx.source, tree, frag.offset);\n ctx.stack.pop();\n ctx.results.push(frag);\n }\n\n return ctx;\n },\n });\n}\n\nfunction recordStartHandler(ctx: Ctx, { offset }: lexer.Token): Ctx {\n ctx.stack.push({\n type: 'record',\n value: '',\n offset,\n children: {},\n });\n return ctx;\n}\n\nfunction ruleNameHandler(ctx: Ctx, { value, offset }: lexer.Token): Ctx {\n const ruleFragment = currentFragment(ctx);\n // v8 ignore else -- type narrowing only\n if (ruleFragment.type === 'record') {\n ruleFragment.children.rule = { type: 'string', value, offset };\n }\n\n return ctx;\n}\n\n/**\n * Matches regular rules:\n * - `git_repository(...)`\n * - `_go_repository(...)`\n */\nconst regularRule = q\n .sym<Ctx>(supportedRulesRegex, (ctx, token) =>\n ruleNameHandler(recordStartHandler(ctx, token), token),\n )\n .join(ruleCall(kwParams));\n\n/**\n * Matches \"maybe\"-form rules:\n * - `maybe(git_repository, ...)`\n * - `maybe(_go_repository, ...)`\n */\nconst maybeRule = q\n .sym<Ctx>('maybe', recordStartHandler)\n .join(\n ruleCall(\n q.alt(\n q.begin<Ctx>().sym(supportedRulesRegex, ruleNameHandler).op(','),\n kwParams,\n ),\n ),\n );\n\nconst rule = q.alt<Ctx>(maybeRule, regularRule);\n\nconst query = q.tree<Ctx>({\n type: 'root-tree',\n maxDepth: 16,\n search: rule,\n});\n\nfunction getCacheKey(input: string): string {\n const hashedInput = hash(input);\n return `bazel-parser-${hashedInput}`;\n}\n\nconst starlark = lang.createLang('starlark');\n\nexport function parse(\n input: string,\n packageFile?: string,\n): RecordFragment[] | null {\n const cacheKey = getCacheKey(input);\n\n const cachedResult = memCache.get<RecordFragment[] | null>(cacheKey);\n // istanbul ignore if\n if (cachedResult === null || cachedResult) {\n return cachedResult;\n }\n\n let result: RecordFragment[] | null = null;\n try {\n const parsedResult = starlark.query(input, query, emptyCtx(input));\n if (parsedResult) {\n result = parsedResult.results;\n }\n } catch (err) /* istanbul ignore next */ {\n logger.debug({ err, packageFile }, 'Bazel parsing error');\n }\n\n memCache.set(cacheKey, result);\n return result;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"parser.js","names":["q","query","memCache.get"],"sources":["../../../../lib/modules/manager/bazel/parser.ts"],"sourcesContent":["import type { lexer, parser } from '@renovatebot/good-enough-parser';\nimport { lang, query as q } from '@renovatebot/good-enough-parser';\nimport { logger } from '../../../logger/index.ts';\nimport * as memCache from '../../../util/cache/memory/index.ts';\nimport { buildCacheKey } from '../../../util/cache/package/key.ts';\nimport { hash } from '../../../util/hash.ts';\nimport { supportedRulesRegex } from './rules/index.ts';\nimport type { NestedFragment, RecordFragment } from './types.ts';\n\ninterface Ctx {\n readonly source: string;\n results: RecordFragment[];\n stack: NestedFragment[];\n recordKey?: string;\n subRecordKey?: string;\n argIndex?: number;\n}\n\nfunction emptyCtx(source: string): Ctx {\n return {\n source,\n results: [],\n stack: [],\n };\n}\n\nfunction currentFragment(ctx: Ctx): NestedFragment {\n const deepestFragment = ctx.stack.at(-1)!;\n return deepestFragment;\n}\n\nfunction extractTreeValue(\n source: string,\n tree: parser.Tree,\n offset: number,\n): string {\n // v8 ignore else -- type narrowing only\n if (tree.type === 'wrapped-tree') {\n const { endsWith } = tree;\n const to = endsWith.offset + endsWith.value.length;\n return source.slice(offset, to);\n }\n\n // istanbul ignore next\n return '';\n}\n\n/**\n * Matches key-value pairs:\n * - `tag = \"1.2.3\"`\n * - `name = \"foobar\"`\n * - `deps = [\"foo\", \"bar\"]`\n * - `\n * artifacts = [\n maven.artifact(\n group = \"com.example1\",\n artifact = \"foobar\",\n version = \"1.2.3\",\n )\n ]\n `\n **/\nconst kwParams = q\n .sym<Ctx>((ctx, { value: recordKey }) => {\n return { ...ctx, recordKey };\n })\n .op('=')\n .alt(\n // string\n q.str((ctx, { offset, value }) => {\n const frag = currentFragment(ctx);\n // v8 ignore else -- type narrowing only\n if (frag.type === 'record' && ctx.recordKey) {\n const key = ctx.recordKey;\n frag.children[key] = { type: 'string', value, offset };\n }\n return ctx;\n }),\n // array of strings or calls\n q.tree({\n type: 'wrapped-tree',\n maxDepth: 1,\n startsWith: '[',\n endsWith: ']',\n preHandler: (ctx, tree) => {\n const parentRecord = currentFragment(ctx) as RecordFragment;\n // v8 ignore else -- type narrowing only\n if (\n parentRecord.type === 'record' &&\n ctx.recordKey &&\n tree.type === 'wrapped-tree'\n ) {\n const key = ctx.recordKey;\n parentRecord.children[key] = {\n type: 'array',\n value: '',\n offset: tree.startsWith.offset,\n children: [],\n };\n }\n return ctx;\n },\n search: q.alt(\n q.str<Ctx>((ctx, { value, offset }) => {\n const parentRecord = currentFragment(ctx);\n // v8 ignore else -- type narrowing only\n if (parentRecord.type === 'record' && ctx.recordKey) {\n const key = ctx.recordKey;\n const array = parentRecord.children[key];\n // v8 ignore else -- type narrowing only\n if (array.type === 'array') {\n array.children.push({ type: 'string', value, offset });\n }\n }\n return ctx;\n }),\n q\n .sym<Ctx>()\n .handler(recordStartHandler)\n .handler((ctx, { value, offset }) => {\n const ruleFragment = currentFragment(ctx);\n // v8 ignore else -- type narrowing only\n if (ruleFragment.type === 'record') {\n ruleFragment.children._function = {\n type: 'string',\n value,\n offset,\n };\n }\n return ctx;\n })\n .many(\n q.op<Ctx>('.').sym((ctx, { value }) => {\n const ruleFragment = currentFragment(ctx);\n // v8 ignore else -- type narrowing only\n if (\n ruleFragment.type === 'record' &&\n ruleFragment.children._function\n ) {\n ruleFragment.children._function.value += `.${value}`;\n }\n return ctx;\n }),\n 0,\n 3,\n )\n .tree({\n type: 'wrapped-tree',\n maxDepth: 1,\n startsWith: '(',\n endsWith: ')',\n search: q\n .opt(\n q\n .sym<Ctx>((ctx, { value: subRecordKey }) => ({\n ...ctx,\n subRecordKey,\n }))\n .op('='),\n )\n .str((ctx, { value: subRecordValue, offset }) => {\n const argIndex = ctx.argIndex ?? 0;\n\n const subRecordKey = ctx.subRecordKey! ?? argIndex.toString();\n const ruleFragment = currentFragment(ctx);\n // v8 ignore else -- type narrowing only\n if (ruleFragment.type === 'record') {\n ruleFragment.children[subRecordKey] = {\n type: 'string',\n value: subRecordValue,\n offset,\n };\n }\n delete ctx.subRecordKey;\n ctx.argIndex = argIndex + 1;\n return ctx;\n }),\n postHandler: (ctx, tree) => {\n delete ctx.argIndex;\n\n const callFrag = currentFragment(ctx);\n ctx.stack.pop();\n // v8 ignore else -- type narrowing only\n if (callFrag.type === 'record' && tree.type === 'wrapped-tree') {\n callFrag.value = extractTreeValue(\n ctx.source,\n tree,\n callFrag.offset,\n );\n\n const parentRecord = currentFragment(ctx);\n // v8 ignore else -- type narrowing only\n if (parentRecord.type === 'record' && ctx.recordKey) {\n const key = ctx.recordKey;\n const array = parentRecord.children[key];\n // v8 ignore else -- type narrowing only\n if (array.type === 'array') {\n array.children.push(callFrag);\n }\n }\n }\n return ctx;\n },\n }),\n ),\n postHandler: (ctx, tree) => {\n const parentRecord = currentFragment(ctx);\n // v8 ignore else -- type narrowing only\n if (\n parentRecord.type === 'record' &&\n ctx.recordKey &&\n tree.type === 'wrapped-tree'\n ) {\n const key = ctx.recordKey;\n const array = parentRecord.children[key];\n // v8 ignore else -- type narrowing only\n if (array.type === 'array') {\n array.value = extractTreeValue(ctx.source, tree, array.offset);\n }\n }\n return ctx;\n },\n }),\n )\n .handler((ctx) => {\n delete ctx.recordKey;\n return ctx;\n });\n\n/**\n * Matches rule signature:\n * `git_repository(......)`\n * ^^^^^^^^\n *\n * @param search something to match inside parens\n */\nfunction ruleCall(\n search: q.QueryBuilder<Ctx, parser.Node>,\n): q.QueryBuilder<Ctx, parser.Node> {\n return q.tree({\n type: 'wrapped-tree',\n maxDepth: 1,\n search,\n postHandler: (ctx, tree) => {\n const frag = currentFragment(ctx);\n // v8 ignore else -- type narrowing only\n if (frag.type === 'record' && tree.type === 'wrapped-tree') {\n frag.value = extractTreeValue(ctx.source, tree, frag.offset);\n ctx.stack.pop();\n ctx.results.push(frag);\n }\n\n return ctx;\n },\n });\n}\n\nfunction recordStartHandler(ctx: Ctx, { offset }: lexer.Token): Ctx {\n ctx.stack.push({\n type: 'record',\n value: '',\n offset,\n children: {},\n });\n return ctx;\n}\n\nfunction ruleNameHandler(ctx: Ctx, { value, offset }: lexer.Token): Ctx {\n const ruleFragment = currentFragment(ctx);\n // v8 ignore else -- type narrowing only\n if (ruleFragment.type === 'record') {\n ruleFragment.children.rule = { type: 'string', value, offset };\n }\n\n return ctx;\n}\n\n/**\n * Matches regular rules:\n * - `git_repository(...)`\n * - `_go_repository(...)`\n */\nconst regularRule = q\n .sym<Ctx>(supportedRulesRegex, (ctx, token) =>\n ruleNameHandler(recordStartHandler(ctx, token), token),\n )\n .join(ruleCall(kwParams));\n\n/**\n * Matches \"maybe\"-form rules:\n * - `maybe(git_repository, ...)`\n * - `maybe(_go_repository, ...)`\n */\nconst maybeRule = q\n .sym<Ctx>('maybe', recordStartHandler)\n .join(\n ruleCall(\n q.alt(\n q.begin<Ctx>().sym(supportedRulesRegex, ruleNameHandler).op(','),\n kwParams,\n ),\n ),\n );\n\nconst rule = q.alt<Ctx>(maybeRule, regularRule);\n\nconst query = q.tree<Ctx>({\n type: 'root-tree',\n maxDepth: 16,\n search: rule,\n});\n\nfunction getCacheKey(input: string): string {\n const hashedInput = hash(input);\n return buildCacheKey('bazel-parser', hashedInput);\n}\n\nconst starlark = lang.createLang('starlark');\n\nexport function parse(\n input: string,\n packageFile?: string,\n): RecordFragment[] | null {\n const cacheKey = getCacheKey(input);\n\n const cachedResult = memCache.get<RecordFragment[] | null>(cacheKey);\n // istanbul ignore if\n if (cachedResult === null || cachedResult) {\n return cachedResult;\n }\n\n let result: RecordFragment[] | null = null;\n try {\n const parsedResult = starlark.query(input, query, emptyCtx(input));\n if (parsedResult) {\n result = parsedResult.results;\n }\n } catch (err) /* istanbul ignore next */ {\n logger.debug({ err, packageFile }, 'Bazel parsing error');\n }\n\n memCache.set(cacheKey, result);\n return result;\n}\n"],"mappings":"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"}
@@ -5,6 +5,7 @@ import { logger, withMeta } from "../../../logger/index.js";
5
5
  import { PLATFORM_FAMILIES } from "../../../constants/platforms.js";
6
6
  import { parseUrl } from "../../../util/url.js";
7
7
  import { detectPlatform } from "../../../util/common.js";
8
+ import { buildCacheKey } from "../../../util/cache/package/key.js";
8
9
  import { readLocalFile } from "../../../util/fs/index.js";
9
10
  import { api } from "../../versioning/docker/index.js";
10
11
  import { id } from "../../versioning/exact/index.js";
@@ -182,7 +183,7 @@ async function readLockfile() {
182
183
  * A repository has a single lock file, but can have any number of package files, so read and parse it only once.
183
184
  */
184
185
  function getLockfile() {
185
- const cacheKey = `github-actions:${actionsLockFile}`;
186
+ const cacheKey = buildCacheKey("github-actions", actionsLockFile);
186
187
  const cached = get(cacheKey);
187
188
  if (cached !== void 0) return cached;
188
189
  const result = readLockfile();
@@ -1 +1 @@
1
- {"version":3,"file":"extract.js","names":["githubActionsVersioning.id","exactVersioning.id","memCache.get"],"sources":["../../../../lib/modules/manager/github-actions/extract.ts"],"sourcesContent":["import is from '@sindresorhus/is';\nimport { GlobalConfig } from '../../../config/global.ts';\nimport { PLATFORM_FAMILIES } from '../../../constants/index.ts';\nimport { logger, withMeta } from '../../../logger/index.ts';\nimport * as memCache from '../../../util/cache/memory/index.ts';\nimport { detectPlatform } from '../../../util/common.ts';\nimport { readLocalFile } from '../../../util/fs/index.ts';\nimport { newlineRegex, regEx } from '../../../util/regex.ts';\nimport { parseUrl } from '../../../util/url.ts';\nimport { GithubDigestDatasource } from '../../datasource/github-digest/index.ts';\nimport { GithubRunnersDatasource } from '../../datasource/github-runners/index.ts';\nimport { GithubTagsDatasource } from '../../datasource/github-tags/index.ts';\nimport * as dockerVersioning from '../../versioning/docker/index.ts';\nimport * as exactVersioning from '../../versioning/exact/index.ts';\nimport * as githubActionsVersioning from '../../versioning/github-actions/index.ts';\nimport { getDep } from '../dockerfile/extract.ts';\nimport type {\n ExtractConfig,\n PackageDependency,\n PackageFileContent,\n} from '../types.ts';\nimport { actionsLockFile, isLockfileManaged } from './common.ts';\nimport { isSha, isShortSha, parseUsesLine, versionLikeRe } from './parse.ts';\nimport type { UsesStep } from './schema.ts';\nimport { ActionsLockfile, CommunityActions, Workflow } from './schema.ts';\nimport type {\n DockerReference,\n LockfileState,\n RepositoryReference,\n} from './types.ts';\n\n// detects if we run against a Github Enterprise Server and adds the URL to the beginning of the registryURLs for looking up Actions\n// This reflects the behavior of how GitHub looks up Actions\n// First on the Enterprise Server, then on GitHub.com\nfunction detectCustomGitHubRegistryUrlsForActions(): PackageDependency {\n const endpoint = GlobalConfig.get('endpoint');\n const registryUrls = ['https://github.com'];\n if (endpoint && GlobalConfig.get('platform') === 'github') {\n const parsedEndpoint = parseUrl(endpoint);\n if (!parsedEndpoint) {\n logger.warn({ endpoint }, 'Failed to parse endpoint url');\n return {};\n }\n\n if (\n parsedEndpoint.host !== 'github.com' &&\n parsedEndpoint.host !== 'api.github.com'\n ) {\n registryUrls.unshift(\n `${parsedEndpoint.protocol}//${parsedEndpoint.host}`,\n );\n return { registryUrls };\n }\n }\n return {};\n}\n\nfunction extractDockerAction(\n actionRef: DockerReference,\n config: ExtractConfig,\n): PackageDependency {\n const dep = getDep(actionRef.originalRef, {\n registryAliases: config.registryAliases,\n depType: 'docker',\n });\n dep.replaceString = actionRef.originalRef;\n return dep;\n}\n\nconst reusableWorkflowPathRe = regEx(/^\\.github\\/workflows\\/[^/]+\\.ya?ml$/);\n\nfunction extractRepositoryAction(\n actionRef: RepositoryReference,\n parsed: ReturnType<typeof parseUsesLine> & object,\n customRegistryUrlsPackageDependency: PackageDependency,\n): PackageDependency {\n const {\n replaceString: valueString,\n quote,\n commentData,\n commentPrecedingWhitespace,\n } = parsed;\n const {\n owner,\n repo,\n path: subPath,\n ref,\n hostname,\n isExplicitHostname,\n } = actionRef;\n\n const registryUrl = isExplicitHostname ? `https://${hostname}/` : '';\n const packageName = `${owner}/${repo}`;\n const depName = `${registryUrl}${packageName}`;\n const pathSuffix = subPath ? `/${subPath}` : '';\n const commentWs = commentPrecedingWhitespace || ' ';\n const isReusableWorkflow = !!subPath && reusableWorkflowPathRe.test(subPath);\n\n const dep: PackageDependency = {\n depName,\n commitMessageTopic: '{{{depName}}} action',\n versioning: githubActionsVersioning.id,\n depType: isReusableWorkflow ? 'workflow' : 'action',\n replaceString: valueString,\n autoReplaceStringTemplate: `${quote}{{depName}}${pathSuffix}@{{#if newDigest}}{{newDigest}}${quote}{{#if newValue}}${commentWs}# {{newValue}}{{/if}}{{/if}}{{#unless newDigest}}{{newValue}}${quote}{{/unless}}`,\n ...(isExplicitHostname\n ? detectDatasource(registryUrl)\n : customRegistryUrlsPackageDependency),\n };\n\n if (packageName !== depName) {\n dep.packageName = packageName;\n }\n\n // Extend replaceString to include relevant comment portions:\n // - Pinned version or ref: include only up to the matched token (truncate trailing text)\n // - Ratchet exclude: include the full comment to preserve the marker\n const pinComment =\n commentData.pinnedVersion ??\n (isSha(ref) || isShortSha(ref) ? commentData.ref : undefined);\n if (\n pinComment &&\n !is.undefined(commentData.index) &&\n !is.undefined(commentData.matchedString)\n ) {\n const cleanComment = parsed.commentString.slice(1);\n const matchEndIndex = commentData.index + commentData.matchedString.length;\n const commentSuffix = cleanComment.slice(0, matchEndIndex);\n dep.replaceString = `${valueString}${commentPrecedingWhitespace}#${commentSuffix}`;\n } else if (commentData.ratchetExclude) {\n dep.replaceString =\n valueString + commentPrecedingWhitespace + parsed.commentString;\n }\n\n if (isSha(ref)) {\n dep.currentValue = commentData.pinnedVersion ?? commentData.ref;\n dep.currentDigest = ref;\n } else if (isShortSha(ref)) {\n dep.currentValue = commentData.pinnedVersion ?? commentData.ref;\n dep.currentDigestShort = ref;\n } else {\n dep.currentValue = ref;\n }\n\n if (!dep.currentValue) {\n dep.enabled = false;\n dep.skipReason = 'unversioned-reference';\n }\n\n const isVersionLike =\n dep.currentValue && versionLikeRe.test(dep.currentValue);\n if (!dep.datasource && dep.currentValue && !isVersionLike) {\n dep.datasource = GithubDigestDatasource.id;\n dep.versioning = exactVersioning.id;\n }\n\n dep.datasource ??= GithubTagsDatasource.id;\n\n return dep;\n}\n\nfunction extractWithRegex(\n content: string,\n config: ExtractConfig,\n): PackageDependency[] {\n const customRegistryUrlsPackageDependency =\n detectCustomGitHubRegistryUrlsForActions();\n logger.trace('github-actions.extractWithRegex()');\n const deps: PackageDependency[] = [];\n\n for (const line of content.split(newlineRegex)) {\n if (line.trim().startsWith('#')) {\n continue;\n }\n\n const parsed = parseUsesLine(line);\n if (!parsed?.actionRef) {\n continue;\n }\n\n const { actionRef } = parsed;\n\n if (actionRef.kind === 'docker') {\n deps.push(extractDockerAction(actionRef, config));\n continue;\n }\n\n // v8 ignore else -- the parsed ref is either a docker or a repository ref\n if (actionRef.kind === 'repository') {\n deps.push(\n extractRepositoryAction(\n actionRef,\n parsed,\n customRegistryUrlsPackageDependency,\n ),\n );\n }\n }\n\n return deps;\n}\n\nfunction detectDatasource(registryUrl: string): PackageDependency {\n const platform = detectPlatform(registryUrl);\n\n switch (platform) {\n case 'forgejo':\n case 'gitea':\n return {\n registryUrls: [registryUrl],\n datasource: PLATFORM_FAMILIES[platform].tagsDatasource,\n };\n case 'github':\n // GitHub is left without a datasource on purpose: `extractRepositoryAction`\n // then picks `github-digest` or `github-tags` from the ref it parsed.\n return { registryUrls: [registryUrl] };\n }\n\n return {\n skipReason: 'unsupported-url',\n };\n}\n\nconst runnerVersionRegex = regEx(\n /^\\s*(?<depName>[a-zA-Z]+)-(?<currentValue>[^\\s]+)/,\n);\n\nfunction extractRunner(runner: string): PackageDependency | null {\n const runnerVersionGroups = runnerVersionRegex.exec(runner)?.groups;\n if (!runnerVersionGroups) {\n return null;\n }\n\n const { depName, currentValue } = runnerVersionGroups;\n\n if (!GithubRunnersDatasource.isValidRunner(depName, currentValue)) {\n return null;\n }\n\n const dependency: PackageDependency = {\n depName,\n currentValue,\n replaceString: `${depName}-${currentValue}`,\n depType: 'github-runner',\n datasource: GithubRunnersDatasource.id,\n autoReplaceStringTemplate: '{{depName}}-{{newValue}}',\n };\n\n if (!dockerVersioning.api.isValid(currentValue)) {\n dependency.skipReason = 'invalid-version';\n }\n\n return dependency;\n}\n\nfunction extractSteps(steps: UsesStep[]): PackageDependency[] {\n const deps: PackageDependency[] = [];\n\n for (const step of steps) {\n const res = CommunityActions.safeParse(step);\n if (res.success) {\n deps.push(...res.data);\n }\n }\n\n return deps;\n}\n\nfunction extractWithYAMLParser(\n content: string,\n packageFile: string,\n config: ExtractConfig,\n): PackageDependency[] {\n logger.trace('github-actions.extractWithYAMLParser()');\n\n const obj = withMeta({ packageFile }, () => Workflow.parse(content));\n if (!obj) {\n return [];\n }\n\n if ('runs' in obj && obj.runs.steps) {\n return extractSteps(obj.runs.steps);\n }\n\n if (!('jobs' in obj)) {\n return [];\n }\n\n const deps: PackageDependency[] = [];\n\n for (const job of Object.values(obj.jobs)) {\n if (job.container) {\n deps.push(\n getDep(job.container, {\n registryAliases: config.registryAliases,\n depType: 'container',\n }),\n );\n }\n\n for (const service of job.services) {\n deps.push(\n getDep(service, {\n registryAliases: config.registryAliases,\n depType: 'service',\n }),\n );\n }\n\n for (const runner of job['runs-on']) {\n const dep = extractRunner(runner);\n if (dep) {\n deps.push(dep);\n }\n }\n\n deps.push(...extractSteps(job.steps));\n }\n\n return deps;\n}\n\nasync function readLockfile(): Promise<LockfileState> {\n const content = await readLocalFile(actionsLockFile, 'utf8');\n if (!content) {\n return { type: 'missing' };\n }\n\n const parsed = ActionsLockfile.safeParse(content);\n if (!parsed.success) {\n // `updateActionsLockfile` warns about this once per branch, so stay quiet\n logger.debug(`Failed to parse ${actionsLockFile}`);\n return { type: 'unparseable' };\n }\n\n return { type: 'parsed', onboardedWorkflows: parsed.data.workflows };\n}\n\n/**\n * A repository has a single lock file, but can have any number of package files, so read and parse it only once.\n */\nfunction getLockfile(): Promise<LockfileState> {\n const cacheKey = `github-actions:${actionsLockFile}`;\n const cached = memCache.get<Promise<LockfileState> | undefined>(cacheKey);\n if (cached !== undefined) {\n return cached;\n }\n\n const result = readLockfile();\n memCache.set(cacheKey, result);\n return result;\n}\n\n/**\n * Whether `gh actions-lock` owns the digests in this file.\n *\n * It rewrites the workflows it manages back to plain refs when regenerating, so an inline digest pin would be stripped straight back out.\n */\nasync function isManagedByLockfile(packageFile: string): Promise<boolean> {\n const lockfile = await getLockfile();\n\n switch (lockfile.type) {\n case 'missing':\n return false;\n case 'unparseable':\n // The tool refuses to regenerate a lock file which it cannot parse, so treat everything as managed: pinning inline here would raise a PR which pins the digests the tool owns and leaves the lock file stale.\n return true;\n case 'parsed':\n return isLockfileManaged(packageFile, lockfile.onboardedWorkflows);\n }\n}\n\nexport async function extractPackageFile(\n content: string,\n packageFile: string,\n config: ExtractConfig = {}, // TODO: enforce ExtractConfig\n): Promise<PackageFileContent | null> {\n logger.trace(`github-actions.extractPackageFile(${packageFile})`);\n\n const deps = [\n ...extractWithRegex(content, config),\n ...extractWithYAMLParser(content, packageFile, config),\n ];\n\n if (!deps.length) {\n return null;\n }\n\n const res: PackageFileContent = { deps };\n\n if (await isManagedByLockfile(packageFile)) {\n // Deliberately no `lockFiles`: nothing here goes through `updateArtifacts`, and `matchFileNames` also tests `lockFiles`, so declaring it would make a negated rule such as `!.github/workflows/release.yml` match every workflow through the lock file path.\n for (const dep of deps) {\n // The lock file only records `OWNER/REPO@REF` pins, so a `docker://` image in a `uses:` is still ours to pin.\n if (dep.depType === 'action' || dep.depType === 'workflow') {\n dep.digestManagedExternally = true;\n }\n }\n }\n\n return res;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"extract.js","names":["githubActionsVersioning.id","exactVersioning.id","memCache.get"],"sources":["../../../../lib/modules/manager/github-actions/extract.ts"],"sourcesContent":["import is from '@sindresorhus/is';\nimport { GlobalConfig } from '../../../config/global.ts';\nimport { PLATFORM_FAMILIES } from '../../../constants/index.ts';\nimport { logger, withMeta } from '../../../logger/index.ts';\nimport * as memCache from '../../../util/cache/memory/index.ts';\nimport { buildCacheKey } from '../../../util/cache/package/key.ts';\nimport { detectPlatform } from '../../../util/common.ts';\nimport { readLocalFile } from '../../../util/fs/index.ts';\nimport { newlineRegex, regEx } from '../../../util/regex.ts';\nimport { parseUrl } from '../../../util/url.ts';\nimport { GithubDigestDatasource } from '../../datasource/github-digest/index.ts';\nimport { GithubRunnersDatasource } from '../../datasource/github-runners/index.ts';\nimport { GithubTagsDatasource } from '../../datasource/github-tags/index.ts';\nimport * as dockerVersioning from '../../versioning/docker/index.ts';\nimport * as exactVersioning from '../../versioning/exact/index.ts';\nimport * as githubActionsVersioning from '../../versioning/github-actions/index.ts';\nimport { getDep } from '../dockerfile/extract.ts';\nimport type {\n ExtractConfig,\n PackageDependency,\n PackageFileContent,\n} from '../types.ts';\nimport { actionsLockFile, isLockfileManaged } from './common.ts';\nimport { isSha, isShortSha, parseUsesLine, versionLikeRe } from './parse.ts';\nimport type { UsesStep } from './schema.ts';\nimport { ActionsLockfile, CommunityActions, Workflow } from './schema.ts';\nimport type {\n DockerReference,\n LockfileState,\n RepositoryReference,\n} from './types.ts';\n\n// detects if we run against a Github Enterprise Server and adds the URL to the beginning of the registryURLs for looking up Actions\n// This reflects the behavior of how GitHub looks up Actions\n// First on the Enterprise Server, then on GitHub.com\nfunction detectCustomGitHubRegistryUrlsForActions(): PackageDependency {\n const endpoint = GlobalConfig.get('endpoint');\n const registryUrls = ['https://github.com'];\n if (endpoint && GlobalConfig.get('platform') === 'github') {\n const parsedEndpoint = parseUrl(endpoint);\n if (!parsedEndpoint) {\n logger.warn({ endpoint }, 'Failed to parse endpoint url');\n return {};\n }\n\n if (\n parsedEndpoint.host !== 'github.com' &&\n parsedEndpoint.host !== 'api.github.com'\n ) {\n registryUrls.unshift(\n `${parsedEndpoint.protocol}//${parsedEndpoint.host}`,\n );\n return { registryUrls };\n }\n }\n return {};\n}\n\nfunction extractDockerAction(\n actionRef: DockerReference,\n config: ExtractConfig,\n): PackageDependency {\n const dep = getDep(actionRef.originalRef, {\n registryAliases: config.registryAliases,\n depType: 'docker',\n });\n dep.replaceString = actionRef.originalRef;\n return dep;\n}\n\nconst reusableWorkflowPathRe = regEx(/^\\.github\\/workflows\\/[^/]+\\.ya?ml$/);\n\nfunction extractRepositoryAction(\n actionRef: RepositoryReference,\n parsed: ReturnType<typeof parseUsesLine> & object,\n customRegistryUrlsPackageDependency: PackageDependency,\n): PackageDependency {\n const {\n replaceString: valueString,\n quote,\n commentData,\n commentPrecedingWhitespace,\n } = parsed;\n const {\n owner,\n repo,\n path: subPath,\n ref,\n hostname,\n isExplicitHostname,\n } = actionRef;\n\n const registryUrl = isExplicitHostname ? `https://${hostname}/` : '';\n const packageName = `${owner}/${repo}`;\n const depName = `${registryUrl}${packageName}`;\n const pathSuffix = subPath ? `/${subPath}` : '';\n const commentWs = commentPrecedingWhitespace || ' ';\n const isReusableWorkflow = !!subPath && reusableWorkflowPathRe.test(subPath);\n\n const dep: PackageDependency = {\n depName,\n commitMessageTopic: '{{{depName}}} action',\n versioning: githubActionsVersioning.id,\n depType: isReusableWorkflow ? 'workflow' : 'action',\n replaceString: valueString,\n autoReplaceStringTemplate: `${quote}{{depName}}${pathSuffix}@{{#if newDigest}}{{newDigest}}${quote}{{#if newValue}}${commentWs}# {{newValue}}{{/if}}{{/if}}{{#unless newDigest}}{{newValue}}${quote}{{/unless}}`,\n ...(isExplicitHostname\n ? detectDatasource(registryUrl)\n : customRegistryUrlsPackageDependency),\n };\n\n if (packageName !== depName) {\n dep.packageName = packageName;\n }\n\n // Extend replaceString to include relevant comment portions:\n // - Pinned version or ref: include only up to the matched token (truncate trailing text)\n // - Ratchet exclude: include the full comment to preserve the marker\n const pinComment =\n commentData.pinnedVersion ??\n (isSha(ref) || isShortSha(ref) ? commentData.ref : undefined);\n if (\n pinComment &&\n !is.undefined(commentData.index) &&\n !is.undefined(commentData.matchedString)\n ) {\n const cleanComment = parsed.commentString.slice(1);\n const matchEndIndex = commentData.index + commentData.matchedString.length;\n const commentSuffix = cleanComment.slice(0, matchEndIndex);\n dep.replaceString = `${valueString}${commentPrecedingWhitespace}#${commentSuffix}`;\n } else if (commentData.ratchetExclude) {\n dep.replaceString =\n valueString + commentPrecedingWhitespace + parsed.commentString;\n }\n\n if (isSha(ref)) {\n dep.currentValue = commentData.pinnedVersion ?? commentData.ref;\n dep.currentDigest = ref;\n } else if (isShortSha(ref)) {\n dep.currentValue = commentData.pinnedVersion ?? commentData.ref;\n dep.currentDigestShort = ref;\n } else {\n dep.currentValue = ref;\n }\n\n if (!dep.currentValue) {\n dep.enabled = false;\n dep.skipReason = 'unversioned-reference';\n }\n\n const isVersionLike =\n dep.currentValue && versionLikeRe.test(dep.currentValue);\n if (!dep.datasource && dep.currentValue && !isVersionLike) {\n dep.datasource = GithubDigestDatasource.id;\n dep.versioning = exactVersioning.id;\n }\n\n dep.datasource ??= GithubTagsDatasource.id;\n\n return dep;\n}\n\nfunction extractWithRegex(\n content: string,\n config: ExtractConfig,\n): PackageDependency[] {\n const customRegistryUrlsPackageDependency =\n detectCustomGitHubRegistryUrlsForActions();\n logger.trace('github-actions.extractWithRegex()');\n const deps: PackageDependency[] = [];\n\n for (const line of content.split(newlineRegex)) {\n if (line.trim().startsWith('#')) {\n continue;\n }\n\n const parsed = parseUsesLine(line);\n if (!parsed?.actionRef) {\n continue;\n }\n\n const { actionRef } = parsed;\n\n if (actionRef.kind === 'docker') {\n deps.push(extractDockerAction(actionRef, config));\n continue;\n }\n\n // v8 ignore else -- the parsed ref is either a docker or a repository ref\n if (actionRef.kind === 'repository') {\n deps.push(\n extractRepositoryAction(\n actionRef,\n parsed,\n customRegistryUrlsPackageDependency,\n ),\n );\n }\n }\n\n return deps;\n}\n\nfunction detectDatasource(registryUrl: string): PackageDependency {\n const platform = detectPlatform(registryUrl);\n\n switch (platform) {\n case 'forgejo':\n case 'gitea':\n return {\n registryUrls: [registryUrl],\n datasource: PLATFORM_FAMILIES[platform].tagsDatasource,\n };\n case 'github':\n // GitHub is left without a datasource on purpose: `extractRepositoryAction`\n // then picks `github-digest` or `github-tags` from the ref it parsed.\n return { registryUrls: [registryUrl] };\n }\n\n return {\n skipReason: 'unsupported-url',\n };\n}\n\nconst runnerVersionRegex = regEx(\n /^\\s*(?<depName>[a-zA-Z]+)-(?<currentValue>[^\\s]+)/,\n);\n\nfunction extractRunner(runner: string): PackageDependency | null {\n const runnerVersionGroups = runnerVersionRegex.exec(runner)?.groups;\n if (!runnerVersionGroups) {\n return null;\n }\n\n const { depName, currentValue } = runnerVersionGroups;\n\n if (!GithubRunnersDatasource.isValidRunner(depName, currentValue)) {\n return null;\n }\n\n const dependency: PackageDependency = {\n depName,\n currentValue,\n replaceString: `${depName}-${currentValue}`,\n depType: 'github-runner',\n datasource: GithubRunnersDatasource.id,\n autoReplaceStringTemplate: '{{depName}}-{{newValue}}',\n };\n\n if (!dockerVersioning.api.isValid(currentValue)) {\n dependency.skipReason = 'invalid-version';\n }\n\n return dependency;\n}\n\nfunction extractSteps(steps: UsesStep[]): PackageDependency[] {\n const deps: PackageDependency[] = [];\n\n for (const step of steps) {\n const res = CommunityActions.safeParse(step);\n if (res.success) {\n deps.push(...res.data);\n }\n }\n\n return deps;\n}\n\nfunction extractWithYAMLParser(\n content: string,\n packageFile: string,\n config: ExtractConfig,\n): PackageDependency[] {\n logger.trace('github-actions.extractWithYAMLParser()');\n\n const obj = withMeta({ packageFile }, () => Workflow.parse(content));\n if (!obj) {\n return [];\n }\n\n if ('runs' in obj && obj.runs.steps) {\n return extractSteps(obj.runs.steps);\n }\n\n if (!('jobs' in obj)) {\n return [];\n }\n\n const deps: PackageDependency[] = [];\n\n for (const job of Object.values(obj.jobs)) {\n if (job.container) {\n deps.push(\n getDep(job.container, {\n registryAliases: config.registryAliases,\n depType: 'container',\n }),\n );\n }\n\n for (const service of job.services) {\n deps.push(\n getDep(service, {\n registryAliases: config.registryAliases,\n depType: 'service',\n }),\n );\n }\n\n for (const runner of job['runs-on']) {\n const dep = extractRunner(runner);\n if (dep) {\n deps.push(dep);\n }\n }\n\n deps.push(...extractSteps(job.steps));\n }\n\n return deps;\n}\n\nasync function readLockfile(): Promise<LockfileState> {\n const content = await readLocalFile(actionsLockFile, 'utf8');\n if (!content) {\n return { type: 'missing' };\n }\n\n const parsed = ActionsLockfile.safeParse(content);\n if (!parsed.success) {\n // `updateActionsLockfile` warns about this once per branch, so stay quiet\n logger.debug(`Failed to parse ${actionsLockFile}`);\n return { type: 'unparseable' };\n }\n\n return { type: 'parsed', onboardedWorkflows: parsed.data.workflows };\n}\n\n/**\n * A repository has a single lock file, but can have any number of package files, so read and parse it only once.\n */\nfunction getLockfile(): Promise<LockfileState> {\n const cacheKey = buildCacheKey('github-actions', actionsLockFile);\n const cached = memCache.get<Promise<LockfileState> | undefined>(cacheKey);\n if (cached !== undefined) {\n return cached;\n }\n\n const result = readLockfile();\n memCache.set(cacheKey, result);\n return result;\n}\n\n/**\n * Whether `gh actions-lock` owns the digests in this file.\n *\n * It rewrites the workflows it manages back to plain refs when regenerating, so an inline digest pin would be stripped straight back out.\n */\nasync function isManagedByLockfile(packageFile: string): Promise<boolean> {\n const lockfile = await getLockfile();\n\n switch (lockfile.type) {\n case 'missing':\n return false;\n case 'unparseable':\n // The tool refuses to regenerate a lock file which it cannot parse, so treat everything as managed: pinning inline here would raise a PR which pins the digests the tool owns and leaves the lock file stale.\n return true;\n case 'parsed':\n return isLockfileManaged(packageFile, lockfile.onboardedWorkflows);\n }\n}\n\nexport async function extractPackageFile(\n content: string,\n packageFile: string,\n config: ExtractConfig = {}, // TODO: enforce ExtractConfig\n): Promise<PackageFileContent | null> {\n logger.trace(`github-actions.extractPackageFile(${packageFile})`);\n\n const deps = [\n ...extractWithRegex(content, config),\n ...extractWithYAMLParser(content, packageFile, config),\n ];\n\n if (!deps.length) {\n return null;\n }\n\n const res: PackageFileContent = { deps };\n\n if (await isManagedByLockfile(packageFile)) {\n // Deliberately no `lockFiles`: nothing here goes through `updateArtifacts`, and `matchFileNames` also tests `lockFiles`, so declaring it would make a negated rule such as `!.github/workflows/release.yml` match every workflow through the lock file path.\n for (const dep of deps) {\n // The lock file only records `OWNER/REPO@REF` pins, so a `docker://` image in a `uses:` is still ours to pin.\n if (dep.depType === 'action' || dep.depType === 'workflow') {\n dep.digestManagedExternally = true;\n }\n }\n }\n\n return res;\n}\n"],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import { coerceArray, deduplicateArray, isNotNullOrUndefined } from "../../../../util/array.js";
2
2
  import { logger } from "../../../../logger/index.js";
3
+ import { buildCacheKey } from "../../../../util/cache/package/key.js";
3
4
  import { hashStream } from "../../../../util/hash.js";
4
5
  import { createCacheWriteStream, ensureCacheDir, pipeline, rmCache } from "../../../../util/fs/index.js";
5
6
  import { withCache } from "../../../../util/cache/package/with-cache.js";
@@ -92,7 +93,7 @@ var TerraformProviderHash = class TerraformProviderHash {
92
93
  static calculateSingleHash(build, cacheDir) {
93
94
  return withCache({
94
95
  namespace: `terraform-provider-hash`,
95
- key: `calculateSingleHash:${build.url}`,
96
+ key: buildCacheKey("calculateSingleHash", build.url),
96
97
  ttlMinutes: TerraformProviderHash.hashCacheTTL
97
98
  }, () => TerraformProviderHash._calculateSingleHash(build, cacheDir));
98
99
  }
@@ -1 +1 @@
1
- {"version":3,"file":"hash.js","names":["fs.createCacheWriteStream","fs.pipeline","fs.rmCache","fs.ensureCacheDir","p.map"],"sources":["../../../../../lib/modules/manager/terraform/lockfile/hash.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport { once } from 'node:events';\nimport { Transform, type TransformCallback } from 'node:stream';\nimport { crc32 } from 'node:zlib';\nimport upath from 'upath';\nimport { openPromise, validateFileName } from 'yauzl';\nimport { logger } from '../../../../logger/index.ts';\nimport {\n coerceArray,\n deduplicateArray,\n isNotNullOrUndefined,\n} from '../../../../util/array.ts';\nimport { withCache } from '../../../../util/cache/package/with-cache.ts';\nimport * as fs from '../../../../util/fs/index.ts';\nimport { hashStream } from '../../../../util/hash.ts';\nimport { Http } from '../../../../util/http/index.ts';\nimport * as p from '../../../../util/promises.ts';\nimport { TerraformProviderDatasource } from '../../../datasource/terraform-provider/index.ts';\nimport type { TerraformBuild } from '../../../datasource/terraform-provider/schema.ts';\n\nclass Crc32Stream extends Transform {\n checksum = 0;\n\n override _transform(\n chunk: Buffer,\n _encoding: BufferEncoding,\n callback: TransformCallback,\n ): void {\n this.checksum = crc32(chunk, this.checksum);\n callback(null, chunk);\n }\n}\n\nexport class TerraformProviderHash {\n static http = new Http(TerraformProviderDatasource.id);\n\n static terraformDatasource = new TerraformProviderDatasource();\n\n static hashCacheTTL = 10080; // in minutes == 1 week\n\n /**\n * Computes the `h1:` checksum used in Terraform dependency lock files.\n *\n * Terraform verifies extracted providers with Go's `HashDir`, which ignores\n * ZIP directory entries. Ignore them here so the archive produces the same\n * checksum as the extracted provider.\n *\n * See https://github.com/golang/go/issues/53448.\n */\n static async hashOfZipContent(zipFilePath: string): Promise<string> {\n const zipFile = await openPromise(zipFilePath, {\n autoClose: false,\n decodeStrings: false,\n });\n const entryHashes: { path: Buffer; hash: string }[] = [];\n const entryPaths = new Set<string>();\n\n try {\n for await (const entry of zipFile.eachEntry()) {\n // Go's ZIP reader preserves the central-directory name bytes instead of decoding CP437 or Unicode path fields.\n const fileName = entry.fileNameRaw\n .toString('latin1')\n .replaceAll('\\\\', '/');\n\n // Terraform verifies extracted packages with Go HashDir semantics, which omit directory records.\n if (fileName.endsWith('/')) {\n continue;\n }\n if (fileName.includes('\\n')) {\n throw new Error('ZIP entry name contains a newline');\n }\n const fileNameError = validateFileName(fileName);\n if (fileNameError) {\n throw new Error(fileNameError);\n }\n const normalizedPath = upath.normalize(fileName);\n if (entryPaths.has(normalizedPath)) {\n throw new Error(`Duplicate ZIP entry path: ${normalizedPath}`);\n }\n entryPaths.add(normalizedPath);\n const path = Buffer.from(normalizedPath, 'latin1');\n\n const entryStream = await zipFile.openReadStreamPromise(entry);\n const crc32Stream = new Crc32Stream();\n const hash = await hashStream(\n entryStream.compose(crc32Stream),\n 'sha256',\n );\n if (crc32Stream.checksum !== entry.crc32) {\n throw new Error(\n `CRC-32 mismatch for ZIP entry ${entry.fileNameRaw.toString('utf8')}`,\n );\n }\n entryHashes.push({ path, hash });\n }\n } finally {\n const closePromise = once(zipFile, 'close');\n zipFile.close();\n await closePromise;\n }\n\n entryHashes.sort((left, right) => Buffer.compare(left.path, right.path));\n\n const rootHash = crypto.createHash('sha256');\n for (const entry of entryHashes) {\n rootHash.update(`${entry.hash} `);\n rootHash.update(entry.path);\n rootHash.update('\\n');\n }\n\n return rootHash.digest('base64');\n }\n\n private static async _calculateSingleHash(\n build: TerraformBuild,\n cacheDir: string,\n ): Promise<string> {\n const downloadFileName = upath.join(cacheDir, crypto.randomUUID());\n logger.trace(\n `Downloading archive and generating hash for ${build.name}-${build.version}...`,\n );\n const startTime = Date.now();\n const readStream = TerraformProviderHash.http.stream(build.url);\n const writeStream = fs.createCacheWriteStream(downloadFileName);\n\n try {\n await fs.pipeline(readStream, writeStream);\n\n const hash = await this.hashOfZipContent(downloadFileName);\n logger.debug(\n `Hash generation for ${build.url} took ${Date.now() - startTime}ms for ${build.name}-${build.version}`,\n );\n return hash;\n } finally {\n await fs.rmCache(downloadFileName);\n }\n }\n\n static calculateSingleHash(\n build: TerraformBuild,\n cacheDir: string,\n ): Promise<string> {\n return withCache(\n {\n namespace: `terraform-provider-hash`,\n key: `calculateSingleHash:${build.url}`,\n ttlMinutes: TerraformProviderHash.hashCacheTTL,\n },\n () => TerraformProviderHash._calculateSingleHash(build, cacheDir),\n );\n }\n\n static async calculateHashScheme1Hashes(\n builds: TerraformBuild[],\n ): Promise<string[]> {\n logger.debug(`Calculating hashes for ${builds.length} builds`);\n const cacheDir = await fs.ensureCacheDir('terraform');\n\n return p.map(builds, (build) => this.calculateSingleHash(build, cacheDir), {\n concurrency: 4,\n });\n }\n\n static async createHashes(\n registryURL: string,\n repository: string,\n version: string,\n ): Promise<string[] | null> {\n logger.debug(\n `Creating hashes for ${repository}@${version} (${registryURL})`,\n );\n\n if (\n registryURL === TerraformProviderDatasource.openTofuRegistryUrl ||\n registryURL === TerraformProviderDatasource.openTofuApiUrl\n ) {\n const packagesHashes =\n await TerraformProviderHash.terraformDatasource.getProviderPackages(\n repository,\n version,\n );\n if (packagesHashes?.length) {\n logger.debug(\n `Using OpenTofu packages API for ${repository}@${version}`,\n );\n // hashes are a logical set which Terraform deduplicates and sorts\n return deduplicateArray(packagesHashes).sort();\n }\n logger.debug(\n `OpenTofu packages field unavailable for ${repository}@${version}, falling back to zip download`,\n );\n }\n\n const builds = await TerraformProviderHash.terraformDatasource.getBuilds(\n registryURL,\n repository,\n version,\n );\n if (!builds) {\n return null;\n }\n\n // check if the publisher uses one shasum file for all builds or separate ones\n // we deduplicate to reduce the number of API calls\n const shaUrls = deduplicateArray(\n builds.map((build) => build.shasums_url).filter(isNotNullOrUndefined),\n );\n\n logger.debug(\n `Getting zip hashes for ${shaUrls.length} shasum URL(s) for ${repository}@${version}`,\n );\n\n const zhHashes: string[] = [];\n for (const shaUrl of shaUrls) {\n const hashes =\n await TerraformProviderHash.terraformDatasource.getZipHashes(shaUrl);\n\n zhHashes.push(...coerceArray(hashes));\n }\n\n logger.debug(\n `Got ${zhHashes.length} zip hashes for ${repository}@${version}`,\n );\n\n const h1Hashes =\n await TerraformProviderHash.calculateHashScheme1Hashes(builds);\n\n const hashes = [];\n hashes.push(...h1Hashes.map((hash) => `h1:${hash}`));\n hashes.push(...zhHashes.map((hash) => `zh:${hash}`));\n\n // hashes are a logical set which Terraform deduplicates and sorts\n return deduplicateArray(hashes).sort();\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"hash.js","names":["fs.createCacheWriteStream","fs.pipeline","fs.rmCache","fs.ensureCacheDir","p.map"],"sources":["../../../../../lib/modules/manager/terraform/lockfile/hash.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport { once } from 'node:events';\nimport { Transform, type TransformCallback } from 'node:stream';\nimport { crc32 } from 'node:zlib';\nimport upath from 'upath';\nimport { openPromise, validateFileName } from 'yauzl';\nimport { logger } from '../../../../logger/index.ts';\nimport {\n coerceArray,\n deduplicateArray,\n isNotNullOrUndefined,\n} from '../../../../util/array.ts';\nimport { buildCacheKey } from '../../../../util/cache/package/key.ts';\nimport { withCache } from '../../../../util/cache/package/with-cache.ts';\nimport * as fs from '../../../../util/fs/index.ts';\nimport { hashStream } from '../../../../util/hash.ts';\nimport { Http } from '../../../../util/http/index.ts';\nimport * as p from '../../../../util/promises.ts';\nimport { TerraformProviderDatasource } from '../../../datasource/terraform-provider/index.ts';\nimport type { TerraformBuild } from '../../../datasource/terraform-provider/schema.ts';\n\nclass Crc32Stream extends Transform {\n checksum = 0;\n\n override _transform(\n chunk: Buffer,\n _encoding: BufferEncoding,\n callback: TransformCallback,\n ): void {\n this.checksum = crc32(chunk, this.checksum);\n callback(null, chunk);\n }\n}\n\nexport class TerraformProviderHash {\n static http = new Http(TerraformProviderDatasource.id);\n\n static terraformDatasource = new TerraformProviderDatasource();\n\n static hashCacheTTL = 10080; // in minutes == 1 week\n\n /**\n * Computes the `h1:` checksum used in Terraform dependency lock files.\n *\n * Terraform verifies extracted providers with Go's `HashDir`, which ignores\n * ZIP directory entries. Ignore them here so the archive produces the same\n * checksum as the extracted provider.\n *\n * See https://github.com/golang/go/issues/53448.\n */\n static async hashOfZipContent(zipFilePath: string): Promise<string> {\n const zipFile = await openPromise(zipFilePath, {\n autoClose: false,\n decodeStrings: false,\n });\n const entryHashes: { path: Buffer; hash: string }[] = [];\n const entryPaths = new Set<string>();\n\n try {\n for await (const entry of zipFile.eachEntry()) {\n // Go's ZIP reader preserves the central-directory name bytes instead of decoding CP437 or Unicode path fields.\n const fileName = entry.fileNameRaw\n .toString('latin1')\n .replaceAll('\\\\', '/');\n\n // Terraform verifies extracted packages with Go HashDir semantics, which omit directory records.\n if (fileName.endsWith('/')) {\n continue;\n }\n if (fileName.includes('\\n')) {\n throw new Error('ZIP entry name contains a newline');\n }\n const fileNameError = validateFileName(fileName);\n if (fileNameError) {\n throw new Error(fileNameError);\n }\n const normalizedPath = upath.normalize(fileName);\n if (entryPaths.has(normalizedPath)) {\n throw new Error(`Duplicate ZIP entry path: ${normalizedPath}`);\n }\n entryPaths.add(normalizedPath);\n const path = Buffer.from(normalizedPath, 'latin1');\n\n const entryStream = await zipFile.openReadStreamPromise(entry);\n const crc32Stream = new Crc32Stream();\n const hash = await hashStream(\n entryStream.compose(crc32Stream),\n 'sha256',\n );\n if (crc32Stream.checksum !== entry.crc32) {\n throw new Error(\n `CRC-32 mismatch for ZIP entry ${entry.fileNameRaw.toString('utf8')}`,\n );\n }\n entryHashes.push({ path, hash });\n }\n } finally {\n const closePromise = once(zipFile, 'close');\n zipFile.close();\n await closePromise;\n }\n\n entryHashes.sort((left, right) => Buffer.compare(left.path, right.path));\n\n const rootHash = crypto.createHash('sha256');\n for (const entry of entryHashes) {\n rootHash.update(`${entry.hash} `);\n rootHash.update(entry.path);\n rootHash.update('\\n');\n }\n\n return rootHash.digest('base64');\n }\n\n private static async _calculateSingleHash(\n build: TerraformBuild,\n cacheDir: string,\n ): Promise<string> {\n const downloadFileName = upath.join(cacheDir, crypto.randomUUID());\n logger.trace(\n `Downloading archive and generating hash for ${build.name}-${build.version}...`,\n );\n const startTime = Date.now();\n const readStream = TerraformProviderHash.http.stream(build.url);\n const writeStream = fs.createCacheWriteStream(downloadFileName);\n\n try {\n await fs.pipeline(readStream, writeStream);\n\n const hash = await this.hashOfZipContent(downloadFileName);\n logger.debug(\n `Hash generation for ${build.url} took ${Date.now() - startTime}ms for ${build.name}-${build.version}`,\n );\n return hash;\n } finally {\n await fs.rmCache(downloadFileName);\n }\n }\n\n static calculateSingleHash(\n build: TerraformBuild,\n cacheDir: string,\n ): Promise<string> {\n return withCache(\n {\n namespace: `terraform-provider-hash`,\n key: buildCacheKey('calculateSingleHash', build.url),\n ttlMinutes: TerraformProviderHash.hashCacheTTL,\n },\n () => TerraformProviderHash._calculateSingleHash(build, cacheDir),\n );\n }\n\n static async calculateHashScheme1Hashes(\n builds: TerraformBuild[],\n ): Promise<string[]> {\n logger.debug(`Calculating hashes for ${builds.length} builds`);\n const cacheDir = await fs.ensureCacheDir('terraform');\n\n return p.map(builds, (build) => this.calculateSingleHash(build, cacheDir), {\n concurrency: 4,\n });\n }\n\n static async createHashes(\n registryURL: string,\n repository: string,\n version: string,\n ): Promise<string[] | null> {\n logger.debug(\n `Creating hashes for ${repository}@${version} (${registryURL})`,\n );\n\n if (\n registryURL === TerraformProviderDatasource.openTofuRegistryUrl ||\n registryURL === TerraformProviderDatasource.openTofuApiUrl\n ) {\n const packagesHashes =\n await TerraformProviderHash.terraformDatasource.getProviderPackages(\n repository,\n version,\n );\n if (packagesHashes?.length) {\n logger.debug(\n `Using OpenTofu packages API for ${repository}@${version}`,\n );\n // hashes are a logical set which Terraform deduplicates and sorts\n return deduplicateArray(packagesHashes).sort();\n }\n logger.debug(\n `OpenTofu packages field unavailable for ${repository}@${version}, falling back to zip download`,\n );\n }\n\n const builds = await TerraformProviderHash.terraformDatasource.getBuilds(\n registryURL,\n repository,\n version,\n );\n if (!builds) {\n return null;\n }\n\n // check if the publisher uses one shasum file for all builds or separate ones\n // we deduplicate to reduce the number of API calls\n const shaUrls = deduplicateArray(\n builds.map((build) => build.shasums_url).filter(isNotNullOrUndefined),\n );\n\n logger.debug(\n `Getting zip hashes for ${shaUrls.length} shasum URL(s) for ${repository}@${version}`,\n );\n\n const zhHashes: string[] = [];\n for (const shaUrl of shaUrls) {\n const hashes =\n await TerraformProviderHash.terraformDatasource.getZipHashes(shaUrl);\n\n zhHashes.push(...coerceArray(hashes));\n }\n\n logger.debug(\n `Got ${zhHashes.length} zip hashes for ${repository}@${version}`,\n );\n\n const h1Hashes =\n await TerraformProviderHash.calculateHashScheme1Hashes(builds);\n\n const hashes = [];\n hashes.push(...h1Hashes.map((hash) => `h1:${hash}`));\n hashes.push(...zhHashes.map((hash) => `zh:${hash}`));\n\n // hashes are a logical set which Terraform deduplicates and sorts\n return deduplicateArray(hashes).sort();\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAqBA,IAAM,cAAN,cAA0B,UAAU;CAClC,WAAW;CAEX,WACE,OACA,WACA,UACM;EACN,KAAK,WAAW,MAAM,OAAO,KAAK,QAAQ;EAC1C,SAAS,MAAM,KAAK;CACtB;AACF;AAEA,IAAa,wBAAb,MAAa,sBAAsB;CACjC,OAAO,OAAO,IAAI,KAAK,4BAA4B,EAAE;CAErD,OAAO,sBAAsB,IAAI,4BAA4B;CAE7D,OAAO,eAAe;;;;;;;;;;CAWtB,aAAa,iBAAiB,aAAsC;EAClE,MAAM,UAAU,MAAM,YAAY,aAAa;GAC7C,WAAW;GACX,eAAe;EACjB,CAAC;EACD,MAAM,cAAgD,CAAC;EACvD,MAAM,6BAAa,IAAI,IAAY;EAEnC,IAAI;GACF,WAAW,MAAM,SAAS,QAAQ,UAAU,GAAG;IAE7C,MAAM,WAAW,MAAM,YACpB,SAAS,QAAQ,CAAC,CAClB,WAAW,MAAM,GAAG;IAGvB,IAAI,SAAS,SAAS,GAAG,GACvB;IAEF,IAAI,SAAS,SAAS,IAAI,GACxB,MAAM,IAAI,MAAM,mCAAmC;IAErD,MAAM,gBAAgB,iBAAiB,QAAQ;IAC/C,IAAI,eACF,MAAM,IAAI,MAAM,aAAa;IAE/B,MAAM,iBAAiB,MAAM,UAAU,QAAQ;IAC/C,IAAI,WAAW,IAAI,cAAc,GAC/B,MAAM,IAAI,MAAM,6BAA6B,gBAAgB;IAE/D,WAAW,IAAI,cAAc;IAC7B,MAAM,OAAO,OAAO,KAAK,gBAAgB,QAAQ;IAEjD,MAAM,cAAc,MAAM,QAAQ,sBAAsB,KAAK;IAC7D,MAAM,cAAc,IAAI,YAAY;IACpC,MAAM,OAAO,MAAM,WACjB,YAAY,QAAQ,WAAW,GAC/B,QACF;IACA,IAAI,YAAY,aAAa,MAAM,OACjC,MAAM,IAAI,MACR,iCAAiC,MAAM,YAAY,SAAS,MAAM,GACpE;IAEF,YAAY,KAAK;KAAE;KAAM;IAAK,CAAC;GACjC;EACF,UAAU;GACR,MAAM,eAAe,KAAK,SAAS,OAAO;GAC1C,QAAQ,MAAM;GACd,MAAM;EACR;EAEA,YAAY,MAAM,MAAM,UAAU,OAAO,QAAQ,KAAK,MAAM,MAAM,IAAI,CAAC;EAEvE,MAAM,WAAW,OAAO,WAAW,QAAQ;EAC3C,KAAK,MAAM,SAAS,aAAa;GAC/B,SAAS,OAAO,GAAG,MAAM,KAAK,GAAG;GACjC,SAAS,OAAO,MAAM,IAAI;GAC1B,SAAS,OAAO,IAAI;EACtB;EAEA,OAAO,SAAS,OAAO,QAAQ;CACjC;CAEA,aAAqB,qBACnB,OACA,UACiB;EACjB,MAAM,mBAAmB,MAAM,KAAK,UAAU,OAAO,WAAW,CAAC;EACjE,OAAO,MACL,+CAA+C,MAAM,KAAK,GAAG,MAAM,QAAQ,IAC7E;EACA,MAAM,YAAY,KAAK,IAAI;EAC3B,MAAM,aAAa,sBAAsB,KAAK,OAAO,MAAM,GAAG;EAC9D,MAAM,cAAcA,uBAA0B,gBAAgB;EAE9D,IAAI;GACF,MAAMC,SAAY,YAAY,WAAW;GAEzC,MAAM,OAAO,MAAM,KAAK,iBAAiB,gBAAgB;GACzD,OAAO,MACL,uBAAuB,MAAM,IAAI,QAAQ,KAAK,IAAI,IAAI,UAAU,SAAS,MAAM,KAAK,GAAG,MAAM,SAC/F;GACA,OAAO;EACT,UAAU;GACR,MAAMC,QAAW,gBAAgB;EACnC;CACF;CAEA,OAAO,oBACL,OACA,UACiB;EACjB,OAAO,UACL;GACE,WAAW;GACX,KAAK,cAAc,uBAAuB,MAAM,GAAG;GACnD,YAAY,sBAAsB;EACpC,SACM,sBAAsB,qBAAqB,OAAO,QAAQ,CAClE;CACF;CAEA,aAAa,2BACX,QACmB;EACnB,OAAO,MAAM,0BAA0B,OAAO,OAAO,QAAQ;EAC7D,MAAM,WAAW,MAAMC,eAAkB,WAAW;EAEpD,OAAOC,IAAM,SAAS,UAAU,KAAK,oBAAoB,OAAO,QAAQ,GAAG,EACzE,aAAa,EACf,CAAC;CACH;CAEA,aAAa,aACX,aACA,YACA,SAC0B;EAC1B,OAAO,MACL,uBAAuB,WAAW,GAAG,QAAQ,IAAI,YAAY,EAC/D;EAEA,IACE,gBAAgB,4BAA4B,uBAC5C,gBAAgB,4BAA4B,gBAC5C;GACA,MAAM,iBACJ,MAAM,sBAAsB,oBAAoB,oBAC9C,YACA,OACF;GACF,IAAI,gBAAgB,QAAQ;IAC1B,OAAO,MACL,mCAAmC,WAAW,GAAG,SACnD;IAEA,OAAO,iBAAiB,cAAc,CAAC,CAAC,KAAK;GAC/C;GACA,OAAO,MACL,2CAA2C,WAAW,GAAG,QAAQ,+BACnE;EACF;EAEA,MAAM,SAAS,MAAM,sBAAsB,oBAAoB,UAC7D,aACA,YACA,OACF;EACA,IAAI,CAAC,QACH,OAAO;EAKT,MAAM,UAAU,iBACd,OAAO,KAAK,UAAU,MAAM,WAAW,CAAC,CAAC,OAAO,oBAAoB,CACtE;EAEA,OAAO,MACL,0BAA0B,QAAQ,OAAO,qBAAqB,WAAW,GAAG,SAC9E;EAEA,MAAM,WAAqB,CAAC;EAC5B,KAAK,MAAM,UAAU,SAAS;GAC5B,MAAM,SACJ,MAAM,sBAAsB,oBAAoB,aAAa,MAAM;GAErE,SAAS,KAAK,GAAG,YAAY,MAAM,CAAC;EACtC;EAEA,OAAO,MACL,OAAO,SAAS,OAAO,kBAAkB,WAAW,GAAG,SACzD;EAEA,MAAM,WACJ,MAAM,sBAAsB,2BAA2B,MAAM;EAE/D,MAAM,SAAS,CAAC;EAChB,OAAO,KAAK,GAAG,SAAS,KAAK,SAAS,MAAM,MAAM,CAAC;EACnD,OAAO,KAAK,GAAG,SAAS,KAAK,SAAS,MAAM,MAAM,CAAC;EAGnD,OAAO,iBAAiB,MAAM,CAAC,CAAC,KAAK;CACvC;AACF"}
@@ -7,6 +7,7 @@ import { prFieldsFilter, prInfo, prStates } from "./utils.js";
7
7
  import { dequal } from "dequal";
8
8
  import { DateTime } from "luxon";
9
9
  //#region lib/modules/platform/bitbucket/pr-cache.ts
10
+ const syncedCacheKey = "bitbucket:pr-cache-synced";
10
11
  var BitbucketPrCache = class BitbucketPrCache {
11
12
  items = [];
12
13
  cache;
@@ -40,9 +41,9 @@ var BitbucketPrCache = class BitbucketPrCache {
40
41
  }
41
42
  static async init(http, repo, author) {
42
43
  const res = new BitbucketPrCache(repo, author);
43
- if (!get("bitbucket-pr-cache-synced")) {
44
+ if (!get(syncedCacheKey)) {
44
45
  await res.sync(http);
45
- set("bitbucket-pr-cache-synced", true);
46
+ set(syncedCacheKey, true);
46
47
  }
47
48
  return res;
48
49
  }
@@ -1 +1 @@
1
- {"version":3,"file":"pr-cache.js","names":["memCache.get"],"sources":["../../../../lib/modules/platform/bitbucket/pr-cache.ts"],"sourcesContent":["import { dequal } from 'dequal';\nimport { DateTime } from 'luxon';\nimport { logger } from '../../../logger/index.ts';\nimport * as memCache from '../../../util/cache/memory/index.ts';\nimport { getCache } from '../../../util/cache/repository/index.ts';\nimport { clone } from '../../../util/clone.ts';\nimport type { BitbucketHttp } from '../../../util/http/bitbucket.ts';\nimport { repoCacheProvider } from '../../../util/http/cache/repository-http-cache-provider.ts';\nimport type { Pr } from '../types.ts';\nimport type { BitbucketPrCacheData, PagedResult, PrResponse } from './types.ts';\nimport { prFieldsFilter, prInfo, prStates } from './utils.ts';\n\nexport class BitbucketPrCache {\n private items: Pr[] = [];\n private cache: BitbucketPrCacheData;\n private repo: string;\n private author: string | null;\n\n private constructor(repo: string, author: string | null) {\n this.repo = repo;\n this.author = author;\n const repoCache = getCache();\n repoCache.platform ??= {};\n repoCache.platform.bitbucket ??= {};\n\n let pullRequestCache = repoCache.platform.bitbucket.pullRequestsCache as\n BitbucketPrCacheData | undefined;\n if (!pullRequestCache) {\n logger.debug('Initializing new PR cache at repository cache');\n pullRequestCache = {\n items: {},\n updated_on: null,\n author,\n };\n } else if (pullRequestCache.author !== author) {\n logger.debug('Resetting PR cache because authors do not match');\n pullRequestCache = {\n items: {},\n updated_on: null,\n author,\n };\n }\n repoCache.platform.bitbucket.pullRequestsCache = pullRequestCache;\n this.cache = pullRequestCache;\n this.updateItems();\n }\n\n private static async init(\n http: BitbucketHttp,\n repo: string,\n author: string | null,\n ): Promise<BitbucketPrCache> {\n const res = new BitbucketPrCache(repo, author);\n const isSynced = memCache.get<true | undefined>(\n 'bitbucket-pr-cache-synced',\n );\n\n if (!isSynced) {\n await res.sync(http);\n memCache.set('bitbucket-pr-cache-synced', true);\n }\n\n return res;\n }\n\n private getPrs(): Pr[] {\n return this.items;\n }\n\n static async getPrs(\n http: BitbucketHttp,\n repo: string,\n author: string | null,\n ): Promise<Pr[]> {\n const prCache = await BitbucketPrCache.init(http, repo, author);\n return prCache.getPrs();\n }\n\n private setPr(pr: Pr): void {\n logger.debug(`Adding PR #${pr.number} to the PR cache`);\n this.cache.items[pr.number] = pr;\n this.updateItems();\n }\n\n static async setPr(\n http: BitbucketHttp,\n repo: string,\n author: string | null,\n item: Pr,\n ): Promise<void> {\n const prCache = await BitbucketPrCache.init(http, repo, author);\n prCache.setPr(item);\n }\n\n private reconcile(rawItems: PrResponse[]): void {\n const { items: oldItems } = this.cache;\n let { updated_on } = this.cache;\n\n for (const rawItem of rawItems) {\n const id = rawItem.id;\n\n const oldItem = oldItems[id];\n const newItem = prInfo(rawItem);\n\n const itemNewTime = DateTime.fromISO(rawItem.updated_on);\n\n if (!dequal(oldItem, newItem)) {\n oldItems[id] = newItem;\n }\n\n const cacheOldTime = updated_on ? DateTime.fromISO(updated_on) : null;\n // v8 ignore else -- TODO: add test #40625\n if (!cacheOldTime || itemNewTime > cacheOldTime) {\n updated_on = rawItem.updated_on;\n }\n }\n\n this.cache.updated_on = updated_on;\n }\n\n private getUrl(): string {\n const params = new URLSearchParams();\n\n for (const state of prStates.all) {\n params.append('state', state);\n }\n\n params.append('fields', prFieldsFilter);\n\n const q: string[] = [];\n if (this.author) {\n q.push(`author.uuid = \"${this.author}\"`);\n }\n if (this.cache.updated_on) {\n q.push(`updated_on > \"${this.cache.updated_on}\"`);\n }\n params.append('q', q.join(' AND '));\n\n const query = params.toString();\n return `/2.0/repositories/${this.repo}/pullrequests?${query}`;\n }\n\n private async sync(http: BitbucketHttp): Promise<BitbucketPrCache> {\n logger.debug('Syncing PR list');\n const url = this.getUrl();\n const opts = {\n paginate: true,\n pagelen: 50,\n cacheProvider: repoCacheProvider,\n };\n const res = await http.getJsonUnchecked<PagedResult<PrResponse>>(url, opts);\n\n const items = res.body.values;\n logger.debug(`Fetched ${items.length} PRs to sync with cache`);\n const oldCache = clone(this.cache.items);\n\n this.reconcile(items);\n\n logger.debug(`Total PRs cached: ${Object.values(this.cache.items).length}`);\n logger.trace(\n {\n items,\n oldCache,\n newCache: this.cache.items,\n },\n `PR cache sync finished`,\n );\n\n this.updateItems();\n return this;\n }\n\n /**\n * Ensure the pr cache starts with the most recent PRs.\n * JavaScript ensures that the cache is sorted by PR number.\n */\n private updateItems(): void {\n this.items = Object.values(this.cache.items).reverse();\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"pr-cache.js","names":["memCache.get"],"sources":["../../../../lib/modules/platform/bitbucket/pr-cache.ts"],"sourcesContent":["import { dequal } from 'dequal';\nimport { DateTime } from 'luxon';\nimport { logger } from '../../../logger/index.ts';\nimport * as memCache from '../../../util/cache/memory/index.ts';\nimport { getCache } from '../../../util/cache/repository/index.ts';\nimport { clone } from '../../../util/clone.ts';\nimport type { BitbucketHttp } from '../../../util/http/bitbucket.ts';\nimport { repoCacheProvider } from '../../../util/http/cache/repository-http-cache-provider.ts';\nimport type { Pr } from '../types.ts';\nimport type { BitbucketPrCacheData, PagedResult, PrResponse } from './types.ts';\nimport { prFieldsFilter, prInfo, prStates } from './utils.ts';\n\nconst syncedCacheKey = 'bitbucket:pr-cache-synced';\n\nexport class BitbucketPrCache {\n private items: Pr[] = [];\n private cache: BitbucketPrCacheData;\n private repo: string;\n private author: string | null;\n\n private constructor(repo: string, author: string | null) {\n this.repo = repo;\n this.author = author;\n const repoCache = getCache();\n repoCache.platform ??= {};\n repoCache.platform.bitbucket ??= {};\n\n let pullRequestCache = repoCache.platform.bitbucket.pullRequestsCache as\n BitbucketPrCacheData | undefined;\n if (!pullRequestCache) {\n logger.debug('Initializing new PR cache at repository cache');\n pullRequestCache = {\n items: {},\n updated_on: null,\n author,\n };\n } else if (pullRequestCache.author !== author) {\n logger.debug('Resetting PR cache because authors do not match');\n pullRequestCache = {\n items: {},\n updated_on: null,\n author,\n };\n }\n repoCache.platform.bitbucket.pullRequestsCache = pullRequestCache;\n this.cache = pullRequestCache;\n this.updateItems();\n }\n\n private static async init(\n http: BitbucketHttp,\n repo: string,\n author: string | null,\n ): Promise<BitbucketPrCache> {\n const res = new BitbucketPrCache(repo, author);\n const isSynced = memCache.get<true | undefined>(syncedCacheKey);\n\n if (!isSynced) {\n await res.sync(http);\n memCache.set(syncedCacheKey, true);\n }\n\n return res;\n }\n\n private getPrs(): Pr[] {\n return this.items;\n }\n\n static async getPrs(\n http: BitbucketHttp,\n repo: string,\n author: string | null,\n ): Promise<Pr[]> {\n const prCache = await BitbucketPrCache.init(http, repo, author);\n return prCache.getPrs();\n }\n\n private setPr(pr: Pr): void {\n logger.debug(`Adding PR #${pr.number} to the PR cache`);\n this.cache.items[pr.number] = pr;\n this.updateItems();\n }\n\n static async setPr(\n http: BitbucketHttp,\n repo: string,\n author: string | null,\n item: Pr,\n ): Promise<void> {\n const prCache = await BitbucketPrCache.init(http, repo, author);\n prCache.setPr(item);\n }\n\n private reconcile(rawItems: PrResponse[]): void {\n const { items: oldItems } = this.cache;\n let { updated_on } = this.cache;\n\n for (const rawItem of rawItems) {\n const id = rawItem.id;\n\n const oldItem = oldItems[id];\n const newItem = prInfo(rawItem);\n\n const itemNewTime = DateTime.fromISO(rawItem.updated_on);\n\n if (!dequal(oldItem, newItem)) {\n oldItems[id] = newItem;\n }\n\n const cacheOldTime = updated_on ? DateTime.fromISO(updated_on) : null;\n // v8 ignore else -- TODO: add test #40625\n if (!cacheOldTime || itemNewTime > cacheOldTime) {\n updated_on = rawItem.updated_on;\n }\n }\n\n this.cache.updated_on = updated_on;\n }\n\n private getUrl(): string {\n const params = new URLSearchParams();\n\n for (const state of prStates.all) {\n params.append('state', state);\n }\n\n params.append('fields', prFieldsFilter);\n\n const q: string[] = [];\n if (this.author) {\n q.push(`author.uuid = \"${this.author}\"`);\n }\n if (this.cache.updated_on) {\n q.push(`updated_on > \"${this.cache.updated_on}\"`);\n }\n params.append('q', q.join(' AND '));\n\n const query = params.toString();\n return `/2.0/repositories/${this.repo}/pullrequests?${query}`;\n }\n\n private async sync(http: BitbucketHttp): Promise<BitbucketPrCache> {\n logger.debug('Syncing PR list');\n const url = this.getUrl();\n const opts = {\n paginate: true,\n pagelen: 50,\n cacheProvider: repoCacheProvider,\n };\n const res = await http.getJsonUnchecked<PagedResult<PrResponse>>(url, opts);\n\n const items = res.body.values;\n logger.debug(`Fetched ${items.length} PRs to sync with cache`);\n const oldCache = clone(this.cache.items);\n\n this.reconcile(items);\n\n logger.debug(`Total PRs cached: ${Object.values(this.cache.items).length}`);\n logger.trace(\n {\n items,\n oldCache,\n newCache: this.cache.items,\n },\n `PR cache sync finished`,\n );\n\n this.updateItems();\n return this;\n }\n\n /**\n * Ensure the pr cache starts with the most recent PRs.\n * JavaScript ensures that the cache is sorted by PR number.\n */\n private updateItems(): void {\n this.items = Object.values(this.cache.items).reverse();\n }\n}\n"],"mappings":"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"}
@@ -7,6 +7,7 @@ import { isNullOrUndefined, isPlainObject, isString } from "@sindresorhus/is";
7
7
  import { dequal } from "dequal";
8
8
  import { DateTime } from "luxon";
9
9
  //#region lib/modules/platform/bitbucket-server/pr-cache.ts
10
+ const syncedCacheKey = "bitbucket-server:pr-cache-synced";
10
11
  /* v8 ignore next -- one-off cache-schema migration shim, only runs against real legacy repo caches */
11
12
  function migrateBitbucketServerCache(platform) {
12
13
  if (!isPlainObject(platform)) return;
@@ -43,9 +44,9 @@ var BbsPrCache = class BbsPrCache {
43
44
  static async init(http, projectKey, repo, ignorePrAuthor, author) {
44
45
  const res = new BbsPrCache(projectKey, repo, ignorePrAuthor, author);
45
46
  // v8 ignore next -- TODO: add test #40625
46
- if (!get("bbs-pr-cache-synced")) {
47
+ if (!get(syncedCacheKey)) {
47
48
  await res.sync(http);
48
- set("bbs-pr-cache-synced", true);
49
+ set(syncedCacheKey, true);
49
50
  }
50
51
  return res;
51
52
  }