renovate 43.209.4 → 43.210.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/dist/modules/datasource/bitbucket-tags/index.js +6 -5
  2. package/dist/modules/datasource/bitbucket-tags/index.js.map +1 -1
  3. package/dist/modules/datasource/bitbucket-tags/schema.js +20 -0
  4. package/dist/modules/datasource/bitbucket-tags/schema.js.map +1 -0
  5. package/dist/modules/datasource/conan/index.js +2 -2
  6. package/dist/modules/datasource/conan/index.js.map +1 -1
  7. package/dist/modules/datasource/conda/index.js +3 -3
  8. package/dist/modules/datasource/conda/index.js.map +1 -1
  9. package/dist/modules/datasource/conda/schema.js +17 -0
  10. package/dist/modules/datasource/conda/schema.js.map +1 -0
  11. package/dist/modules/datasource/crate/index.js +3 -2
  12. package/dist/modules/datasource/crate/index.js.map +1 -1
  13. package/dist/modules/datasource/crate/schema.js +8 -1
  14. package/dist/modules/datasource/crate/schema.js.map +1 -1
  15. package/dist/modules/datasource/gradle-version/index.js +2 -2
  16. package/dist/modules/datasource/gradle-version/index.js.map +1 -1
  17. package/dist/modules/datasource/gradle-version/schema.js +2 -2
  18. package/dist/modules/datasource/gradle-version/schema.js.map +1 -1
  19. package/dist/modules/datasource/jenkins-plugins/index.js +7 -6
  20. package/dist/modules/datasource/jenkins-plugins/index.js.map +1 -1
  21. package/dist/modules/datasource/jenkins-plugins/schema.js +20 -0
  22. package/dist/modules/datasource/jenkins-plugins/schema.js.map +1 -0
  23. package/dist/modules/datasource/nuget/schema.js +26 -0
  24. package/dist/modules/datasource/nuget/schema.js.map +1 -0
  25. package/dist/modules/datasource/nuget/v3.js +7 -4
  26. package/dist/modules/datasource/nuget/v3.js.map +1 -1
  27. package/dist/modules/datasource/puppet-forge/index.js +5 -18
  28. package/dist/modules/datasource/puppet-forge/index.js.map +1 -1
  29. package/dist/modules/datasource/puppet-forge/schema.js +28 -0
  30. package/dist/modules/datasource/puppet-forge/schema.js.map +1 -0
  31. package/dist/modules/datasource/repology/index.js +2 -1
  32. package/dist/modules/datasource/repology/index.js.map +1 -1
  33. package/dist/modules/datasource/repology/schema.js +14 -0
  34. package/dist/modules/datasource/repology/schema.js.map +1 -0
  35. package/dist/modules/datasource/terraform-provider/index.js +6 -6
  36. package/dist/modules/datasource/terraform-provider/index.js.map +1 -1
  37. package/dist/modules/datasource/terraform-provider/schema.js +37 -1
  38. package/dist/modules/datasource/terraform-provider/schema.js.map +1 -1
  39. package/dist/modules/manager/fingerprint.generated.js +1 -1
  40. package/dist/modules/manager/fingerprint.generated.js.map +1 -1
  41. package/dist/modules/manager/npm/extract/pnpm.js +2 -2
  42. package/dist/modules/manager/npm/extract/pnpm.js.map +1 -1
  43. package/dist/modules/manager/terraform/lockfile/hash.js.map +1 -1
  44. package/dist/modules/platform/azure/azure-got-wrapper.js +4 -1
  45. package/dist/modules/platform/azure/azure-got-wrapper.js.map +1 -1
  46. package/dist/modules/platform/azure/index.js +13 -16
  47. package/dist/modules/platform/azure/index.js.map +1 -1
  48. package/dist/modules/platform/azure/issue.js +197 -0
  49. package/dist/modules/platform/azure/issue.js.map +1 -0
  50. package/dist/modules/platform/azure/types.js.map +1 -1
  51. package/dist/modules/platform/azure/util.js +6 -1
  52. package/dist/modules/platform/azure/util.js.map +1 -1
  53. package/dist/modules/platform/types.d.ts +2 -0
  54. package/dist/util/merge-confidence/index.js +2 -1
  55. package/dist/util/merge-confidence/index.js.map +1 -1
  56. package/dist/util/merge-confidence/schema.js +7 -0
  57. package/dist/util/merge-confidence/schema.js.map +1 -0
  58. package/package.json +2 -2
@@ -0,0 +1,26 @@
1
+ import { LooseArray } from "../../../util/schema-utils/index.js";
2
+ import { z } from "zod/v4";
3
+ //#region lib/modules/datasource/nuget/schema.ts
4
+ const ServicesIndexRaw = z.object({ resources: LooseArray(z.object({
5
+ "@id": z.string(),
6
+ "@type": z.string()
7
+ })).catch([]) });
8
+ const Deprecation = z.object({ reasons: z.array(z.string()) });
9
+ const CatalogEntry = z.object({
10
+ version: z.string(),
11
+ published: z.string().optional(),
12
+ projectUrl: z.string().optional(),
13
+ listed: z.boolean().optional(),
14
+ packageContent: z.string().optional(),
15
+ deprecation: Deprecation.optional()
16
+ });
17
+ const CatalogItem = z.object({ catalogEntry: CatalogEntry });
18
+ const CatalogPage = z.object({
19
+ "@id": z.string().optional(),
20
+ items: LooseArray(CatalogItem).optional()
21
+ });
22
+ const PackageRegistration = z.object({ items: LooseArray(CatalogPage).default([]) });
23
+ //#endregion
24
+ export { CatalogPage, PackageRegistration, ServicesIndexRaw };
25
+
26
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","names":[],"sources":["../../../../lib/modules/datasource/nuget/schema.ts"],"sourcesContent":["import { z } from 'zod/v4';\nimport { LooseArray } from '../../../util/schema-utils/index.ts';\n\nexport const ServicesIndexRaw = z.object({\n resources: LooseArray(\n z.object({\n '@id': z.string(),\n '@type': z.string(),\n }),\n ).catch([]),\n});\n\nexport type ServicesIndexRaw = z.infer<typeof ServicesIndexRaw>;\n\nconst Deprecation = z.object({\n reasons: z.array(z.string()),\n});\n\n// See https://learn.microsoft.com/en-us/nuget/api/registration-base-url-resource#catalog-entry\nexport const CatalogEntry = z.object({\n version: z.string(),\n published: z.string().optional(),\n projectUrl: z.string().optional(),\n listed: z.boolean().optional(),\n packageContent: z.string().optional(),\n deprecation: Deprecation.optional(),\n});\n\nexport type CatalogEntry = z.infer<typeof CatalogEntry>;\n\nconst CatalogItem = z.object({\n catalogEntry: CatalogEntry,\n});\n\nexport const CatalogPage = z.object({\n '@id': z.string().optional(),\n items: LooseArray(CatalogItem).optional(),\n});\n\nexport type CatalogPage = z.infer<typeof CatalogPage>;\n\nexport const PackageRegistration = z.object({\n items: LooseArray(CatalogPage).default([]),\n});\n\nexport type PackageRegistration = z.infer<typeof PackageRegistration>;\n"],"mappings":";;;AAGA,MAAa,mBAAmB,EAAE,OAAO,EACvC,WAAW,WACT,EAAE,OAAO;CACP,OAAO,EAAE,OAAO;CAChB,SAAS,EAAE,OAAO;AACpB,CAAC,CACH,EAAE,MAAM,CAAC,CAAC,EACZ,CAAC;AAID,MAAM,cAAc,EAAE,OAAO,EAC3B,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAC7B,CAAC;AAGD,MAAa,eAAe,EAAE,OAAO;CACnC,SAAS,EAAE,OAAO;CAClB,WAAW,EAAE,OAAO,EAAE,SAAS;CAC/B,YAAY,EAAE,OAAO,EAAE,SAAS;CAChC,QAAQ,EAAE,QAAQ,EAAE,SAAS;CAC7B,gBAAgB,EAAE,OAAO,EAAE,SAAS;CACpC,aAAa,YAAY,SAAS;AACpC,CAAC;AAID,MAAM,cAAc,EAAE,OAAO,EAC3B,cAAc,aAChB,CAAC;AAED,MAAa,cAAc,EAAE,OAAO;CAClC,OAAO,EAAE,OAAO,EAAE,SAAS;CAC3B,OAAO,WAAW,WAAW,EAAE,SAAS;AAC1C,CAAC;AAID,MAAa,sBAAsB,EAAE,OAAO,EAC1C,OAAO,WAAW,WAAW,EAAE,QAAQ,CAAC,CAAC,EAC3C,CAAC"}
@@ -2,6 +2,7 @@ import { getEnv } from "../../../util/env.js";
2
2
  import { regEx } from "../../../util/regex.js";
3
3
  import { logger } from "../../../logger/index.js";
4
4
  import { ensureTrailingSlash } from "../../../util/url.js";
5
+ import { coerceArray } from "../../../util/array.js";
5
6
  import { ExternalHostError } from "../../../types/errors/external-host-error.js";
6
7
  import { createCacheWriteStream, ensureCacheDir, pipeline, readCacheFile, rmCache } from "../../../util/fs/index.js";
7
8
  import api from "../../versioning/nuget/index.js";
@@ -13,6 +14,7 @@ import { asTimestamp } from "../../../util/timestamp.js";
13
14
  import { all } from "../../../util/promises.js";
14
15
  import { memCacheProvider } from "../../../util/http/cache/memory-http-cache-provider.js";
15
16
  import { massageUrl, removeBuildMeta, sortNugetVersions } from "./common.js";
17
+ import { CatalogPage, PackageRegistration, ServicesIndexRaw } from "./schema.js";
16
18
  import { isNonEmptyString } from "@sindresorhus/is";
17
19
  import upath from "upath";
18
20
  import semver from "semver";
@@ -31,7 +33,7 @@ var NugetV3Api = class NugetV3Api {
31
33
  const responseCacheKey = url;
32
34
  servicesIndexRaw = await get(NugetV3Api.cacheNamespace, responseCacheKey);
33
35
  if (!servicesIndexRaw) {
34
- servicesIndexRaw = (await http.getJsonUnchecked(url, { cacheProvider: memCacheProvider })).body;
36
+ servicesIndexRaw = (await http.getJson(url, { cacheProvider: memCacheProvider }, ServicesIndexRaw)).body;
35
37
  await set(NugetV3Api.cacheNamespace, responseCacheKey, servicesIndexRaw, 4320);
36
38
  }
37
39
  const services = servicesIndexRaw.resources.map(({ "@id": serviceId, "@type": t }) => ({
@@ -70,13 +72,14 @@ var NugetV3Api = class NugetV3Api {
70
72
  let items = catalogPage.items;
71
73
  if (!items) {
72
74
  const url = catalogPage["@id"];
73
- items = (await http.getJsonUnchecked(url)).body.items;
75
+ if (!url) return [];
76
+ items = (await http.getJson(url, CatalogPage)).body.items;
74
77
  }
75
- return items.map(({ catalogEntry }) => catalogEntry);
78
+ return coerceArray(items).map(({ catalogEntry }) => catalogEntry);
76
79
  }
77
80
  async getReleases(http, registryUrl, feedUrl, pkgName) {
78
81
  const url = `${feedUrl.replace(regEx(/\/*$/), "")}/${pkgName.toLowerCase()}/index.json`;
79
- const catalogPages = (await http.getJsonUnchecked(url)).body.items || [];
82
+ const catalogPages = (await http.getJson(url, PackageRegistration)).body.items;
80
83
  const catalogEntries = (await all(catalogPages.map((page) => () => this.getCatalogEntry(http, page)))).flat().sort((a, b) => sortNugetVersions(a.version, b.version));
81
84
  let homepage = null;
82
85
  let latestStable = null;
@@ -1 +1 @@
1
- {"version":3,"file":"v3.js","names":["packageCache.get","packageCache.set","p.all","versioning","HttpError","fs.createCacheWriteStream","fs.pipeline","fs.readCacheFile","fs.rmCache"],"sources":["../../../../lib/modules/datasource/nuget/v3.ts"],"sourcesContent":["import { isNonEmptyString } from '@sindresorhus/is';\nimport AdmZip from 'adm-zip';\nimport semver from 'semver';\nimport upath from 'upath';\nimport { XmlDocument } from 'xmldoc';\nimport { logger } from '../../../logger/index.ts';\nimport { ExternalHostError } from '../../../types/errors/external-host-error.ts';\nimport * as packageCache from '../../../util/cache/package/index.ts';\nimport { withCache } from '../../../util/cache/package/with-cache.ts';\nimport { getEnv } from '../../../util/env.ts';\nimport * as fs from '../../../util/fs/index.ts';\nimport { ensureCacheDir } from '../../../util/fs/index.ts';\nimport { memCacheProvider } from '../../../util/http/cache/memory-http-cache-provider.ts';\nimport type { Http } from '../../../util/http/index.ts';\nimport { HttpError } from '../../../util/http/index.ts';\nimport * as p from '../../../util/promises.ts';\nimport { regEx } from '../../../util/regex.ts';\nimport { asTimestamp } from '../../../util/timestamp.ts';\nimport { ensureTrailingSlash } from '../../../util/url.ts';\nimport { api as versioning } from '../../versioning/nuget/index.ts';\nimport type { Release, ReleaseResult } from '../types.ts';\nimport { massageUrl, removeBuildMeta, sortNugetVersions } from './common.ts';\nimport type {\n CatalogEntry,\n CatalogPage,\n PackageRegistration,\n ServicesIndexRaw,\n} from './types.ts';\n\nexport class NugetV3Api {\n static readonly cacheNamespace = 'datasource-nuget-v3';\n\n async getResourceUrl(\n http: Http,\n url: string,\n resourceType = 'RegistrationsBaseUrl',\n ): Promise<string | null> {\n // https://learn.microsoft.com/nuget/api/service-index\n const resultCacheKey = `${url}:${resourceType}`;\n const cachedResult = await packageCache.get<string>(\n NugetV3Api.cacheNamespace,\n resultCacheKey,\n );\n\n /* v8 ignore next 3 -- TODO: add test */\n if (cachedResult) {\n return cachedResult;\n }\n let servicesIndexRaw: ServicesIndexRaw | undefined;\n try {\n const responseCacheKey = url;\n servicesIndexRaw = await packageCache.get<ServicesIndexRaw>(\n NugetV3Api.cacheNamespace,\n responseCacheKey,\n );\n if (!servicesIndexRaw) {\n servicesIndexRaw = (\n await http.getJsonUnchecked<ServicesIndexRaw>(url, {\n cacheProvider: memCacheProvider,\n })\n ).body;\n await packageCache.set(\n NugetV3Api.cacheNamespace,\n responseCacheKey,\n servicesIndexRaw,\n 3 * 24 * 60,\n );\n }\n\n const services = servicesIndexRaw.resources\n .map(({ '@id': serviceId, '@type': t }) => ({\n serviceId,\n type: t?.split('/')?.shift(),\n version: t?.split('/')?.pop(),\n }))\n .filter(\n ({ type, version }) => type === resourceType && semver.valid(version),\n )\n .sort((x, y) =>\n x.version && y.version\n ? semver.compare(x.version, y.version)\n : /* istanbul ignore next: hard to test */ 0,\n );\n\n if (services.length === 0) {\n await packageCache.set(\n NugetV3Api.cacheNamespace,\n resultCacheKey,\n null,\n 60,\n );\n logger.debug(\n { url, servicesIndexRaw },\n `no ${resourceType} services found`,\n );\n return null;\n }\n\n const { serviceId, version } = services.pop()!;\n\n // istanbul ignore if\n if (\n resourceType === 'RegistrationsBaseUrl' &&\n version &&\n !version.startsWith('3.0.0-') &&\n !semver.satisfies(version, '^3.0.0')\n ) {\n logger.warn(\n { url, version },\n `Nuget: Unknown version returned. Only v3 is supported`,\n );\n }\n\n await packageCache.set(\n NugetV3Api.cacheNamespace,\n resultCacheKey,\n serviceId,\n 60,\n );\n return serviceId;\n } catch (err) {\n // istanbul ignore if: not easy testable with nock\n if (err instanceof ExternalHostError) {\n throw err;\n }\n logger.debug(\n { err, url, servicesIndexRaw },\n `nuget registry failure: can't get ${resourceType}`,\n );\n return null;\n }\n }\n\n async getCatalogEntry(\n http: Http,\n catalogPage: CatalogPage,\n ): Promise<CatalogEntry[]> {\n let items = catalogPage.items;\n if (!items) {\n const url = catalogPage['@id'];\n const catalogPageFull = await http.getJsonUnchecked<CatalogPage>(url);\n items = catalogPageFull.body.items;\n }\n return items.map(({ catalogEntry }) => catalogEntry);\n }\n\n async getReleases(\n http: Http,\n registryUrl: string,\n feedUrl: string,\n pkgName: string,\n ): Promise<ReleaseResult | null> {\n const baseUrl = feedUrl.replace(regEx(/\\/*$/), '');\n const url = `${baseUrl}/${pkgName.toLowerCase()}/index.json`;\n const packageRegistration =\n await http.getJsonUnchecked<PackageRegistration>(url);\n const catalogPages = packageRegistration.body.items || [];\n const catalogPagesQueue = catalogPages.map(\n (page) => (): Promise<CatalogEntry[]> => this.getCatalogEntry(http, page),\n );\n const catalogEntries = (await p.all(catalogPagesQueue))\n .flat()\n .sort((a, b) => sortNugetVersions(a.version, b.version));\n\n let homepage: string | null = null;\n let latestStable: string | null = null;\n let nupkgUrl: string | null = null;\n const releases = catalogEntries.map(\n ({\n version,\n published,\n projectUrl,\n listed,\n packageContent,\n deprecation,\n }) => {\n const release: Release = { version: removeBuildMeta(version) };\n const releaseTimestamp = asTimestamp(published);\n if (releaseTimestamp) {\n release.releaseTimestamp = releaseTimestamp;\n }\n if (\n versioning.isValid(version) &&\n versioning.isStable(version) &&\n listed\n ) {\n latestStable = removeBuildMeta(version);\n homepage = projectUrl ? massageUrl(projectUrl) : homepage;\n nupkgUrl = massageUrl(packageContent);\n }\n\n if (listed === false || deprecation) {\n release.isDeprecated = true;\n }\n return release;\n },\n );\n\n if (!releases.length) {\n return null;\n }\n\n // istanbul ignore next: only happens when no stable version exists\n if (latestStable === null && catalogPages.length) {\n const last = catalogEntries.pop()!;\n latestStable = removeBuildMeta(last.version);\n homepage ??= last.projectUrl ?? null;\n nupkgUrl ??= massageUrl(last.packageContent);\n }\n\n const dep: ReleaseResult = {\n releases,\n };\n\n if (releases.every((release) => release.isDeprecated === true)) {\n dep.deprecationMessage = this.getDeprecationMessage(pkgName);\n }\n\n try {\n const packageBaseAddress = await this.getResourceUrl(\n http,\n registryUrl,\n 'PackageBaseAddress',\n );\n if (isNonEmptyString(packageBaseAddress)) {\n const nuspecUrl = `${ensureTrailingSlash(\n packageBaseAddress,\n )}${pkgName.toLowerCase()}/${\n // TODO: types (#22198)\n latestStable\n }/${pkgName.toLowerCase()}.nuspec`;\n const metaresult = await http.getText(nuspecUrl, {\n cacheProvider: memCacheProvider,\n });\n const nuspec = new XmlDocument(metaresult.body);\n const releaseNotes = nuspec.valueWithPath('metadata.releaseNotes');\n if (releaseNotes) {\n dep.changelogContent = releaseNotes;\n }\n const sourceUrl = nuspec.valueWithPath('metadata.repository@url');\n if (sourceUrl) {\n dep.sourceUrl = massageUrl(sourceUrl);\n }\n } else if (nupkgUrl) {\n const sourceUrl = await this.getSourceUrlFromNupkg(\n http,\n registryUrl,\n pkgName,\n latestStable,\n nupkgUrl,\n );\n if (sourceUrl) {\n dep.sourceUrl = massageUrl(sourceUrl);\n logger.debug(`Determined sourceUrl ${sourceUrl} from ${nupkgUrl}`);\n }\n }\n } catch (err) {\n // istanbul ignore if: not easy testable with nock\n if (err instanceof ExternalHostError) {\n throw err;\n }\n // ignore / silence 404. Seen on proget, if remote connector is used and package is not yet cached\n if (err instanceof HttpError && err.response?.statusCode === 404) {\n logger.debug(\n { registryUrl, pkgName, pkgVersion: latestStable },\n `package manifest (.nuspec) not found`,\n );\n } else {\n logger.debug(\n { err, registryUrl, pkgName, pkgVersion: latestStable },\n `Cannot obtain sourceUrl`,\n );\n }\n }\n\n if (homepage) {\n // only assign if not assigned\n dep.sourceUrl ??= homepage;\n dep.homepage ??= homepage;\n }\n\n return dep;\n }\n\n private async _getSourceUrlFromNupkg(\n http: Http,\n _registryUrl: string,\n packageName: string,\n packageVersion: string | null,\n nupkgUrl: string,\n ): Promise<string | null> {\n /* v8 ignore next 4 */\n if (!getEnv().RENOVATE_X_NUGET_DOWNLOAD_NUPKGS) {\n logger.once.debug('RENOVATE_X_NUGET_DOWNLOAD_NUPKGS is not set');\n return null;\n }\n const cacheDir = await ensureCacheDir('nuget');\n const nupkgFile = upath.join(\n cacheDir,\n `${packageName}.${packageVersion}.nupkg`,\n );\n const nupkgContentsDir = upath.join(\n cacheDir,\n `${packageName}.${packageVersion}`,\n );\n const readStream = http.stream(nupkgUrl);\n try {\n const writeStream = fs.createCacheWriteStream(nupkgFile);\n await fs.pipeline(readStream, writeStream);\n const zip = new AdmZip(nupkgFile);\n zip.extractAllTo(nupkgContentsDir);\n const nuspecFile = upath.join(nupkgContentsDir, `${packageName}.nuspec`);\n const nuspec = new XmlDocument(\n await fs.readCacheFile(nuspecFile, 'utf8'),\n );\n return nuspec.valueWithPath('metadata.repository@url') ?? null;\n } finally {\n await fs.rmCache(nupkgFile);\n await fs.rmCache(nupkgContentsDir);\n }\n }\n\n getSourceUrlFromNupkg(\n http: Http,\n registryUrl: string,\n packageName: string,\n packageVersion: string | null,\n nupkgUrl: string,\n ): Promise<string | null> {\n return withCache(\n {\n namespace: NugetV3Api.cacheNamespace,\n key: `source-url:${registryUrl}:${packageName}`,\n ttlMinutes: 10080, // 1 week\n },\n () =>\n this._getSourceUrlFromNupkg(\n http,\n registryUrl,\n packageName,\n packageVersion,\n nupkgUrl,\n ),\n );\n }\n\n getDeprecationMessage(packageName: string): string {\n return `The package \\`${packageName}\\` is deprecated.`;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"v3.js","names":["packageCache.get","packageCache.set","p.all","versioning","HttpError","fs.createCacheWriteStream","fs.pipeline","fs.readCacheFile","fs.rmCache"],"sources":["../../../../lib/modules/datasource/nuget/v3.ts"],"sourcesContent":["import { isNonEmptyString } from '@sindresorhus/is';\nimport AdmZip from 'adm-zip';\nimport semver from 'semver';\nimport upath from 'upath';\nimport { XmlDocument } from 'xmldoc';\nimport { logger } from '../../../logger/index.ts';\nimport { ExternalHostError } from '../../../types/errors/external-host-error.ts';\nimport { coerceArray } from '../../../util/array.ts';\nimport * as packageCache from '../../../util/cache/package/index.ts';\nimport { withCache } from '../../../util/cache/package/with-cache.ts';\nimport { getEnv } from '../../../util/env.ts';\nimport * as fs from '../../../util/fs/index.ts';\nimport { ensureCacheDir } from '../../../util/fs/index.ts';\nimport { memCacheProvider } from '../../../util/http/cache/memory-http-cache-provider.ts';\nimport type { Http } from '../../../util/http/index.ts';\nimport { HttpError } from '../../../util/http/index.ts';\nimport * as p from '../../../util/promises.ts';\nimport { regEx } from '../../../util/regex.ts';\nimport { asTimestamp } from '../../../util/timestamp.ts';\nimport { ensureTrailingSlash } from '../../../util/url.ts';\nimport { api as versioning } from '../../versioning/nuget/index.ts';\nimport type { Release, ReleaseResult } from '../types.ts';\nimport { massageUrl, removeBuildMeta, sortNugetVersions } from './common.ts';\nimport type { CatalogEntry } from './schema.ts';\nimport {\n CatalogPage,\n PackageRegistration,\n ServicesIndexRaw,\n} from './schema.ts';\n\nexport class NugetV3Api {\n static readonly cacheNamespace = 'datasource-nuget-v3';\n\n async getResourceUrl(\n http: Http,\n url: string,\n resourceType = 'RegistrationsBaseUrl',\n ): Promise<string | null> {\n // https://learn.microsoft.com/nuget/api/service-index\n const resultCacheKey = `${url}:${resourceType}`;\n const cachedResult = await packageCache.get<string>(\n NugetV3Api.cacheNamespace,\n resultCacheKey,\n );\n\n /* v8 ignore next 3 -- TODO: add test */\n if (cachedResult) {\n return cachedResult;\n }\n let servicesIndexRaw: ServicesIndexRaw | undefined;\n try {\n const responseCacheKey = url;\n servicesIndexRaw = await packageCache.get<ServicesIndexRaw>(\n NugetV3Api.cacheNamespace,\n responseCacheKey,\n );\n if (!servicesIndexRaw) {\n servicesIndexRaw = (\n await http.getJson(\n url,\n { cacheProvider: memCacheProvider },\n ServicesIndexRaw,\n )\n ).body;\n await packageCache.set(\n NugetV3Api.cacheNamespace,\n responseCacheKey,\n servicesIndexRaw,\n 3 * 24 * 60,\n );\n }\n\n const services = servicesIndexRaw.resources\n .map(({ '@id': serviceId, '@type': t }) => ({\n serviceId,\n type: t?.split('/')?.shift(),\n version: t?.split('/')?.pop(),\n }))\n .filter(\n ({ type, version }) => type === resourceType && semver.valid(version),\n )\n .sort((x, y) =>\n x.version && y.version\n ? semver.compare(x.version, y.version)\n : /* istanbul ignore next: hard to test */ 0,\n );\n\n if (services.length === 0) {\n await packageCache.set(\n NugetV3Api.cacheNamespace,\n resultCacheKey,\n null,\n 60,\n );\n logger.debug(\n { url, servicesIndexRaw },\n `no ${resourceType} services found`,\n );\n return null;\n }\n\n const { serviceId, version } = services.pop()!;\n\n // istanbul ignore if\n if (\n resourceType === 'RegistrationsBaseUrl' &&\n version &&\n !version.startsWith('3.0.0-') &&\n !semver.satisfies(version, '^3.0.0')\n ) {\n logger.warn(\n { url, version },\n `Nuget: Unknown version returned. Only v3 is supported`,\n );\n }\n\n await packageCache.set(\n NugetV3Api.cacheNamespace,\n resultCacheKey,\n serviceId,\n 60,\n );\n return serviceId;\n } catch (err) {\n // istanbul ignore if: not easy testable with nock\n if (err instanceof ExternalHostError) {\n throw err;\n }\n logger.debug(\n { err, url, servicesIndexRaw },\n `nuget registry failure: can't get ${resourceType}`,\n );\n return null;\n }\n }\n\n async getCatalogEntry(\n http: Http,\n catalogPage: CatalogPage,\n ): Promise<CatalogEntry[]> {\n let items = catalogPage.items;\n if (!items) {\n const url = catalogPage['@id'];\n if (!url) {\n return [];\n }\n const catalogPageFull = await http.getJson(url, CatalogPage);\n items = catalogPageFull.body.items;\n }\n return coerceArray(items).map(({ catalogEntry }) => catalogEntry);\n }\n\n async getReleases(\n http: Http,\n registryUrl: string,\n feedUrl: string,\n pkgName: string,\n ): Promise<ReleaseResult | null> {\n const baseUrl = feedUrl.replace(regEx(/\\/*$/), '');\n const url = `${baseUrl}/${pkgName.toLowerCase()}/index.json`;\n const packageRegistration = await http.getJson(url, PackageRegistration);\n const catalogPages = packageRegistration.body.items;\n const catalogPagesQueue = catalogPages.map(\n (page) => (): Promise<CatalogEntry[]> => this.getCatalogEntry(http, page),\n );\n const catalogEntries = (await p.all(catalogPagesQueue))\n .flat()\n .sort((a, b) => sortNugetVersions(a.version, b.version));\n\n let homepage: string | null = null;\n let latestStable: string | null = null;\n let nupkgUrl: string | null = null;\n const releases = catalogEntries.map(\n ({\n version,\n published,\n projectUrl,\n listed,\n packageContent,\n deprecation,\n }) => {\n const release: Release = { version: removeBuildMeta(version) };\n const releaseTimestamp = asTimestamp(published);\n if (releaseTimestamp) {\n release.releaseTimestamp = releaseTimestamp;\n }\n if (\n versioning.isValid(version) &&\n versioning.isStable(version) &&\n listed\n ) {\n latestStable = removeBuildMeta(version);\n homepage = projectUrl ? massageUrl(projectUrl) : homepage;\n nupkgUrl = massageUrl(packageContent);\n }\n\n if (listed === false || deprecation) {\n release.isDeprecated = true;\n }\n return release;\n },\n );\n\n if (!releases.length) {\n return null;\n }\n\n // istanbul ignore next: only happens when no stable version exists\n if (latestStable === null && catalogPages.length) {\n const last = catalogEntries.pop()!;\n latestStable = removeBuildMeta(last.version);\n homepage ??= last.projectUrl ?? null;\n nupkgUrl ??= massageUrl(last.packageContent);\n }\n\n const dep: ReleaseResult = {\n releases,\n };\n\n if (releases.every((release) => release.isDeprecated === true)) {\n dep.deprecationMessage = this.getDeprecationMessage(pkgName);\n }\n\n try {\n const packageBaseAddress = await this.getResourceUrl(\n http,\n registryUrl,\n 'PackageBaseAddress',\n );\n if (isNonEmptyString(packageBaseAddress)) {\n const nuspecUrl = `${ensureTrailingSlash(\n packageBaseAddress,\n )}${pkgName.toLowerCase()}/${\n latestStable\n }/${pkgName.toLowerCase()}.nuspec`;\n const metaresult = await http.getText(nuspecUrl, {\n cacheProvider: memCacheProvider,\n });\n const nuspec = new XmlDocument(metaresult.body);\n const releaseNotes = nuspec.valueWithPath('metadata.releaseNotes');\n if (releaseNotes) {\n dep.changelogContent = releaseNotes;\n }\n const sourceUrl = nuspec.valueWithPath('metadata.repository@url');\n if (sourceUrl) {\n dep.sourceUrl = massageUrl(sourceUrl);\n }\n } else if (nupkgUrl) {\n const sourceUrl = await this.getSourceUrlFromNupkg(\n http,\n registryUrl,\n pkgName,\n latestStable,\n nupkgUrl,\n );\n if (sourceUrl) {\n dep.sourceUrl = massageUrl(sourceUrl);\n logger.debug(`Determined sourceUrl ${sourceUrl} from ${nupkgUrl}`);\n }\n }\n } catch (err) {\n // istanbul ignore if: not easy testable with nock\n if (err instanceof ExternalHostError) {\n throw err;\n }\n // ignore / silence 404. Seen on proget, if remote connector is used and package is not yet cached\n if (err instanceof HttpError && err.response?.statusCode === 404) {\n logger.debug(\n { registryUrl, pkgName, pkgVersion: latestStable },\n `package manifest (.nuspec) not found`,\n );\n } else {\n logger.debug(\n { err, registryUrl, pkgName, pkgVersion: latestStable },\n `Cannot obtain sourceUrl`,\n );\n }\n }\n\n if (homepage) {\n // only assign if not assigned\n dep.sourceUrl ??= homepage;\n dep.homepage ??= homepage;\n }\n\n return dep;\n }\n\n private async _getSourceUrlFromNupkg(\n http: Http,\n _registryUrl: string,\n packageName: string,\n packageVersion: string | null,\n nupkgUrl: string,\n ): Promise<string | null> {\n /* v8 ignore next 4 */\n if (!getEnv().RENOVATE_X_NUGET_DOWNLOAD_NUPKGS) {\n logger.once.debug('RENOVATE_X_NUGET_DOWNLOAD_NUPKGS is not set');\n return null;\n }\n const cacheDir = await ensureCacheDir('nuget');\n const nupkgFile = upath.join(\n cacheDir,\n `${packageName}.${packageVersion}.nupkg`,\n );\n const nupkgContentsDir = upath.join(\n cacheDir,\n `${packageName}.${packageVersion}`,\n );\n const readStream = http.stream(nupkgUrl);\n try {\n const writeStream = fs.createCacheWriteStream(nupkgFile);\n await fs.pipeline(readStream, writeStream);\n const zip = new AdmZip(nupkgFile);\n zip.extractAllTo(nupkgContentsDir);\n const nuspecFile = upath.join(nupkgContentsDir, `${packageName}.nuspec`);\n const nuspec = new XmlDocument(\n await fs.readCacheFile(nuspecFile, 'utf8'),\n );\n return nuspec.valueWithPath('metadata.repository@url') ?? null;\n } finally {\n await fs.rmCache(nupkgFile);\n await fs.rmCache(nupkgContentsDir);\n }\n }\n\n getSourceUrlFromNupkg(\n http: Http,\n registryUrl: string,\n packageName: string,\n packageVersion: string | null,\n nupkgUrl: string,\n ): Promise<string | null> {\n return withCache(\n {\n namespace: NugetV3Api.cacheNamespace,\n key: `source-url:${registryUrl}:${packageName}`,\n ttlMinutes: 10080, // 1 week\n },\n () =>\n this._getSourceUrlFromNupkg(\n http,\n registryUrl,\n packageName,\n packageVersion,\n nupkgUrl,\n ),\n );\n }\n\n getDeprecationMessage(packageName: string): string {\n return `The package \\`${packageName}\\` is deprecated.`;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AA8BA,IAAa,aAAb,MAAa,WAAW;CACtB,OAAgB,iBAAiB;CAEjC,MAAM,eACJ,MACA,KACA,eAAe,wBACS;EAExB,MAAM,iBAAiB,GAAG,IAAI,GAAG;EACjC,MAAM,eAAe,MAAMA,IACzB,WAAW,gBACX,cACF;;EAGA,IAAI,cACF,OAAO;EAET,IAAI;EACJ,IAAI;GACF,MAAM,mBAAmB;GACzB,mBAAmB,MAAMA,IACvB,WAAW,gBACX,gBACF;GACA,IAAI,CAAC,kBAAkB;IACrB,oBACE,MAAM,KAAK,QACT,KACA,EAAE,eAAe,iBAAiB,GAClC,gBACF,GACA;IACF,MAAMC,IACJ,WAAW,gBACX,kBACA,kBACA,IACF;GACF;GAEA,MAAM,WAAW,iBAAiB,UAC/B,KAAK,EAAE,OAAO,WAAW,SAAS,SAAS;IAC1C;IACA,MAAM,GAAG,MAAM,GAAG,GAAG,MAAM;IAC3B,SAAS,GAAG,MAAM,GAAG,GAAG,IAAI;GAC9B,EAAE,EACD,QACE,EAAE,MAAM,cAAc,SAAS,gBAAgB,OAAO,MAAM,OAAO,CACtE,EACC,MAAM,GAAG,MACR,EAAE,WAAW,EAAE,UACX,OAAO,QAAQ,EAAE,SAAS,EAAE,OAAO,gDACM,CAC/C;GAEF,IAAI,SAAS,WAAW,GAAG;IACzB,MAAMA,IACJ,WAAW,gBACX,gBACA,MACA,EACF;IACA,OAAO,MACL;KAAE;KAAK;IAAiB,GACxB,MAAM,aAAa,gBACrB;IACA,OAAO;GACT;GAEA,MAAM,EAAE,WAAW,YAAY,SAAS,IAAI;;GAG5C,IACE,iBAAiB,0BACjB,WACA,CAAC,QAAQ,WAAW,QAAQ,KAC5B,CAAC,OAAO,UAAU,SAAS,QAAQ,GAEnC,OAAO,KACL;IAAE;IAAK;GAAQ,GACf,uDACF;GAGF,MAAMA,IACJ,WAAW,gBACX,gBACA,WACA,EACF;GACA,OAAO;EACT,SAAS,KAAK;;GAEZ,IAAI,eAAe,mBACjB,MAAM;GAER,OAAO,MACL;IAAE;IAAK;IAAK;GAAiB,GAC7B,qCAAqC,cACvC;GACA,OAAO;EACT;CACF;CAEA,MAAM,gBACJ,MACA,aACyB;EACzB,IAAI,QAAQ,YAAY;EACxB,IAAI,CAAC,OAAO;GACV,MAAM,MAAM,YAAY;GACxB,IAAI,CAAC,KACH,OAAO,CAAC;GAGV,SAAQ,MADsB,KAAK,QAAQ,KAAK,WAAW,GACnC,KAAK;EAC/B;EACA,OAAO,YAAY,KAAK,EAAE,KAAK,EAAE,mBAAmB,YAAY;CAClE;CAEA,MAAM,YACJ,MACA,aACA,SACA,SAC+B;EAE/B,MAAM,MAAM,GADI,QAAQ,QAAQ,MAAM,MAAM,GAAG,EAC1B,EAAE,GAAG,QAAQ,YAAY,EAAE;EAEhD,MAAM,gBAAe,MADa,KAAK,QAAQ,KAAK,mBAAmB,GAC9B,KAAK;EAI9C,MAAM,kBAAkB,MAAMC,IAHJ,aAAa,KACpC,eAAwC,KAAK,gBAAgB,MAAM,IAAI,CAEtC,CAAiB,GAClD,KAAK,EACL,MAAM,GAAG,MAAM,kBAAkB,EAAE,SAAS,EAAE,OAAO,CAAC;EAEzD,IAAI,WAA0B;EAC9B,IAAI,eAA8B;EAClC,IAAI,WAA0B;EAC9B,MAAM,WAAW,eAAe,KAC7B,EACC,SACA,WACA,YACA,QACA,gBACA,kBACI;GACJ,MAAM,UAAmB,EAAE,SAAS,gBAAgB,OAAO,EAAE;GAC7D,MAAM,mBAAmB,YAAY,SAAS;GAC9C,IAAI,kBACF,QAAQ,mBAAmB;GAE7B,IACEC,IAAW,QAAQ,OAAO,KAC1BA,IAAW,SAAS,OAAO,KAC3B,QACA;IACA,eAAe,gBAAgB,OAAO;IACtC,WAAW,aAAa,WAAW,UAAU,IAAI;IACjD,WAAW,WAAW,cAAc;GACtC;GAEA,IAAI,WAAW,SAAS,aACtB,QAAQ,eAAe;GAEzB,OAAO;EACT,CACF;EAEA,IAAI,CAAC,SAAS,QACZ,OAAO;;EAIT,IAAI,iBAAiB,QAAQ,aAAa,QAAQ;GAChD,MAAM,OAAO,eAAe,IAAI;GAChC,eAAe,gBAAgB,KAAK,OAAO;GAC3C,aAAa,KAAK,cAAc;GAChC,aAAa,WAAW,KAAK,cAAc;EAC7C;EAEA,MAAM,MAAqB,EACzB,SACF;EAEA,IAAI,SAAS,OAAO,YAAY,QAAQ,iBAAiB,IAAI,GAC3D,IAAI,qBAAqB,KAAK,sBAAsB,OAAO;EAG7D,IAAI;GACF,MAAM,qBAAqB,MAAM,KAAK,eACpC,MACA,aACA,oBACF;GACA,IAAI,iBAAiB,kBAAkB,GAAG;IACxC,MAAM,YAAY,GAAG,oBACnB,kBACF,IAAI,QAAQ,YAAY,EAAE,GACxB,aACD,GAAG,QAAQ,YAAY,EAAE;IAI1B,MAAM,SAAS,IAAI,aAAY,MAHN,KAAK,QAAQ,WAAW,EAC/C,eAAe,iBACjB,CAAC,GACyC,IAAI;IAC9C,MAAM,eAAe,OAAO,cAAc,uBAAuB;IACjE,IAAI,cACF,IAAI,mBAAmB;IAEzB,MAAM,YAAY,OAAO,cAAc,yBAAyB;IAChE,IAAI,WACF,IAAI,YAAY,WAAW,SAAS;GAExC,OAAO,IAAI,UAAU;IACnB,MAAM,YAAY,MAAM,KAAK,sBAC3B,MACA,aACA,SACA,cACA,QACF;IACA,IAAI,WAAW;KACb,IAAI,YAAY,WAAW,SAAS;KACpC,OAAO,MAAM,wBAAwB,UAAU,QAAQ,UAAU;IACnE;GACF;EACF,SAAS,KAAK;;GAEZ,IAAI,eAAe,mBACjB,MAAM;GAGR,IAAI,eAAeC,gBAAa,IAAI,UAAU,eAAe,KAC3D,OAAO,MACL;IAAE;IAAa;IAAS,YAAY;GAAa,GACjD,sCACF;QAEA,OAAO,MACL;IAAE;IAAK;IAAa;IAAS,YAAY;GAAa,GACtD,yBACF;EAEJ;EAEA,IAAI,UAAU;GAEZ,IAAI,cAAc;GAClB,IAAI,aAAa;EACnB;EAEA,OAAO;CACT;CAEA,MAAc,uBACZ,MACA,cACA,aACA,gBACA,UACwB;;EAExB,IAAI,CAAC,OAAO,EAAE,kCAAkC;GAC9C,OAAO,KAAK,MAAM,6CAA6C;GAC/D,OAAO;EACT;EACA,MAAM,WAAW,MAAM,eAAe,OAAO;EAC7C,MAAM,YAAY,MAAM,KACtB,UACA,GAAG,YAAY,GAAG,eAAe,OACnC;EACA,MAAM,mBAAmB,MAAM,KAC7B,UACA,GAAG,YAAY,GAAG,gBACpB;EACA,MAAM,aAAa,KAAK,OAAO,QAAQ;EACvC,IAAI;GAEF,MAAME,SAAY,YADED,uBAA0B,SAChB,CAAW;GAEzC,IADgB,OAAO,SACrB,EAAE,aAAa,gBAAgB;GAKjC,OAAO,IAHY,YACjB,MAAME,cAFW,MAAM,KAAK,kBAAkB,GAAG,YAAY,QAEtC,GAAY,MAAM,CAE/B,EAAE,cAAc,yBAAyB,KAAK;EAC5D,UAAU;GACR,MAAMC,QAAW,SAAS;GAC1B,MAAMA,QAAW,gBAAgB;EACnC;CACF;CAEA,sBACE,MACA,aACA,aACA,gBACA,UACwB;EACxB,OAAO,UACL;GACE,WAAW,WAAW;GACtB,KAAK,cAAc,YAAY,GAAG;GAClC,YAAY;EACd,SAEE,KAAK,uBACH,MACA,aACA,aACA,gBACA,QACF,CACJ;CACF;CAEA,sBAAsB,aAA6B;EACjD,OAAO,iBAAiB,YAAY;CACtC;AACF"}
@@ -1,6 +1,6 @@
1
- import { asTimestamp } from "../../../util/timestamp.js";
2
1
  import { Datasource } from "../datasource.js";
3
2
  import { PUPPET_FORGE } from "./common.js";
3
+ import { PuppetModule } from "./schema.js";
4
4
  //#region lib/modules/datasource/puppet-forge/index.ts
5
5
  var PuppetForgeDatasource = class PuppetForgeDatasource extends Datasource {
6
6
  static id = "puppet-forge";
@@ -12,27 +12,14 @@ var PuppetForgeDatasource = class PuppetForgeDatasource extends Datasource {
12
12
  releaseTimestampNote = "The release timestamp is determined from the `created_at` field from the response.";
13
13
  async getReleases({ packageName, registryUrl }) {
14
14
  const url = `${registryUrl}/v3/modules/${packageName.replace("/", "-")}?exclude_fields=current_release`;
15
- let module;
15
+ let result;
16
16
  try {
17
- module = (await this.http.getJsonUnchecked(url)).body;
17
+ result = (await this.http.getJson(url, PuppetModule)).body;
18
18
  } catch (err) {
19
19
  this.handleGenericErrors(err);
20
20
  }
21
- const releases = module?.releases?.map((release) => ({
22
- version: release.version,
23
- downloadUrl: release.file_uri,
24
- releaseTimestamp: asTimestamp(release.created_at),
25
- registryUrl
26
- }));
27
- if (!releases?.length) return null;
28
- return PuppetForgeDatasource.createReleaseResult(releases, module);
29
- }
30
- static createReleaseResult(releases, module) {
31
- const result = {
32
- releases,
33
- homepage: module.homepage_url
34
- };
35
- if (module.deprecated_for) result.deprecationMessage = module.deprecated_for;
21
+ if (!result.releases.length) return null;
22
+ for (const release of result.releases) release.registryUrl = registryUrl;
36
23
  return result;
37
24
  }
38
25
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../../lib/modules/datasource/puppet-forge/index.ts"],"sourcesContent":["import { asTimestamp } from '../../../util/timestamp.ts';\nimport { Datasource } from '../datasource.ts';\nimport type { GetReleasesConfig, Release, ReleaseResult } from '../types.ts';\nimport { PUPPET_FORGE } from './common.ts';\nimport type { PuppetModule } from './types.ts';\n\nexport class PuppetForgeDatasource extends Datasource {\n static id = 'puppet-forge';\n\n constructor() {\n super(PuppetForgeDatasource.id);\n }\n\n override readonly defaultRegistryUrls = [PUPPET_FORGE];\n\n override readonly releaseTimestampSupport = true;\n override readonly releaseTimestampNote =\n 'The release timestamp is determined from the `created_at` field from the response.';\n\n async getReleases({\n packageName,\n registryUrl,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n // https://forgeapi.puppet.com\n const moduleSlug = packageName.replace('/', '-');\n // TODO: types (#22198)\n const url = `${registryUrl}/v3/modules/${moduleSlug}?exclude_fields=current_release`;\n\n let module: PuppetModule;\n\n try {\n const response = await this.http.getJsonUnchecked<PuppetModule>(url);\n module = response.body;\n } catch (err) {\n this.handleGenericErrors(err);\n }\n\n const releases: Release[] = module?.releases?.map((release) => ({\n version: release.version,\n downloadUrl: release.file_uri,\n releaseTimestamp: asTimestamp(release.created_at),\n registryUrl,\n }));\n\n if (!releases?.length) {\n return null;\n }\n\n return PuppetForgeDatasource.createReleaseResult(releases, module);\n }\n\n static createReleaseResult(\n releases: Release[],\n module: PuppetModule,\n ): ReleaseResult {\n const result: ReleaseResult = {\n releases,\n // the homepage url in the fixtures is a github repo, we can use this as sourceUrl\n homepage: module.homepage_url,\n };\n\n if (module.deprecated_for) {\n result.deprecationMessage = module.deprecated_for;\n }\n\n return result;\n }\n}\n"],"mappings":";;;;AAMA,IAAa,wBAAb,MAAa,8BAA8B,WAAW;CACpD,OAAO,KAAK;CAEZ,cAAc;EACZ,MAAM,sBAAsB,EAAE;CAChC;CAEA,sBAAwC,CAAC,YAAY;CAErD,0BAA4C;CAC5C,uBACE;CAEF,MAAM,YAAY,EAChB,aACA,eACmD;EAInD,MAAM,MAAM,GAAG,YAAY,cAFR,YAAY,QAAQ,KAAK,GAEM,EAAE;EAEpD,IAAI;EAEJ,IAAI;GAEF,UAAS,MADc,KAAK,KAAK,iBAA+B,GAAG,GACjD;EACpB,SAAS,KAAK;GACZ,KAAK,oBAAoB,GAAG;EAC9B;EAEA,MAAM,WAAsB,QAAQ,UAAU,KAAK,aAAa;GAC9D,SAAS,QAAQ;GACjB,aAAa,QAAQ;GACrB,kBAAkB,YAAY,QAAQ,UAAU;GAChD;EACF,EAAE;EAEF,IAAI,CAAC,UAAU,QACb,OAAO;EAGT,OAAO,sBAAsB,oBAAoB,UAAU,MAAM;CACnE;CAEA,OAAO,oBACL,UACA,QACe;EACf,MAAM,SAAwB;GAC5B;GAEA,UAAU,OAAO;EACnB;EAEA,IAAI,OAAO,gBACT,OAAO,qBAAqB,OAAO;EAGrC,OAAO;CACT;AACF"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../../lib/modules/datasource/puppet-forge/index.ts"],"sourcesContent":["import { Datasource } from '../datasource.ts';\nimport type { GetReleasesConfig, ReleaseResult } from '../types.ts';\nimport { PUPPET_FORGE } from './common.ts';\nimport { PuppetModule } from './schema.ts';\n\nexport class PuppetForgeDatasource extends Datasource {\n static id = 'puppet-forge';\n\n constructor() {\n super(PuppetForgeDatasource.id);\n }\n\n override readonly defaultRegistryUrls = [PUPPET_FORGE];\n\n override readonly releaseTimestampSupport = true;\n override readonly releaseTimestampNote =\n 'The release timestamp is determined from the `created_at` field from the response.';\n\n async getReleases({\n packageName,\n registryUrl,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n // https://forgeapi.puppet.com\n const moduleSlug = packageName.replace('/', '-');\n const url = `${registryUrl}/v3/modules/${moduleSlug}?exclude_fields=current_release`;\n\n let result: ReleaseResult;\n\n try {\n const response = await this.http.getJson(url, PuppetModule);\n result = response.body;\n } catch (err) {\n this.handleGenericErrors(err);\n }\n\n if (!result.releases.length) {\n return null;\n }\n\n for (const release of result.releases) {\n release.registryUrl = registryUrl;\n }\n\n return result;\n }\n}\n"],"mappings":";;;;AAKA,IAAa,wBAAb,MAAa,8BAA8B,WAAW;CACpD,OAAO,KAAK;CAEZ,cAAc;EACZ,MAAM,sBAAsB,EAAE;CAChC;CAEA,sBAAwC,CAAC,YAAY;CAErD,0BAA4C;CAC5C,uBACE;CAEF,MAAM,YAAY,EAChB,aACA,eACmD;EAGnD,MAAM,MAAM,GAAG,YAAY,cADR,YAAY,QAAQ,KAAK,GACM,EAAE;EAEpD,IAAI;EAEJ,IAAI;GAEF,UAAS,MADc,KAAK,KAAK,QAAQ,KAAK,YAAY,GACxC;EACpB,SAAS,KAAK;GACZ,KAAK,oBAAoB,GAAG;EAC9B;EAEA,IAAI,CAAC,OAAO,SAAS,QACnB,OAAO;EAGT,KAAK,MAAM,WAAW,OAAO,UAC3B,QAAQ,cAAc;EAGxB,OAAO;CACT;AACF"}
@@ -0,0 +1,28 @@
1
+ import { LooseArray } from "../../../util/schema-utils/index.js";
2
+ import { MaybeTimestamp } from "../../../util/timestamp.js";
3
+ import { z } from "zod/v4";
4
+ //#region lib/modules/datasource/puppet-forge/schema.ts
5
+ const PuppetReleaseAbbreviated = z.object({
6
+ version: z.string(),
7
+ created_at: MaybeTimestamp,
8
+ file_uri: z.string().optional().nullable()
9
+ }).transform(({ version, created_at, file_uri }) => {
10
+ const release = { version };
11
+ if (file_uri) release.downloadUrl = file_uri;
12
+ if (created_at) release.releaseTimestamp = created_at;
13
+ return release;
14
+ });
15
+ const PuppetModule = z.object({
16
+ releases: LooseArray(PuppetReleaseAbbreviated).default([]),
17
+ homepage_url: z.string().optional().nullable(),
18
+ deprecated_for: z.string().optional().nullable()
19
+ }).transform((module) => {
20
+ const result = { releases: module.releases };
21
+ if (module.homepage_url) result.homepage = module.homepage_url;
22
+ if (module.deprecated_for) result.deprecationMessage = module.deprecated_for;
23
+ return result;
24
+ });
25
+ //#endregion
26
+ export { PuppetModule };
27
+
28
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","names":[],"sources":["../../../../lib/modules/datasource/puppet-forge/schema.ts"],"sourcesContent":["import { z } from 'zod/v4';\nimport { LooseArray } from '../../../util/schema-utils/index.ts';\nimport { MaybeTimestamp } from '../../../util/timestamp.ts';\nimport type { Release, ReleaseResult } from '../types.ts';\n\nexport const PuppetReleaseAbbreviated = z\n .object({\n version: z.string(),\n created_at: MaybeTimestamp,\n file_uri: z.string().optional().nullable(),\n })\n .transform(({ version, created_at, file_uri }): Release => {\n const release: Release = { version };\n if (file_uri) {\n release.downloadUrl = file_uri;\n }\n if (created_at) {\n release.releaseTimestamp = created_at;\n }\n return release;\n });\n\nexport const PuppetModule = z\n .object({\n releases: LooseArray(PuppetReleaseAbbreviated).default([]),\n homepage_url: z.string().optional().nullable(),\n deprecated_for: z.string().optional().nullable(),\n })\n .transform((module): ReleaseResult => {\n const result: ReleaseResult = { releases: module.releases };\n if (module.homepage_url) {\n result.homepage = module.homepage_url;\n }\n if (module.deprecated_for) {\n result.deprecationMessage = module.deprecated_for;\n }\n return result;\n });\n"],"mappings":";;;;AAKA,MAAa,2BAA2B,EACrC,OAAO;CACN,SAAS,EAAE,OAAO;CAClB,YAAY;CACZ,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAC3C,CAAC,EACA,WAAW,EAAE,SAAS,YAAY,eAAwB;CACzD,MAAM,UAAmB,EAAE,QAAQ;CACnC,IAAI,UACF,QAAQ,cAAc;CAExB,IAAI,YACF,QAAQ,mBAAmB;CAE7B,OAAO;AACT,CAAC;AAEH,MAAa,eAAe,EACzB,OAAO;CACN,UAAU,WAAW,wBAAwB,EAAE,QAAQ,CAAC,CAAC;CACzD,cAAc,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;CAC7C,gBAAgB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AACjD,CAAC,EACA,WAAW,WAA0B;CACpC,MAAM,SAAwB,EAAE,UAAU,OAAO,SAAS;CAC1D,IAAI,OAAO,cACT,OAAO,WAAW,OAAO;CAE3B,IAAI,OAAO,gBACT,OAAO,qBAAqB,OAAO;CAErC,OAAO;AACT,CAAC"}
@@ -4,6 +4,7 @@ import { getQueryString, joinUrlParts } from "../../../util/url.js";
4
4
  import { ExternalHostError } from "../../../types/errors/external-host-error.js";
5
5
  import { withCache } from "../../../util/cache/package/with-cache.js";
6
6
  import { Datasource } from "../datasource.js";
7
+ import { RepologyPackages } from "./schema.js";
7
8
  import { isNonEmptyArray } from "@sindresorhus/is";
8
9
  //#region lib/modules/datasource/repology/index.ts
9
10
  const packageTypes = ["binname", "srcname"];
@@ -26,7 +27,7 @@ var RepologyDatasource = class RepologyDatasource extends Datasource {
26
27
  }
27
28
  async queryPackages(url) {
28
29
  try {
29
- return (await this.http.getJsonUnchecked(url)).body;
30
+ return (await this.http.getJson(url, RepologyPackages)).body;
30
31
  } catch (err) {
31
32
  if (err.statusCode === 404) return [];
32
33
  throw err;
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../../lib/modules/datasource/repology/index.ts"],"sourcesContent":["import { isNonEmptyArray } from '@sindresorhus/is';\nimport { HOST_DISABLED } from '../../../constants/error-messages.ts';\nimport { logger } from '../../../logger/index.ts';\nimport { ExternalHostError } from '../../../types/errors/external-host-error.ts';\nimport { withCache } from '../../../util/cache/package/with-cache.ts';\nimport { getQueryString, joinUrlParts } from '../../../util/url.ts';\nimport { Datasource } from '../datasource.ts';\nimport type { GetReleasesConfig, ReleaseResult } from '../types.ts';\nimport type { RepologyPackage, RepologyPackageType } from './types.ts';\n\nconst packageTypes: RepologyPackageType[] = ['binname', 'srcname'];\n\nfunction findPackageInResponse(\n response: RepologyPackage[],\n repoName: string,\n pkgName: string,\n types: RepologyPackageType[],\n): RepologyPackage[] | null {\n const repoPackages = response.filter((pkg) => pkg.repo === repoName);\n\n if (repoPackages.length === 0) {\n // no packages associated with repoName\n return null;\n }\n\n if (repoPackages.length === 1) {\n // repo contains exactly one package, so we can return them safely\n return repoPackages;\n }\n\n // In some cases Repology bundles multiple packages into a single project, which might result in ambiguous results.\n // We need to do additional filtering by matching allowed package types passed as params with package description.\n // Remaining packages are the one we are looking for\n const packagesWithType = repoPackages.filter((pkg) => {\n for (const pkgType of types) {\n if (pkg[pkgType] && pkg[pkgType] === pkgName) {\n return true;\n }\n }\n return false;\n });\n\n return packagesWithType.length > 0 ? packagesWithType : null;\n}\n\nexport class RepologyDatasource extends Datasource {\n static readonly id = 'repology';\n\n override readonly defaultRegistryUrls = ['https://repology.org/'];\n\n override readonly registryStrategy = 'hunt';\n\n constructor() {\n super(RepologyDatasource.id);\n }\n\n private async queryPackages(url: string): Promise<RepologyPackage[]> {\n try {\n const res = await this.http.getJsonUnchecked<RepologyPackage[]>(url);\n return res.body;\n } catch (err) {\n if (err.statusCode === 404) {\n // Return an array here because the api does not return proper http codes\n // and instead of an 404 error an empty array with code 200 is returned\n // When querying the resolver 404 is thrown if package could not be resolved\n // and 403 if the repo is not supported\n // 403 is handled later because in this case we are trying the API\n return [];\n }\n\n throw err;\n }\n }\n\n private async queryPackagesViaResolver(\n registryUrl: string,\n repoName: string,\n packageName: string,\n packageType: RepologyPackageType,\n ): Promise<RepologyPackage[]> {\n const query = getQueryString({\n repo: repoName,\n name_type: packageType,\n target_page: 'api_v1_project',\n noautoresolve: 'on',\n name: packageName,\n });\n\n // Retrieve list of packages by looking up Repology project\n const packages = await this.queryPackages(\n joinUrlParts(registryUrl, `tools/project-by?${query}`),\n );\n\n return packages;\n }\n\n private async queryPackagesViaAPI(\n registryUrl: string,\n packageName: string,\n ): Promise<RepologyPackage[]> {\n // Directly query the package via the API. This will only work if `packageName` has the\n // same name as the repology project\n const packages = await this.queryPackages(\n joinUrlParts(registryUrl, `api/v1/project`, packageName),\n );\n\n return packages;\n }\n\n private async _queryPackage(\n registryUrl: string,\n repoName: string,\n pkgName: string,\n ): Promise<RepologyPackage[] | undefined> {\n let response: RepologyPackage[];\n // Try getting the packages from tools/project-by first for type binname and\n // afterwards for srcname. This needs to be done first, because some packages\n // resolve to repology projects which have a different name than the package\n // e.g. `pulseaudio-utils` resolves to project `pulseaudio`, BUT there is also\n // a project called `pulseaudio-utils` but it does not contain the package we\n // are looking for.\n try {\n for (const pkgType of packageTypes) {\n response = await this.queryPackagesViaResolver(\n registryUrl,\n repoName,\n pkgName,\n pkgType,\n );\n\n if (response) {\n const pkg = findPackageInResponse(response, repoName, pkgName, [\n pkgType,\n ]);\n if (isNonEmptyArray(pkg)) {\n // exit immediately if package found\n return pkg;\n }\n }\n }\n } catch (err) {\n if (err.statusCode === 403) {\n logger.debug(\n { repoName, pkgName },\n 'Repology does not support tools/project-by lookups for repository. Will try direct API access now',\n );\n\n // If the repository is not supported in tools/project-by we try directly accessing the\n // API. This will support all repositories but requires that the project name is equal to the\n // package name. This won't be always the case but for a good portion we might be able to resolve\n // the package this way.\n response = await this.queryPackagesViaAPI(registryUrl, pkgName);\n const pkg = findPackageInResponse(\n response,\n repoName,\n pkgName,\n packageTypes,\n );\n if (isNonEmptyArray(pkg)) {\n // exit immediately if package found\n return pkg;\n }\n } else if (err.statusCode === 300) {\n logger.warn(\n { repoName, pkgName },\n 'Ambiguous redirection from package name to project name in Repology. Skipping this package',\n );\n return undefined;\n }\n\n throw err;\n }\n\n logger.debug(\n { repoName, pkgName },\n 'Repository or package not found on Repology',\n );\n\n return undefined;\n }\n\n queryPackage(\n registryUrl: string,\n repoName: string,\n pkgName: string,\n ): Promise<RepologyPackage[] | undefined> {\n return withCache(\n {\n ttlMinutes: 60,\n namespace: `datasource-${RepologyDatasource.id}`,\n key: joinUrlParts(registryUrl, repoName, pkgName),\n },\n () => this._queryPackage(registryUrl, repoName, pkgName),\n );\n }\n\n async getReleases({\n packageName,\n registryUrl,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n /* v8 ignore next 3 -- should never happen */\n if (!registryUrl) {\n return null;\n }\n // Ensure lookup name contains both repository and package\n const [repoName, pkgName] = packageName.split('/', 2);\n if (!repoName || !pkgName) {\n throw new ExternalHostError(\n new Error(\n 'Repology lookup name must contain repository and package separated by slash (<repo>/<pkg>)',\n ),\n );\n }\n\n logger.trace(`repology.getReleases(${repoName}, ${pkgName})`);\n try {\n // Try to retrieve (cached) package information from Repology\n const pkg = await this.queryPackage(registryUrl, repoName, pkgName);\n if (!pkg) {\n return null;\n }\n\n // Always prefer origversion if available, otherwise default to version\n // This is required as source packages usually have no origversion\n const releases = pkg.map((item) => ({\n version: item.origversion ?? item.version,\n }));\n return { releases };\n } catch (err) {\n if (err.message === HOST_DISABLED) {\n logger.trace({ packageName, err }, 'Host disabled');\n } else {\n logger.warn(\n { packageName, err },\n 'Repology lookup failed with unexpected error',\n );\n }\n\n throw new ExternalHostError(err);\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../../lib/modules/datasource/repology/index.ts"],"sourcesContent":["import { isNonEmptyArray } from '@sindresorhus/is';\nimport { HOST_DISABLED } from '../../../constants/error-messages.ts';\nimport { logger } from '../../../logger/index.ts';\nimport { ExternalHostError } from '../../../types/errors/external-host-error.ts';\nimport { withCache } from '../../../util/cache/package/with-cache.ts';\nimport { getQueryString, joinUrlParts } from '../../../util/url.ts';\nimport { Datasource } from '../datasource.ts';\nimport type { GetReleasesConfig, ReleaseResult } from '../types.ts';\nimport { type RepologyPackage, RepologyPackages } from './schema.ts';\nimport type { RepologyPackageType } from './types.ts';\n\nconst packageTypes: RepologyPackageType[] = ['binname', 'srcname'];\n\nfunction findPackageInResponse(\n response: RepologyPackage[],\n repoName: string,\n pkgName: string,\n types: RepologyPackageType[],\n): RepologyPackage[] | null {\n const repoPackages = response.filter((pkg) => pkg.repo === repoName);\n\n if (repoPackages.length === 0) {\n // no packages associated with repoName\n return null;\n }\n\n if (repoPackages.length === 1) {\n // repo contains exactly one package, so we can return them safely\n return repoPackages;\n }\n\n // In some cases Repology bundles multiple packages into a single project, which might result in ambiguous results.\n // We need to do additional filtering by matching allowed package types passed as params with package description.\n // Remaining packages are the one we are looking for\n const packagesWithType = repoPackages.filter((pkg) => {\n for (const pkgType of types) {\n if (pkg[pkgType] && pkg[pkgType] === pkgName) {\n return true;\n }\n }\n return false;\n });\n\n return packagesWithType.length > 0 ? packagesWithType : null;\n}\n\nexport class RepologyDatasource extends Datasource {\n static readonly id = 'repology';\n\n override readonly defaultRegistryUrls = ['https://repology.org/'];\n\n override readonly registryStrategy = 'hunt';\n\n constructor() {\n super(RepologyDatasource.id);\n }\n\n private async queryPackages(url: string): Promise<RepologyPackage[]> {\n try {\n const res = await this.http.getJson(url, RepologyPackages);\n return res.body;\n } catch (err) {\n if (err.statusCode === 404) {\n // Return an array here because the api does not return proper http codes\n // and instead of an 404 error an empty array with code 200 is returned\n // When querying the resolver 404 is thrown if package could not be resolved\n // and 403 if the repo is not supported\n // 403 is handled later because in this case we are trying the API\n return [];\n }\n\n throw err;\n }\n }\n\n private async queryPackagesViaResolver(\n registryUrl: string,\n repoName: string,\n packageName: string,\n packageType: RepologyPackageType,\n ): Promise<RepologyPackage[]> {\n const query = getQueryString({\n repo: repoName,\n name_type: packageType,\n target_page: 'api_v1_project',\n noautoresolve: 'on',\n name: packageName,\n });\n\n // Retrieve list of packages by looking up Repology project\n const packages = await this.queryPackages(\n joinUrlParts(registryUrl, `tools/project-by?${query}`),\n );\n\n return packages;\n }\n\n private async queryPackagesViaAPI(\n registryUrl: string,\n packageName: string,\n ): Promise<RepologyPackage[]> {\n // Directly query the package via the API. This will only work if `packageName` has the\n // same name as the repology project\n const packages = await this.queryPackages(\n joinUrlParts(registryUrl, `api/v1/project`, packageName),\n );\n\n return packages;\n }\n\n private async _queryPackage(\n registryUrl: string,\n repoName: string,\n pkgName: string,\n ): Promise<RepologyPackage[] | undefined> {\n let response: RepologyPackage[];\n // Try getting the packages from tools/project-by first for type binname and\n // afterwards for srcname. This needs to be done first, because some packages\n // resolve to repology projects which have a different name than the package\n // e.g. `pulseaudio-utils` resolves to project `pulseaudio`, BUT there is also\n // a project called `pulseaudio-utils` but it does not contain the package we\n // are looking for.\n try {\n for (const pkgType of packageTypes) {\n response = await this.queryPackagesViaResolver(\n registryUrl,\n repoName,\n pkgName,\n pkgType,\n );\n\n if (response) {\n const pkg = findPackageInResponse(response, repoName, pkgName, [\n pkgType,\n ]);\n if (isNonEmptyArray(pkg)) {\n // exit immediately if package found\n return pkg;\n }\n }\n }\n } catch (err) {\n if (err.statusCode === 403) {\n logger.debug(\n { repoName, pkgName },\n 'Repology does not support tools/project-by lookups for repository. Will try direct API access now',\n );\n\n // If the repository is not supported in tools/project-by we try directly accessing the\n // API. This will support all repositories but requires that the project name is equal to the\n // package name. This won't be always the case but for a good portion we might be able to resolve\n // the package this way.\n response = await this.queryPackagesViaAPI(registryUrl, pkgName);\n const pkg = findPackageInResponse(\n response,\n repoName,\n pkgName,\n packageTypes,\n );\n if (isNonEmptyArray(pkg)) {\n // exit immediately if package found\n return pkg;\n }\n } else if (err.statusCode === 300) {\n logger.warn(\n { repoName, pkgName },\n 'Ambiguous redirection from package name to project name in Repology. Skipping this package',\n );\n return undefined;\n }\n\n throw err;\n }\n\n logger.debug(\n { repoName, pkgName },\n 'Repository or package not found on Repology',\n );\n\n return undefined;\n }\n\n queryPackage(\n registryUrl: string,\n repoName: string,\n pkgName: string,\n ): Promise<RepologyPackage[] | undefined> {\n return withCache(\n {\n ttlMinutes: 60,\n namespace: `datasource-${RepologyDatasource.id}`,\n key: joinUrlParts(registryUrl, repoName, pkgName),\n },\n () => this._queryPackage(registryUrl, repoName, pkgName),\n );\n }\n\n async getReleases({\n packageName,\n registryUrl,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n /* v8 ignore next 3 -- should never happen */\n if (!registryUrl) {\n return null;\n }\n // Ensure lookup name contains both repository and package\n const [repoName, pkgName] = packageName.split('/', 2);\n if (!repoName || !pkgName) {\n throw new ExternalHostError(\n new Error(\n 'Repology lookup name must contain repository and package separated by slash (<repo>/<pkg>)',\n ),\n );\n }\n\n logger.trace(`repology.getReleases(${repoName}, ${pkgName})`);\n try {\n // Try to retrieve (cached) package information from Repology\n const pkg = await this.queryPackage(registryUrl, repoName, pkgName);\n if (!pkg) {\n return null;\n }\n\n // Always prefer origversion if available, otherwise default to version\n // This is required as source packages usually have no origversion\n const releases = pkg.map((item) => ({\n version: item.origversion ?? item.version,\n }));\n return { releases };\n } catch (err) {\n if (err.message === HOST_DISABLED) {\n logger.trace({ packageName, err }, 'Host disabled');\n } else {\n logger.warn(\n { packageName, err },\n 'Repology lookup failed with unexpected error',\n );\n }\n\n throw new ExternalHostError(err);\n }\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { LooseArray } from "../../../util/schema-utils/index.js";
2
+ import { z } from "zod/v4";
3
+ const RepologyPackages = LooseArray(z.object({
4
+ repo: z.string(),
5
+ visiblename: z.string(),
6
+ version: z.string(),
7
+ srcname: z.string().nullable().optional(),
8
+ binname: z.string().nullable().optional(),
9
+ origversion: z.string().nullable().optional()
10
+ })).catch([]);
11
+ //#endregion
12
+ export { RepologyPackages };
13
+
14
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","names":[],"sources":["../../../../lib/modules/datasource/repology/schema.ts"],"sourcesContent":["import { z } from 'zod/v4';\nimport { LooseArray } from '../../../util/schema-utils/index.ts';\n\nexport const RepologyPackage = z.object({\n repo: z.string(),\n visiblename: z.string(),\n version: z.string(),\n srcname: z.string().nullable().optional(),\n binname: z.string().nullable().optional(),\n origversion: z.string().nullable().optional(),\n});\n\nexport type RepologyPackage = z.infer<typeof RepologyPackage>;\n\nexport const RepologyPackages = LooseArray(RepologyPackage).catch([]);\n"],"mappings":";;AAcA,MAAa,mBAAmB,WAXD,EAAE,OAAO;CACtC,MAAM,EAAE,OAAO;CACf,aAAa,EAAE,OAAO;CACtB,SAAS,EAAE,OAAO;CAClB,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;CACxC,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;CACxC,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAC9C,CAI2C,CAAe,EAAE,MAAM,CAAC,CAAC"}
@@ -7,7 +7,7 @@ import { withCache } from "../../../util/cache/package/with-cache.js";
7
7
  import { map } from "../../../util/promises.js";
8
8
  import { TerraformDatasource } from "../terraform-module/base.js";
9
9
  import { createSDBackendURL } from "../terraform-module/utils.js";
10
- import { OpenTofuProviderDocsResponse, TerraformProviderV2Response } from "./schema.js";
10
+ import { OpenTofuProviderDocsResponse, TerraformProviderReleaseBackend, TerraformProviderV2Response, TerraformProviderVersions, TerraformRegistryBuildResponse, TerraformRegistryVersions, VersionDetailResponse } from "./schema.js";
11
11
  //#region lib/modules/datasource/terraform-provider/index.ts
12
12
  var TerraformProviderDatasource = class TerraformProviderDatasource extends TerraformDatasource {
13
13
  static id = "terraform-provider";
@@ -82,12 +82,12 @@ var TerraformProviderDatasource = class TerraformProviderDatasource extends Terr
82
82
  async queryProviderRegistry(registryUrl, packageName) {
83
83
  const repository = TerraformProviderDatasource.getRepository({ packageName });
84
84
  const backendURL = createSDBackendURL(registryUrl, "providers.v1", await this.getTerraformServiceDiscoveryResult(registryUrl), `${repository}/versions`);
85
- return { releases: (await this.http.getJsonUnchecked(backendURL)).body.versions.map(({ version }) => ({ version })) };
85
+ return { releases: (await this.http.getJson(backendURL, TerraformProviderVersions)).body.versions.map(({ version }) => ({ version })) };
86
86
  }
87
87
  async queryReleaseBackend(packageName, registryURL) {
88
88
  const backendLookUpName = `terraform-provider-${packageName.replace("hashicorp/", "")}`;
89
89
  const backendURL = joinUrlParts(registryURL, backendLookUpName, `index.json`);
90
- const res = (await this.http.getJsonUnchecked(backendURL)).body;
90
+ const res = (await this.http.getJson(backendURL, TerraformProviderReleaseBackend)).body;
91
91
  return {
92
92
  releases: Object.keys(res.versions).map((version) => ({ version })),
93
93
  sourceUrl: joinUrlParts("https://github.com/terraform-providers", backendLookUpName)
@@ -115,14 +115,14 @@ var TerraformProviderDatasource = class TerraformProviderDatasource extends Terr
115
115
  const serviceDiscovery = await this.getTerraformServiceDiscoveryResult(registryURL);
116
116
  if (!serviceDiscovery) throw new ExternalHostError(/* @__PURE__ */ new Error(`Service discovery not found for ${registryURL}`));
117
117
  const backendURL = createSDBackendURL(registryURL, "providers.v1", serviceDiscovery, repository);
118
- const versionsResponse = (await this.http.getJsonUnchecked(`${backendURL}/versions`)).body;
118
+ const versionsResponse = (await this.http.getJson(`${backendURL}/versions`, TerraformRegistryVersions)).body;
119
119
  if (!versionsResponse.versions) throw new ExternalHostError(/* @__PURE__ */ new Error(`Failed to retrieve version list for ${backendURL}`));
120
120
  const builds = versionsResponse.versions.find((value) => value.version === version);
121
121
  if (!builds) throw new ExternalHostError(/* @__PURE__ */ new Error(`No builds found for ${repository}:${version} on ${registryURL}`));
122
122
  return await map(builds.platforms, async (platform) => {
123
123
  const buildURL = `${backendURL}/${version}/download/${platform.os}/${platform.arch}`;
124
124
  try {
125
- const res = (await this.http.getJsonUnchecked(buildURL)).body;
125
+ const res = (await this.http.getJson(buildURL, TerraformRegistryBuildResponse)).body;
126
126
  return {
127
127
  name: repository,
128
128
  url: res.download_url,
@@ -168,7 +168,7 @@ var TerraformProviderDatasource = class TerraformProviderDatasource extends Terr
168
168
  }, () => this._getZipHashes(zipHashUrl));
169
169
  }
170
170
  async _getReleaseBackendIndex(backendLookUpName, version) {
171
- return (await this.http.getJsonUnchecked(`${TerraformProviderDatasource.hashicorpReleaseUrl}/${backendLookUpName}/${version}/index.json`)).body;
171
+ return (await this.http.getJson(`${TerraformProviderDatasource.hashicorpReleaseUrl}/${backendLookUpName}/${version}/index.json`, VersionDetailResponse)).body;
172
172
  }
173
173
  getReleaseBackendIndex(backendLookUpName, version) {
174
174
  return withCache({
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["hashicorpVersioning.id","p.map"],"sources":["../../../../lib/modules/datasource/terraform-provider/index.ts"],"sourcesContent":["// TODO: types (#22198)\nimport { logger } from '../../../logger/index.ts';\nimport { ExternalHostError } from '../../../types/errors/external-host-error.ts';\nimport { withCache } from '../../../util/cache/package/with-cache.ts';\nimport * 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 TerraformProviderV2Response,\n} from './schema.ts';\nimport type {\n TerraformBuild,\n TerraformProviderReleaseBackend,\n TerraformProviderVersions,\n TerraformRegistryBuildResponse,\n TerraformRegistryVersions,\n VersionDetailResponse,\n} from './types.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 static readonly defaultRegistryUrls = [\n TerraformProviderDatasource.terraformRegistryUrl,\n TerraformProviderDatasource.hashicorpReleaseUrl,\n ];\n\n static repositoryRegex = regEx(/^hashicorp\\/(?<packageName>\\S+)$/);\n\n constructor() {\n super(TerraformProviderDatasource.id);\n }\n\n override readonly defaultRegistryUrls =\n TerraformProviderDatasource.defaultRegistryUrls;\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 _getReleases({\n packageName,\n registryUrl,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n /* v8 ignore next 3 -- should never happen */\n if (!registryUrl) {\n return null;\n }\n 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 withCache(\n {\n namespace: `datasource-${TerraformProviderDatasource.id}`,\n key: `getReleases:${url}/${repo}`,\n fallback: true,\n },\n () => this._getReleases(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 = (\n await this.http.getJsonUnchecked<TerraformProviderVersions>(backendURL)\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.getJsonUnchecked<TerraformProviderReleaseBackend>(\n backendURL,\n )\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 _getBuilds(\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.getJsonUnchecked<TerraformRegistryVersions>(\n `${backendURL}/versions`,\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.getJsonUnchecked<TerraformRegistryBuildResponse>(\n buildURL,\n )\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 3 -- 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 withCache(\n {\n namespace: `datasource-${TerraformProviderDatasource.id}`,\n key: `getBuilds:${registryURL}/${repository}/${version}`,\n },\n () => this._getBuilds(registryURL, repository, version),\n );\n }\n\n private async _getZipHashes(\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 3 -- 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(/\\s/)[0]);\n }\n\n getZipHashes(zipHashUrl: string): Promise<string[] | undefined> {\n return withCache(\n {\n namespace: `datasource-${TerraformProviderDatasource.id}`,\n key: `getZipHashes:${zipHashUrl}`,\n },\n () => this._getZipHashes(zipHashUrl),\n );\n }\n\n private async _getReleaseBackendIndex(\n backendLookUpName: string,\n version: string,\n ): Promise<VersionDetailResponse> {\n return (\n await this.http.getJsonUnchecked<VersionDetailResponse>(\n `${TerraformProviderDatasource.hashicorpReleaseUrl}/${backendLookUpName}/${version}/index.json`,\n )\n ).body;\n }\n\n getReleaseBackendIndex(\n backendLookUpName: string,\n version: string,\n ): Promise<VersionDetailResponse> {\n return withCache(\n {\n namespace: `datasource-${TerraformProviderDatasource.id}`,\n key: `getReleaseBackendIndex:${backendLookUpName}/${version}`,\n },\n () => this._getReleaseBackendIndex(backendLookUpName, version),\n );\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["hashicorpVersioning.id","p.map"],"sources":["../../../../lib/modules/datasource/terraform-provider/index.ts"],"sourcesContent":["import { logger } from '../../../logger/index.ts';\nimport { ExternalHostError } from '../../../types/errors/external-host-error.ts';\nimport { withCache } from '../../../util/cache/package/with-cache.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 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 static readonly defaultRegistryUrls = [\n TerraformProviderDatasource.terraformRegistryUrl,\n TerraformProviderDatasource.hashicorpReleaseUrl,\n ];\n\n static repositoryRegex = regEx(/^hashicorp\\/(?<packageName>\\S+)$/);\n\n constructor() {\n super(TerraformProviderDatasource.id);\n }\n\n override readonly defaultRegistryUrls =\n TerraformProviderDatasource.defaultRegistryUrls;\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 _getReleases({\n packageName,\n registryUrl,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n /* v8 ignore next 3 -- should never happen */\n if (!registryUrl) {\n return null;\n }\n 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 withCache(\n {\n namespace: `datasource-${TerraformProviderDatasource.id}`,\n key: `getReleases:${url}/${repo}`,\n fallback: true,\n },\n () => this._getReleases(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 _getBuilds(\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 3 -- 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 withCache(\n {\n namespace: `datasource-${TerraformProviderDatasource.id}`,\n key: `getBuilds:${registryURL}/${repository}/${version}`,\n },\n () => this._getBuilds(registryURL, repository, version),\n );\n }\n\n private async _getZipHashes(\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 3 -- 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(/\\s/)[0]);\n }\n\n getZipHashes(zipHashUrl: string): Promise<string[] | undefined> {\n return withCache(\n {\n namespace: `datasource-${TerraformProviderDatasource.id}`,\n key: `getZipHashes:${zipHashUrl}`,\n },\n () => this._getZipHashes(zipHashUrl),\n );\n }\n\n private async _getReleaseBackendIndex(\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 withCache(\n {\n namespace: `datasource-${TerraformProviderDatasource.id}`,\n key: `getReleaseBackendIndex:${backendLookUpName}/${version}`,\n },\n () => this._getReleaseBackendIndex(backendLookUpName, version),\n );\n }\n}\n"],"mappings":"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"}
@@ -29,7 +29,43 @@ const OpenTofuProviderVersion = z.object({
29
29
  releaseTimestamp: version.published
30
30
  }));
31
31
  const OpenTofuProviderDocsResponse = z.object({ versions: LooseArray(OpenTofuProviderVersion).catch([]) }).transform((response) => ({ releases: response.versions }));
32
+ const TerraformBuild = z.object({
33
+ name: z.string(),
34
+ version: z.string(),
35
+ os: z.string(),
36
+ arch: z.string(),
37
+ filename: z.string(),
38
+ url: z.string(),
39
+ shasums_url: z.string().optional()
40
+ });
41
+ const VersionDetailResponse = z.object({
42
+ name: z.string().optional(),
43
+ version: z.string().optional(),
44
+ builds: z.array(TerraformBuild)
45
+ });
46
+ const TerraformProviderReleaseBackend = z.object({
47
+ name: z.string().optional(),
48
+ versions: z.record(z.string(), VersionDetailResponse)
49
+ });
50
+ const TerraformProviderVersionsVersion = z.object({ version: z.string() });
51
+ const TerraformProviderVersions = z.object({ versions: LooseArray(TerraformProviderVersionsVersion) });
52
+ const TerraformRegistryPlatform = z.object({
53
+ os: z.string(),
54
+ arch: z.string()
55
+ });
56
+ const TerraformRegistryVersionItem = z.object({
57
+ version: z.string(),
58
+ platforms: LooseArray(TerraformRegistryPlatform)
59
+ });
60
+ const TerraformRegistryVersions = z.object({ versions: LooseArray(TerraformRegistryVersionItem).optional() });
61
+ const TerraformRegistryBuildResponse = z.object({
62
+ os: z.string(),
63
+ arch: z.string(),
64
+ filename: z.string(),
65
+ download_url: z.string(),
66
+ shasums_url: z.string().optional()
67
+ });
32
68
  //#endregion
33
- export { OpenTofuProviderDocsResponse, TerraformProviderV2Response };
69
+ export { OpenTofuProviderDocsResponse, TerraformProviderReleaseBackend, TerraformProviderV2Response, TerraformProviderVersions, TerraformRegistryBuildResponse, TerraformRegistryVersions, VersionDetailResponse };
34
70
 
35
71
  //# sourceMappingURL=schema.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"schema.js","names":[],"sources":["../../../../lib/modules/datasource/terraform-provider/schema.ts"],"sourcesContent":["import { z } from 'zod/v4';\nimport { regEx } from '../../../util/regex.ts';\nimport { LooseArray } from '../../../util/schema-utils/index.ts';\nimport { MaybeTimestamp } from '../../../util/timestamp.ts';\nimport type { Release, ReleaseResult } from '../types.ts';\n\nconst ProviderAttributes = z.object({\n source: z.string().optional(),\n});\n\nconst ProviderVersion = z\n .object({\n type: z.literal('provider-versions'),\n attributes: z.object({\n version: z.string(),\n 'published-at': MaybeTimestamp,\n }),\n })\n .transform(\n (resource): Release => ({\n version: resource.attributes.version,\n releaseTimestamp: resource.attributes['published-at'],\n }),\n );\n\nexport const TerraformProviderV2Response = z\n .object({\n data: z.object({\n attributes: ProviderAttributes,\n }),\n included: LooseArray(ProviderVersion).catch([]),\n })\n .transform(\n (response): ReleaseResult => ({\n sourceUrl: response.data.attributes.source,\n releases: response.included,\n }),\n );\n\nexport type TerraformProviderV2Response = z.infer<\n typeof TerraformProviderV2Response\n>;\n\nconst OpenTofuProviderVersion = z\n .object({\n id: z.string(),\n published: MaybeTimestamp,\n })\n .transform(\n (version): Release => ({\n version: version.id.replace(regEx(/^v/), ''),\n releaseTimestamp: version.published,\n }),\n );\n\nexport const OpenTofuProviderDocsResponse = z\n .object({\n versions: LooseArray(OpenTofuProviderVersion).catch([]),\n })\n .transform(\n (response): ReleaseResult => ({\n releases: response.versions,\n }),\n );\n\nexport type OpenTofuProviderDocsResponse = z.infer<\n typeof OpenTofuProviderDocsResponse\n>;\n"],"mappings":";;;;;AAMA,MAAM,qBAAqB,EAAE,OAAO,EAClC,QAAQ,EAAE,OAAO,EAAE,SAAS,EAC9B,CAAC;AAED,MAAM,kBAAkB,EACrB,OAAO;CACN,MAAM,EAAE,QAAQ,mBAAmB;CACnC,YAAY,EAAE,OAAO;EACnB,SAAS,EAAE,OAAO;EAClB,gBAAgB;CAClB,CAAC;AACH,CAAC,EACA,WACE,cAAuB;CACtB,SAAS,SAAS,WAAW;CAC7B,kBAAkB,SAAS,WAAW;AACxC,EACF;AAEF,MAAa,8BAA8B,EACxC,OAAO;CACN,MAAM,EAAE,OAAO,EACb,YAAY,mBACd,CAAC;CACD,UAAU,WAAW,eAAe,EAAE,MAAM,CAAC,CAAC;AAChD,CAAC,EACA,WACE,cAA6B;CAC5B,WAAW,SAAS,KAAK,WAAW;CACpC,UAAU,SAAS;AACrB,EACF;AAMF,MAAM,0BAA0B,EAC7B,OAAO;CACN,IAAI,EAAE,OAAO;CACb,WAAW;AACb,CAAC,EACA,WACE,aAAsB;CACrB,SAAS,QAAQ,GAAG,QAAQ,MAAM,IAAI,GAAG,EAAE;CAC3C,kBAAkB,QAAQ;AAC5B,EACF;AAEF,MAAa,+BAA+B,EACzC,OAAO,EACN,UAAU,WAAW,uBAAuB,EAAE,MAAM,CAAC,CAAC,EACxD,CAAC,EACA,WACE,cAA6B,EAC5B,UAAU,SAAS,SACrB,EACF"}
1
+ {"version":3,"file":"schema.js","names":[],"sources":["../../../../lib/modules/datasource/terraform-provider/schema.ts"],"sourcesContent":["import { z } from 'zod/v4';\nimport { regEx } from '../../../util/regex.ts';\nimport { LooseArray } from '../../../util/schema-utils/index.ts';\nimport { MaybeTimestamp } from '../../../util/timestamp.ts';\nimport type { Release, ReleaseResult } from '../types.ts';\n\nconst ProviderAttributes = z.object({\n source: z.string().optional(),\n});\n\nconst ProviderVersion = z\n .object({\n type: z.literal('provider-versions'),\n attributes: z.object({\n version: z.string(),\n 'published-at': MaybeTimestamp,\n }),\n })\n .transform(\n (resource): Release => ({\n version: resource.attributes.version,\n releaseTimestamp: resource.attributes['published-at'],\n }),\n );\n\nexport const TerraformProviderV2Response = z\n .object({\n data: z.object({\n attributes: ProviderAttributes,\n }),\n included: LooseArray(ProviderVersion).catch([]),\n })\n .transform(\n (response): ReleaseResult => ({\n sourceUrl: response.data.attributes.source,\n releases: response.included,\n }),\n );\n\nexport type TerraformProviderV2Response = z.infer<\n typeof TerraformProviderV2Response\n>;\n\nconst OpenTofuProviderVersion = z\n .object({\n id: z.string(),\n published: MaybeTimestamp,\n })\n .transform(\n (version): Release => ({\n version: version.id.replace(regEx(/^v/), ''),\n releaseTimestamp: version.published,\n }),\n );\n\nexport const OpenTofuProviderDocsResponse = z\n .object({\n versions: LooseArray(OpenTofuProviderVersion).catch([]),\n })\n .transform(\n (response): ReleaseResult => ({\n releases: response.versions,\n }),\n );\n\nexport type OpenTofuProviderDocsResponse = z.infer<\n typeof OpenTofuProviderDocsResponse\n>;\n\n// VersionDetailResponse — used by the releases.hashicorp.com backend\nexport const TerraformBuild = z.object({\n name: z.string(),\n version: z.string(),\n os: z.string(),\n arch: z.string(),\n filename: z.string(),\n url: z.string(),\n shasums_url: z.string().optional(),\n});\n\nexport type TerraformBuild = z.infer<typeof TerraformBuild>;\n\nexport const VersionDetailResponse = z.object({\n name: z.string().optional(),\n version: z.string().optional(),\n builds: z.array(TerraformBuild),\n});\n\nexport type VersionDetailResponse = z.infer<typeof VersionDetailResponse>;\n\n// TerraformProviderReleaseBackend — index.json from releases.hashicorp.com\nexport const TerraformProviderReleaseBackend = z.object({\n name: z.string().optional(),\n versions: z.record(z.string(), VersionDetailResponse),\n});\n\nexport type TerraformProviderReleaseBackend = z.infer<\n typeof TerraformProviderReleaseBackend\n>;\n\n// TerraformProviderVersions — Provider Registry Protocol /versions endpoint\nconst TerraformProviderVersionsVersion = z.object({\n version: z.string(),\n});\n\nexport const TerraformProviderVersions = z.object({\n versions: LooseArray(TerraformProviderVersionsVersion),\n});\n\nexport type TerraformProviderVersions = z.infer<\n typeof TerraformProviderVersions\n>;\n\n// TerraformRegistryVersions — registry /versions endpoint (for getBuilds)\nconst TerraformRegistryPlatform = z.object({\n os: z.string(),\n arch: z.string(),\n});\n\nconst TerraformRegistryVersionItem = z.object({\n version: z.string(),\n platforms: LooseArray(TerraformRegistryPlatform),\n});\n\nexport const TerraformRegistryVersions = z.object({\n versions: LooseArray(TerraformRegistryVersionItem).optional(),\n});\n\nexport type TerraformRegistryVersions = z.infer<\n typeof TerraformRegistryVersions\n>;\n\n// TerraformRegistryBuildResponse — per-platform download info\nexport const TerraformRegistryBuildResponse = z.object({\n os: z.string(),\n arch: z.string(),\n filename: z.string(),\n download_url: z.string(),\n shasums_url: z.string().optional(),\n});\n\nexport type TerraformRegistryBuildResponse = z.infer<\n typeof TerraformRegistryBuildResponse\n>;\n"],"mappings":";;;;;AAMA,MAAM,qBAAqB,EAAE,OAAO,EAClC,QAAQ,EAAE,OAAO,EAAE,SAAS,EAC9B,CAAC;AAED,MAAM,kBAAkB,EACrB,OAAO;CACN,MAAM,EAAE,QAAQ,mBAAmB;CACnC,YAAY,EAAE,OAAO;EACnB,SAAS,EAAE,OAAO;EAClB,gBAAgB;CAClB,CAAC;AACH,CAAC,EACA,WACE,cAAuB;CACtB,SAAS,SAAS,WAAW;CAC7B,kBAAkB,SAAS,WAAW;AACxC,EACF;AAEF,MAAa,8BAA8B,EACxC,OAAO;CACN,MAAM,EAAE,OAAO,EACb,YAAY,mBACd,CAAC;CACD,UAAU,WAAW,eAAe,EAAE,MAAM,CAAC,CAAC;AAChD,CAAC,EACA,WACE,cAA6B;CAC5B,WAAW,SAAS,KAAK,WAAW;CACpC,UAAU,SAAS;AACrB,EACF;AAMF,MAAM,0BAA0B,EAC7B,OAAO;CACN,IAAI,EAAE,OAAO;CACb,WAAW;AACb,CAAC,EACA,WACE,aAAsB;CACrB,SAAS,QAAQ,GAAG,QAAQ,MAAM,IAAI,GAAG,EAAE;CAC3C,kBAAkB,QAAQ;AAC5B,EACF;AAEF,MAAa,+BAA+B,EACzC,OAAO,EACN,UAAU,WAAW,uBAAuB,EAAE,MAAM,CAAC,CAAC,EACxD,CAAC,EACA,WACE,cAA6B,EAC5B,UAAU,SAAS,SACrB,EACF;AAOF,MAAa,iBAAiB,EAAE,OAAO;CACrC,MAAM,EAAE,OAAO;CACf,SAAS,EAAE,OAAO;CAClB,IAAI,EAAE,OAAO;CACb,MAAM,EAAE,OAAO;CACf,UAAU,EAAE,OAAO;CACnB,KAAK,EAAE,OAAO;CACd,aAAa,EAAE,OAAO,EAAE,SAAS;AACnC,CAAC;AAID,MAAa,wBAAwB,EAAE,OAAO;CAC5C,MAAM,EAAE,OAAO,EAAE,SAAS;CAC1B,SAAS,EAAE,OAAO,EAAE,SAAS;CAC7B,QAAQ,EAAE,MAAM,cAAc;AAChC,CAAC;AAKD,MAAa,kCAAkC,EAAE,OAAO;CACtD,MAAM,EAAE,OAAO,EAAE,SAAS;CAC1B,UAAU,EAAE,OAAO,EAAE,OAAO,GAAG,qBAAqB;AACtD,CAAC;AAOD,MAAM,mCAAmC,EAAE,OAAO,EAChD,SAAS,EAAE,OAAO,EACpB,CAAC;AAED,MAAa,4BAA4B,EAAE,OAAO,EAChD,UAAU,WAAW,gCAAgC,EACvD,CAAC;AAOD,MAAM,4BAA4B,EAAE,OAAO;CACzC,IAAI,EAAE,OAAO;CACb,MAAM,EAAE,OAAO;AACjB,CAAC;AAED,MAAM,+BAA+B,EAAE,OAAO;CAC5C,SAAS,EAAE,OAAO;CAClB,WAAW,WAAW,yBAAyB;AACjD,CAAC;AAED,MAAa,4BAA4B,EAAE,OAAO,EAChD,UAAU,WAAW,4BAA4B,EAAE,SAAS,EAC9D,CAAC;AAOD,MAAa,iCAAiC,EAAE,OAAO;CACrD,IAAI,EAAE,OAAO;CACb,MAAM,EAAE,OAAO;CACf,UAAU,EAAE,OAAO;CACnB,cAAc,EAAE,OAAO;CACvB,aAAa,EAAE,OAAO,EAAE,SAAS;AACnC,CAAC"}
@@ -74,7 +74,7 @@ hashMap.set("mise", "d39d941e81e44ea6c3f78ced7869645a874949fefcd2fd2ba5b8091156b
74
74
  hashMap.set("mix", "a82c3d73372c2da723b467c29f0ad0bb1f85acb8cfb313a0aa734cc7ec2d46bf");
75
75
  hashMap.set("nix", "95f0827f7ec9a3bfd5119534f129aad6cf26b28e99d55aec0945f08bbfff9fb1");
76
76
  hashMap.set("nodenv", "ac652c28481b001f7471f4183eac07b45d98ecbe83efc275830b0a1b2e0459b8");
77
- hashMap.set("npm", "cdb387ad533db812e39e16d8679a2da2cf2983b23f7a348c7d55f07be9fa98fe");
77
+ hashMap.set("npm", "5c4dfaf08548824a9d97ef3d3513b0bd7e0917f19a9c43d7edb6311493f1609f");
78
78
  hashMap.set("nuget", "da72ddc95788d80b81c0b539b1b54001ef0c782f790efdc6e73aafb235d1e5f8");
79
79
  hashMap.set("nvm", "3eb77c7b0acf6d64753de78fad2499b352719853aa6872878c40675d14152322");
80
80
  hashMap.set("ocb", "032b216684c14b0954e0bb019513a2aca3faaee5748587acb668d377d7c891ca");