renovate 44.105.0 → 44.105.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/dist/config/presets/forgejo/index.js +2 -34
  2. package/dist/config/presets/forgejo/index.js.map +1 -1
  3. package/dist/config/presets/gitea/common.js +52 -0
  4. package/dist/config/presets/gitea/common.js.map +1 -0
  5. package/dist/config/presets/gitea/index.js +2 -34
  6. package/dist/config/presets/gitea/index.js.map +1 -1
  7. package/dist/modules/datasource/api.js +3 -3
  8. package/dist/modules/datasource/crate/index.js +59 -4
  9. package/dist/modules/datasource/crate/index.js.map +1 -1
  10. package/dist/modules/datasource/forgejo-releases/index.js +9 -58
  11. package/dist/modules/datasource/forgejo-releases/index.js.map +1 -1
  12. package/dist/modules/datasource/forgejo-tags/index.js +9 -72
  13. package/dist/modules/datasource/forgejo-tags/index.js.map +1 -1
  14. package/dist/modules/datasource/gitea-releases/index.js +17 -53
  15. package/dist/modules/datasource/gitea-releases/index.js.map +1 -1
  16. package/dist/modules/datasource/gitea-tags/base.js +93 -0
  17. package/dist/modules/datasource/gitea-tags/base.js.map +1 -0
  18. package/dist/modules/datasource/gitea-tags/index.js +17 -67
  19. package/dist/modules/datasource/gitea-tags/index.js.map +1 -1
  20. package/dist/modules/datasource/gitea-tags/util.js +24 -0
  21. package/dist/modules/datasource/gitea-tags/util.js.map +1 -0
  22. package/dist/modules/datasource/go/base.js +1 -1
  23. package/dist/modules/datasource/go/common.js +1 -1
  24. package/dist/modules/datasource/go/index.js +1 -1
  25. package/dist/modules/datasource/go/releases-direct.js +1 -1
  26. package/dist/modules/manager/github-actions/extract.js +1 -1
  27. package/dist/modules/manager/renovate-config/extract.js +1 -1
  28. package/dist/modules/manager/terragrunt/index.js +1 -1
  29. package/dist/modules/manager/terragrunt/modules.js +1 -1
  30. package/dist/util/cache/package/namespaces.d.ts +1 -1
  31. package/dist/util/cache/package/namespaces.js +1 -0
  32. package/dist/util/cache/package/namespaces.js.map +1 -1
  33. package/dist/workers/repository/dependency-dashboard.js +8 -1
  34. package/dist/workers/repository/dependency-dashboard.js.map +1 -1
  35. package/dist/workers/repository/update/pr/changelog/api.js +1 -1
  36. package/dist/workers/repository/update/pr/changelog/forgejo/index.js +2 -41
  37. package/dist/workers/repository/update/pr/changelog/forgejo/index.js.map +1 -1
  38. package/dist/workers/repository/update/pr/changelog/forgejo/source.js +6 -11
  39. package/dist/workers/repository/update/pr/changelog/forgejo/source.js.map +1 -1
  40. package/dist/workers/repository/update/pr/changelog/gitea/common.js +56 -0
  41. package/dist/workers/repository/update/pr/changelog/gitea/common.js.map +1 -0
  42. package/dist/workers/repository/update/pr/changelog/gitea/index.js +2 -41
  43. package/dist/workers/repository/update/pr/changelog/gitea/index.js.map +1 -1
  44. package/dist/workers/repository/update/pr/changelog/gitea/source.js +3 -2
  45. package/dist/workers/repository/update/pr/changelog/gitea/source.js.map +1 -1
  46. package/package.json +1 -1
  47. package/renovate-schema.json +2 -2
  48. package/dist/modules/datasource/forgejo-releases/schema.js +0 -15
  49. package/dist/modules/datasource/forgejo-releases/schema.js.map +0 -1
  50. package/dist/modules/datasource/forgejo-tags/schema.js +0 -18
  51. package/dist/modules/datasource/forgejo-tags/schema.js.map +0 -1
@@ -1,40 +1,8 @@
1
- import { logger } from "../../../logger/index.js";
2
- import { ExternalHostError } from "../../../types/errors/external-host-error.js";
3
- import { PRESET_DEP_NOT_FOUND, PRESET_INVALID, fetchPreset, parsePreset } from "../util.js";
4
- import { getRepoContents } from "../../../modules/platform/gitea/gitea-helper.js";
5
1
  import { forgejoHttp } from "../../../modules/platform/forgejo/index.js";
2
+ import { createPresetSource } from "../gitea/common.js";
6
3
  //#region lib/config/presets/forgejo/index.ts
7
4
  const Endpoint = "https://code.forgejo.org/";
8
- async function fetchJSONFile(repo, fileName, endpoint, tag) {
9
- let res;
10
- try {
11
- res = await getRepoContents(forgejoHttp, repo, fileName, tag, { baseUrl: endpoint });
12
- } catch (err) {
13
- if (err instanceof ExternalHostError) throw err;
14
- logger.debug(`Preset file ${fileName} not found in ${repo}: ${err.message}`);
15
- throw new Error(PRESET_DEP_NOT_FOUND);
16
- }
17
- let contentString;
18
- if (res.type === "file") contentString = res.contentString;
19
- else {
20
- logger.debug(`Preset ${fileName} has unexpected type '${res.type}'. Only \`file\` is supported`);
21
- throw new Error(PRESET_INVALID);
22
- }
23
- return parsePreset(contentString, fileName);
24
- }
25
- function getPresetFromEndpoint(repo, filePreset, presetPath, endpoint = Endpoint, tag) {
26
- return fetchPreset({
27
- repo,
28
- filePreset,
29
- presetPath,
30
- endpoint,
31
- tag,
32
- fetch: fetchJSONFile
33
- });
34
- }
35
- function getPreset({ repo, presetName = "default", presetPath, tag }) {
36
- return getPresetFromEndpoint(repo, presetName, presetPath, Endpoint, tag);
37
- }
5
+ const { fetchJSONFile, getPresetFromEndpoint, getPreset } = createPresetSource(forgejoHttp, Endpoint);
38
6
  //#endregion
39
7
  export { Endpoint, fetchJSONFile, getPreset, getPresetFromEndpoint };
40
8
 
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../../lib/config/presets/forgejo/index.ts"],"sourcesContent":["import { logger } from '../../../logger/index.ts';\nimport { forgejoHttp } from '../../../modules/platform/forgejo/index.ts';\nimport { getRepoContents } from '../../../modules/platform/gitea/gitea-helper.ts';\nimport type { RepoContents } from '../../../modules/platform/gitea/schema.ts';\nimport { ExternalHostError } from '../../../types/errors/external-host-error.ts';\nimport type { Nullish } from '../../../types/index.ts';\nimport type { Preset, PresetConfig } from '../types.ts';\nimport {\n PRESET_DEP_NOT_FOUND,\n PRESET_INVALID,\n fetchPreset,\n parsePreset,\n} from '../util.ts';\n\nexport const Endpoint = 'https://code.forgejo.org/';\n\nexport async function fetchJSONFile(\n repo: string,\n fileName: string,\n endpoint: string,\n tag?: string | null,\n): Promise<Nullish<Preset>> {\n let res: RepoContents;\n try {\n res = await getRepoContents(forgejoHttp, repo, fileName, tag, {\n baseUrl: endpoint,\n });\n } catch (err) {\n if (err instanceof ExternalHostError) {\n throw err;\n }\n logger.debug(\n `Preset file ${fileName} not found in ${repo}: ${err.message}`,\n );\n throw new Error(PRESET_DEP_NOT_FOUND);\n }\n\n let contentString: string;\n if (res.type === 'file') {\n contentString = res.contentString;\n } else {\n logger.debug(\n `Preset ${fileName} has unexpected type '${res.type}'. Only \\`file\\` is supported`,\n );\n throw new Error(PRESET_INVALID);\n }\n return parsePreset(contentString, fileName);\n}\n\nexport function getPresetFromEndpoint(\n repo: string,\n filePreset: string,\n presetPath?: string,\n endpoint = Endpoint,\n tag?: string,\n): Promise<Nullish<Preset>> {\n return fetchPreset({\n repo,\n filePreset,\n presetPath,\n endpoint,\n tag,\n fetch: fetchJSONFile,\n });\n}\n\nexport function getPreset({\n repo,\n presetName = 'default',\n presetPath,\n tag,\n}: PresetConfig): Promise<Nullish<Preset>> {\n return getPresetFromEndpoint(repo, presetName, presetPath, Endpoint, tag);\n}\n"],"mappings":";;;;;;AAcA,MAAa,WAAW;AAExB,eAAsB,cACpB,MACA,UACA,UACA,KAC0B;CAC1B,IAAI;CACJ,IAAI;EACF,MAAM,MAAM,gBAAgB,aAAa,MAAM,UAAU,KAAK,EAC5D,SAAS,SACX,CAAC;CACH,SAAS,KAAK;EACZ,IAAI,eAAe,mBACjB,MAAM;EAER,OAAO,MACL,eAAe,SAAS,gBAAgB,KAAK,IAAI,IAAI,SACvD;EACA,MAAM,IAAI,MAAM,oBAAoB;CACtC;CAEA,IAAI;CACJ,IAAI,IAAI,SAAS,QACf,gBAAgB,IAAI;MACf;EACL,OAAO,MACL,UAAU,SAAS,wBAAwB,IAAI,KAAK,8BACtD;EACA,MAAM,IAAI,MAAM,cAAc;CAChC;CACA,OAAO,YAAY,eAAe,QAAQ;AAC5C;AAEA,SAAgB,sBACd,MACA,YACA,YACA,WAAW,UACX,KAC0B;CAC1B,OAAO,YAAY;EACjB;EACA;EACA;EACA;EACA;EACA,OAAO;CACT,CAAC;AACH;AAEA,SAAgB,UAAU,EACxB,MACA,aAAa,WACb,YACA,OACyC;CACzC,OAAO,sBAAsB,MAAM,YAAY,YAAY,UAAU,GAAG;AAC1E"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../../lib/config/presets/forgejo/index.ts"],"sourcesContent":["import { forgejoHttp } from '../../../modules/platform/forgejo/index.ts';\nimport { createPresetSource } from '../gitea/common.ts';\n\nexport const Endpoint = 'https://code.forgejo.org/';\n\nexport const { fetchJSONFile, getPresetFromEndpoint, getPreset } =\n createPresetSource(forgejoHttp, Endpoint);\n"],"mappings":";;;AAGA,MAAa,WAAW;AAExB,MAAa,EAAE,eAAe,uBAAuB,cACnD,mBAAmB,aAAa,QAAQ"}
@@ -0,0 +1,52 @@
1
+ import { logger } from "../../../logger/index.js";
2
+ import { ExternalHostError } from "../../../types/errors/external-host-error.js";
3
+ import { PRESET_DEP_NOT_FOUND, PRESET_INVALID, fetchPreset, parsePreset } from "../util.js";
4
+ import { getRepoContents } from "../../../modules/platform/gitea/gitea-helper.js";
5
+ //#region lib/config/presets/gitea/common.ts
6
+ /**
7
+ * Builds the preset source of a platform which speaks the Gitea API.
8
+ *
9
+ * Forgejo is a fork of Gitea and serves the same contents API, so the Gitea
10
+ * and Forgejo sources only differ in their Http client and default endpoint.
11
+ */
12
+ function createPresetSource(http, defaultEndpoint) {
13
+ async function fetchJSONFile(repo, fileName, endpoint, tag) {
14
+ let res;
15
+ try {
16
+ res = await getRepoContents(http, repo, fileName, tag, { baseUrl: endpoint });
17
+ } catch (err) {
18
+ if (err instanceof ExternalHostError) throw err;
19
+ logger.debug(`Preset file ${fileName} not found in ${repo}: ${err.message}`);
20
+ throw new Error(PRESET_DEP_NOT_FOUND);
21
+ }
22
+ let contentString;
23
+ if (res.type === "file") contentString = res.contentString;
24
+ else {
25
+ logger.debug(`Preset ${fileName} has unexpected type '${res.type}'. Only \`file\` is supported`);
26
+ throw new Error(PRESET_INVALID);
27
+ }
28
+ return parsePreset(contentString, fileName);
29
+ }
30
+ function getPresetFromEndpoint(repo, filePreset, presetPath, endpoint = defaultEndpoint, tag) {
31
+ return fetchPreset({
32
+ repo,
33
+ filePreset,
34
+ presetPath,
35
+ endpoint,
36
+ tag,
37
+ fetch: fetchJSONFile
38
+ });
39
+ }
40
+ function getPreset({ repo, presetName = "default", presetPath, tag }) {
41
+ return getPresetFromEndpoint(repo, presetName, presetPath, defaultEndpoint, tag);
42
+ }
43
+ return {
44
+ fetchJSONFile,
45
+ getPresetFromEndpoint,
46
+ getPreset
47
+ };
48
+ }
49
+ //#endregion
50
+ export { createPresetSource };
51
+
52
+ //# sourceMappingURL=common.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"common.js","names":[],"sources":["../../../../lib/config/presets/gitea/common.ts"],"sourcesContent":["import { logger } from '../../../logger/index.ts';\nimport { getRepoContents } from '../../../modules/platform/gitea/gitea-helper.ts';\nimport type { RepoContents } from '../../../modules/platform/gitea/schema.ts';\nimport { ExternalHostError } from '../../../types/errors/external-host-error.ts';\nimport type { Nullish } from '../../../types/index.ts';\nimport type { GiteaHttp } from '../../../util/http/gitea.ts';\nimport type { Preset, PresetConfig } from '../types.ts';\nimport {\n PRESET_DEP_NOT_FOUND,\n PRESET_INVALID,\n fetchPreset,\n parsePreset,\n} from '../util.ts';\n\nexport interface GiteaPresetSource {\n fetchJSONFile: (\n repo: string,\n fileName: string,\n endpoint: string,\n tag?: string | null,\n ) => Promise<Nullish<Preset>>;\n getPresetFromEndpoint: (\n repo: string,\n filePreset: string,\n presetPath?: string,\n endpoint?: string,\n tag?: string,\n ) => Promise<Nullish<Preset>>;\n getPreset: (config: PresetConfig) => Promise<Nullish<Preset>>;\n}\n\n/**\n * Builds the preset source of a platform which speaks the Gitea API.\n *\n * Forgejo is a fork of Gitea and serves the same contents API, so the Gitea\n * and Forgejo sources only differ in their Http client and default endpoint.\n */\nexport function createPresetSource(\n http: GiteaHttp,\n defaultEndpoint: string,\n): GiteaPresetSource {\n async function fetchJSONFile(\n repo: string,\n fileName: string,\n endpoint: string,\n tag?: string | null,\n ): Promise<Nullish<Preset>> {\n let res: RepoContents;\n try {\n res = await getRepoContents(http, repo, fileName, tag, {\n baseUrl: endpoint,\n });\n } catch (err) {\n if (err instanceof ExternalHostError) {\n throw err;\n }\n logger.debug(\n `Preset file ${fileName} not found in ${repo}: ${err.message}`,\n );\n throw new Error(PRESET_DEP_NOT_FOUND);\n }\n\n let contentString: string;\n if (res.type === 'file') {\n contentString = res.contentString;\n } else {\n logger.debug(\n `Preset ${fileName} has unexpected type '${res.type}'. Only \\`file\\` is supported`,\n );\n throw new Error(PRESET_INVALID);\n }\n return parsePreset(contentString, fileName);\n }\n\n function getPresetFromEndpoint(\n repo: string,\n filePreset: string,\n presetPath?: string,\n endpoint = defaultEndpoint,\n tag?: string,\n ): Promise<Nullish<Preset>> {\n return fetchPreset({\n repo,\n filePreset,\n presetPath,\n endpoint,\n tag,\n fetch: fetchJSONFile,\n });\n }\n\n function getPreset({\n repo,\n presetName = 'default',\n presetPath,\n tag,\n }: PresetConfig): Promise<Nullish<Preset>> {\n return getPresetFromEndpoint(\n repo,\n presetName,\n presetPath,\n defaultEndpoint,\n tag,\n );\n }\n\n return { fetchJSONFile, getPresetFromEndpoint, getPreset };\n}\n"],"mappings":";;;;;;;;;;;AAqCA,SAAgB,mBACd,MACA,iBACmB;CACnB,eAAe,cACb,MACA,UACA,UACA,KAC0B;EAC1B,IAAI;EACJ,IAAI;GACF,MAAM,MAAM,gBAAgB,MAAM,MAAM,UAAU,KAAK,EACrD,SAAS,SACX,CAAC;EACH,SAAS,KAAK;GACZ,IAAI,eAAe,mBACjB,MAAM;GAER,OAAO,MACL,eAAe,SAAS,gBAAgB,KAAK,IAAI,IAAI,SACvD;GACA,MAAM,IAAI,MAAM,oBAAoB;EACtC;EAEA,IAAI;EACJ,IAAI,IAAI,SAAS,QACf,gBAAgB,IAAI;OACf;GACL,OAAO,MACL,UAAU,SAAS,wBAAwB,IAAI,KAAK,8BACtD;GACA,MAAM,IAAI,MAAM,cAAc;EAChC;EACA,OAAO,YAAY,eAAe,QAAQ;CAC5C;CAEA,SAAS,sBACP,MACA,YACA,YACA,WAAW,iBACX,KAC0B;EAC1B,OAAO,YAAY;GACjB;GACA;GACA;GACA;GACA;GACA,OAAO;EACT,CAAC;CACH;CAEA,SAAS,UAAU,EACjB,MACA,aAAa,WACb,YACA,OACyC;EACzC,OAAO,sBACL,MACA,YACA,YACA,iBACA,GACF;CACF;CAEA,OAAO;EAAE;EAAe;EAAuB;CAAU;AAC3D"}
@@ -1,40 +1,8 @@
1
- import { logger } from "../../../logger/index.js";
2
- import { ExternalHostError } from "../../../types/errors/external-host-error.js";
3
- import { PRESET_DEP_NOT_FOUND, PRESET_INVALID, fetchPreset, parsePreset } from "../util.js";
4
- import { getRepoContents } from "../../../modules/platform/gitea/gitea-helper.js";
5
1
  import { giteaHttp } from "../../../modules/platform/gitea/index.js";
2
+ import { createPresetSource } from "./common.js";
6
3
  //#region lib/config/presets/gitea/index.ts
7
4
  const Endpoint = "https://gitea.com/";
8
- async function fetchJSONFile(repo, fileName, endpoint, tag) {
9
- let res;
10
- try {
11
- res = await getRepoContents(giteaHttp, repo, fileName, tag, { baseUrl: endpoint });
12
- } catch (err) {
13
- if (err instanceof ExternalHostError) throw err;
14
- logger.debug(`Preset file ${fileName} not found in ${repo}: ${err.message}`);
15
- throw new Error(PRESET_DEP_NOT_FOUND);
16
- }
17
- let contentString;
18
- if (res.type === "file") contentString = res.contentString;
19
- else {
20
- logger.debug(`Preset ${fileName} has unexpected type '${res.type}'. Only \`file\` is supported`);
21
- throw new Error(PRESET_INVALID);
22
- }
23
- return parsePreset(contentString, fileName);
24
- }
25
- function getPresetFromEndpoint(repo, filePreset, presetPath, endpoint = Endpoint, tag) {
26
- return fetchPreset({
27
- repo,
28
- filePreset,
29
- presetPath,
30
- endpoint,
31
- tag,
32
- fetch: fetchJSONFile
33
- });
34
- }
35
- function getPreset({ repo, presetName = "default", presetPath, tag }) {
36
- return getPresetFromEndpoint(repo, presetName, presetPath, Endpoint, tag);
37
- }
5
+ const { fetchJSONFile, getPresetFromEndpoint, getPreset } = createPresetSource(giteaHttp, Endpoint);
38
6
  //#endregion
39
7
  export { Endpoint, fetchJSONFile, getPreset, getPresetFromEndpoint };
40
8
 
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../../lib/config/presets/gitea/index.ts"],"sourcesContent":["import { logger } from '../../../logger/index.ts';\nimport { getRepoContents } from '../../../modules/platform/gitea/gitea-helper.ts';\nimport { giteaHttp } from '../../../modules/platform/gitea/index.ts';\nimport type { RepoContents } from '../../../modules/platform/gitea/schema.ts';\nimport { ExternalHostError } from '../../../types/errors/external-host-error.ts';\nimport type { Nullish } from '../../../types/index.ts';\nimport type { Preset, PresetConfig } from '../types.ts';\nimport {\n PRESET_DEP_NOT_FOUND,\n PRESET_INVALID,\n fetchPreset,\n parsePreset,\n} from '../util.ts';\n\nexport const Endpoint = 'https://gitea.com/';\n\nexport async function fetchJSONFile(\n repo: string,\n fileName: string,\n endpoint: string,\n tag?: string | null,\n): Promise<Nullish<Preset>> {\n let res: RepoContents;\n try {\n res = await getRepoContents(giteaHttp, repo, fileName, tag, {\n baseUrl: endpoint,\n });\n } catch (err) {\n if (err instanceof ExternalHostError) {\n throw err;\n }\n logger.debug(\n `Preset file ${fileName} not found in ${repo}: ${err.message}`,\n );\n throw new Error(PRESET_DEP_NOT_FOUND);\n }\n\n let contentString: string;\n if (res.type === 'file') {\n contentString = res.contentString;\n } else {\n logger.debug(\n `Preset ${fileName} has unexpected type '${res.type}'. Only \\`file\\` is supported`,\n );\n throw new Error(PRESET_INVALID);\n }\n return parsePreset(contentString, fileName);\n}\n\nexport function getPresetFromEndpoint(\n repo: string,\n filePreset: string,\n presetPath?: string,\n endpoint = Endpoint,\n tag?: string,\n): Promise<Nullish<Preset>> {\n return fetchPreset({\n repo,\n filePreset,\n presetPath,\n endpoint,\n tag,\n fetch: fetchJSONFile,\n });\n}\n\nexport function getPreset({\n repo,\n presetName = 'default',\n presetPath,\n tag,\n}: PresetConfig): Promise<Nullish<Preset>> {\n return getPresetFromEndpoint(repo, presetName, presetPath, Endpoint, tag);\n}\n"],"mappings":";;;;;;AAcA,MAAa,WAAW;AAExB,eAAsB,cACpB,MACA,UACA,UACA,KAC0B;CAC1B,IAAI;CACJ,IAAI;EACF,MAAM,MAAM,gBAAgB,WAAW,MAAM,UAAU,KAAK,EAC1D,SAAS,SACX,CAAC;CACH,SAAS,KAAK;EACZ,IAAI,eAAe,mBACjB,MAAM;EAER,OAAO,MACL,eAAe,SAAS,gBAAgB,KAAK,IAAI,IAAI,SACvD;EACA,MAAM,IAAI,MAAM,oBAAoB;CACtC;CAEA,IAAI;CACJ,IAAI,IAAI,SAAS,QACf,gBAAgB,IAAI;MACf;EACL,OAAO,MACL,UAAU,SAAS,wBAAwB,IAAI,KAAK,8BACtD;EACA,MAAM,IAAI,MAAM,cAAc;CAChC;CACA,OAAO,YAAY,eAAe,QAAQ;AAC5C;AAEA,SAAgB,sBACd,MACA,YACA,YACA,WAAW,UACX,KAC0B;CAC1B,OAAO,YAAY;EACjB;EACA;EACA;EACA;EACA;EACA,OAAO;CACT,CAAC;AACH;AAEA,SAAgB,UAAU,EACxB,MACA,aAAa,WACb,YACA,OACyC;CACzC,OAAO,sBAAsB,MAAM,YAAY,YAAY,UAAU,GAAG;AAC1E"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../../lib/config/presets/gitea/index.ts"],"sourcesContent":["import { giteaHttp } from '../../../modules/platform/gitea/index.ts';\nimport { createPresetSource } from './common.ts';\n\nexport const Endpoint = 'https://gitea.com/';\n\nexport const { fetchJSONFile, getPresetFromEndpoint, getPreset } =\n createPresetSource(giteaHttp, Endpoint);\n"],"mappings":";;;AAGA,MAAa,WAAW;AAExB,MAAa,EAAE,eAAe,uBAAuB,cACnD,mBAAmB,WAAW,QAAQ"}
@@ -29,14 +29,14 @@ import { DotnetVersionDatasource } from "./dotnet-version/index.js";
29
29
  import { ElmPackageDatasource } from "./elm-package/index.js";
30
30
  import { EndoflifeDateDatasource } from "./endoflife-date/index.js";
31
31
  import { FlutterVersionDatasource } from "./flutter-version/index.js";
32
- import { ForgejoTagsDatasource } from "./forgejo-tags/index.js";
32
+ import { GiteaReleasesDatasource } from "./gitea-releases/index.js";
33
33
  import { ForgejoReleasesDatasource } from "./forgejo-releases/index.js";
34
+ import { GiteaTagsDatasource } from "./gitea-tags/index.js";
35
+ import { ForgejoTagsDatasource } from "./forgejo-tags/index.js";
34
36
  import { GalaxyDatasource } from "./galaxy/index.js";
35
37
  import { GalaxyCollectionDatasource } from "./galaxy-collection/index.js";
36
38
  import { GitRefsDatasource } from "./git-refs/index.js";
37
39
  import { GitTagsDatasource } from "./git-tags/index.js";
38
- import { GiteaTagsDatasource } from "./gitea-tags/index.js";
39
- import { GiteaReleasesDatasource } from "./gitea-releases/index.js";
40
40
  import { GithubDigestDatasource } from "./github-digest/index.js";
41
41
  import { GithubReleaseAttachmentsDatasource } from "./github-release-attachments/index.js";
42
42
  import { GithubReleasesDatasource } from "./github-releases/index.js";
@@ -8,12 +8,16 @@ import { privateCacheDir, readCacheFile } from "../../../util/fs/index.js";
8
8
  import { Json } from "../../../util/schema-utils/index.js";
9
9
  import { id } from "../../versioning/cargo/index.js";
10
10
  import { acquireLock } from "../../../util/mutex.js";
11
+ import { get as get$1, set as set$1 } from "../../../util/cache/package/index.js";
11
12
  import { withCache } from "../../../util/cache/package/with-cache.js";
13
+ import { RequestError } from "../../../util/http/got.js";
14
+ import "../../../util/http/index.js";
12
15
  import { asTimestamp } from "../../../util/timestamp.js";
13
16
  import { Datasource } from "../datasource.js";
14
17
  import { memCacheProvider } from "../../../util/http/cache/memory-http-cache-provider.js";
15
18
  import { createSimpleGit } from "../../../util/git/index.js";
16
19
  import { CrateMetadataResponse, RegistryConfig, ReleaseTimestamp } from "./schema.js";
20
+ import { isBoolean } from "@sindresorhus/is";
17
21
  import upath from "upath";
18
22
  //#region lib/modules/datasource/crate/index.ts
19
23
  var CrateDatasource = class CrateDatasource extends Datasource {
@@ -99,10 +103,50 @@ var CrateDatasource = class CrateDatasource extends Datasource {
99
103
  }
100
104
  return null;
101
105
  }
106
+ /**
107
+ * The registry web API specification only defines publish, yank, unyank,
108
+ * owners, search and login endpoints, see
109
+ * https://doc.rust-lang.org/cargo/reference/registry-web-api.html.
110
+ * `GET api/v1/crates/<name>` and `GET api/v1/crates/<name>/<version>` are
111
+ * crates.io extensions. Registries which mirror the crates.io index keep
112
+ * `api` pointing at crates.io and are used without probing. Any other
113
+ * registry is probed once and remembered as unsupported when it answers
114
+ * 404, since private registries (Artifactory, CodeArtifact, ...) advertise
115
+ * an `api` URL for publishing only.
116
+ */
117
+ static isCratesIoApi(api) {
118
+ return parseUrl(api)?.hostname === "crates.io";
119
+ }
120
+ static isNotFound(err) {
121
+ return err instanceof RequestError && err.response?.statusCode === 404;
122
+ }
123
+ /**
124
+ * The marker is keyed by the `api` URL, since the missing endpoints are a
125
+ * property of the API server, not of the index that points to it.
126
+ */
127
+ async isReadApiUnsupported(api) {
128
+ if (CrateDatasource.isCratesIoApi(api)) return false;
129
+ const memKey = `crate-datasource/registry-api-unsupported/${api}`;
130
+ const cached = get(memKey);
131
+ if (isBoolean(cached)) return cached;
132
+ const unsupported = await get$1("datasource-crate-registry-api", api) === true;
133
+ set(memKey, unsupported);
134
+ return unsupported;
135
+ }
136
+ async markReadApiUnsupported(registryUrl, api) {
137
+ logger.debug({
138
+ registryUrl,
139
+ api
140
+ }, "Registry does not implement the crates.io read API, skipping crate metadata and release timestamp lookups");
141
+ set(`crate-datasource/registry-api-unsupported/${api}`, true);
142
+ await set$1("datasource-crate-registry-api", api, true, 1440);
143
+ }
102
144
  async _getCrateMetadata(info, packageName) {
103
145
  const registryConfig = await this.fetchRegistryConfig(info);
104
146
  if (!registryConfig?.api) return null;
105
- const crateUrl = `${joinUrlParts(registryConfig.api, "api/v1/")}crates/${packageName}?include=`;
147
+ const { api } = registryConfig;
148
+ if (await this.isReadApiUnsupported(api)) return null;
149
+ const crateUrl = `${joinUrlParts(api, "api/v1/")}crates/${packageName}?include=`;
106
150
  logger.trace({
107
151
  crateUrl,
108
152
  packageName,
@@ -112,6 +156,10 @@ var CrateDatasource = class CrateDatasource extends Datasource {
112
156
  const { body } = await this.http.getJson(crateUrl, CrateMetadataResponse);
113
157
  return body.crate;
114
158
  } catch (err) {
159
+ if (!CrateDatasource.isCratesIoApi(api) && CrateDatasource.isNotFound(err)) {
160
+ await this.markReadApiUnsupported(info.rawUrl, api);
161
+ return null;
162
+ }
115
163
  logger.debug({
116
164
  err,
117
165
  packageName,
@@ -302,15 +350,22 @@ var CrateDatasource = class CrateDatasource extends Datasource {
302
350
  const cacheKey = `crate-datasource/registry-config/${rawUrl}`;
303
351
  const config = get(cacheKey);
304
352
  if (!config?.api) return release;
305
- const url = `${joinUrlParts(config.api, "api/v1/")}crates/${packageName}/${release.versionOrig ?? release.version}`;
353
+ const { api } = config;
354
+ if (await this.isReadApiUnsupported(api)) return release;
355
+ const url = `${joinUrlParts(api, "api/v1/")}crates/${packageName}/${release.versionOrig ?? release.version}`;
306
356
  logger.trace({
307
357
  url,
308
358
  packageName,
309
359
  version: release.version,
310
360
  registryUrl
311
361
  }, "fetching crate release timestamp");
312
- const { body: releaseTimestamp } = await this.http.getJson(url, { cacheProvider: memCacheProvider }, ReleaseTimestamp);
313
- release.releaseTimestamp = releaseTimestamp;
362
+ try {
363
+ const { body: releaseTimestamp } = await this.http.getJson(url, { cacheProvider: memCacheProvider }, ReleaseTimestamp);
364
+ release.releaseTimestamp = releaseTimestamp;
365
+ } catch (err) {
366
+ if (CrateDatasource.isCratesIoApi(api) || !CrateDatasource.isNotFound(err)) throw err;
367
+ await this.markReadApiUnsupported(rawUrl, api);
368
+ }
314
369
  return release;
315
370
  }
316
371
  postprocessRelease(config, release) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["cargoVersioning.id","memCache.get"],"sources":["../../../../lib/modules/datasource/crate/index.ts"],"sourcesContent":["import upath from 'upath';\nimport { GlobalConfig } from '../../../config/global.ts';\nimport { logger } from '../../../logger/index.ts';\nimport * as memCache from '../../../util/cache/memory/index.ts';\nimport { withCache } from '../../../util/cache/package/with-cache.ts';\nimport { privateCacheDir, readCacheFile } from '../../../util/fs/index.ts';\nimport { createSimpleGit } from '../../../util/git/index.ts';\nimport { toSha256 } from '../../../util/hash.ts';\nimport { memCacheProvider } from '../../../util/http/cache/memory-http-cache-provider.ts';\nimport { acquireLock } from '../../../util/mutex.ts';\nimport { newlineRegex, regEx } from '../../../util/regex.ts';\nimport { Json } from '../../../util/schema-utils/index.ts';\nimport { asTimestamp } from '../../../util/timestamp.ts';\nimport { joinUrlParts, parseUrl } from '../../../util/url.ts';\nimport * as cargoVersioning from '../../versioning/cargo/index.ts';\nimport { Datasource } from '../datasource.ts';\nimport type {\n GetReleasesConfig,\n PostprocessReleaseConfig,\n PostprocessReleaseResult,\n Release,\n ReleaseResult,\n} from '../types.ts';\nimport type { CrateMetadata } from './schema.ts';\nimport {\n CrateMetadataResponse,\n RegistryConfig,\n ReleaseTimestamp,\n} from './schema.ts';\nimport type { CrateRecord, RegistryFlavor, RegistryInfo } from './types.ts';\n\ntype CloneResult =\n | {\n err: Error;\n clonePath?: undefined;\n }\n | {\n clonePath: string;\n err?: undefined;\n };\n\nexport class CrateDatasource extends Datasource {\n static readonly id = 'crate';\n\n constructor() {\n super(CrateDatasource.id);\n }\n\n override defaultRegistryUrls = ['sparse+https://index.crates.io/'];\n\n override defaultVersioning = cargoVersioning.id;\n\n override readonly sourceUrlSupport = 'package';\n override readonly sourceUrlNote =\n 'The source URL is determined from the `repository` field in the results.';\n\n override readonly releaseTimestampSupport = true;\n override readonly releaseTimestampNote =\n 'The release timestamp is determined from `pubtime` field from crates.io index if available, or `version.created_at` field from crates.io API otherwise.';\n\n private async _getReleases({\n packageName,\n registryUrl,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n /* v8 ignore if -- should never happen */\n if (!registryUrl) {\n logger.warn(\n 'crate datasource: No registryUrl specified, cannot perform getReleases',\n );\n return null;\n }\n\n const registryInfo = await CrateDatasource.fetchRegistryInfo({\n packageName,\n registryUrl,\n });\n if (!registryInfo) {\n logger.debug(`Could not fetch registry info from ${registryUrl}`);\n return null;\n }\n\n const dependencyUrl = CrateDatasource.getDependencyUrl(\n registryInfo,\n packageName,\n );\n\n const payload = await this.fetchCrateRecordsPayload(\n registryInfo,\n packageName,\n );\n const lines = payload\n .split(newlineRegex) // break into lines\n .map((line) => line.trim()) // remove whitespace\n .filter((line) => line.length !== 0) // remove empty lines\n .map((line) => JSON.parse(line) as CrateRecord); // parse\n\n const metadata = await this.getCrateMetadata(registryInfo, packageName);\n\n const result: ReleaseResult = {\n dependencyUrl,\n releases: [],\n };\n\n if (metadata?.homepage) {\n result.homepage = metadata.homepage;\n }\n\n if (metadata?.repository) {\n result.sourceUrl = metadata.repository;\n }\n\n result.releases = lines\n .map((line) => {\n const versionOrig = line.vers;\n const version = versionOrig.replace(regEx(/\\+.*$/), '');\n const release: Release = { version };\n\n if (versionOrig !== version) {\n release.versionOrig = versionOrig;\n }\n\n if (line.yanked) {\n release.isDeprecated = true;\n }\n\n if (line.rust_version) {\n release.constraints = { rust: [line.rust_version] };\n }\n\n if (line.pubtime) {\n release.releaseTimestamp = asTimestamp(line.pubtime);\n }\n\n return release;\n })\n .filter((release) => release.version);\n if (!result.releases.length) {\n return null;\n }\n\n return result;\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n return withCache(\n {\n namespace: `datasource-${CrateDatasource.id}`,\n // TODO: types (#22198)\n key: `${config.registryUrl}/${config.packageName}`,\n cacheable: CrateDatasource.isCratesIo(config.registryUrl),\n fallback: true,\n },\n () => this._getReleases(config),\n );\n }\n\n /**\n * Fetches the registry's config.json which provides the API base URL\n * and download URL template.\n * See: https://doc.rust-lang.org/cargo/reference/registry-index.html#index-configuration\n */\n private async fetchRegistryConfig(\n info: RegistryInfo,\n ): Promise<RegistryConfig | null> {\n const cacheKey = `crate-datasource/registry-config/${info.rawUrl}`;\n const cached = memCache.get<RegistryConfig>(cacheKey);\n if (cached) {\n return cached;\n }\n\n if (info.clonePath) {\n try {\n const configPath = upath.join(info.clonePath, 'config.json');\n const content = await readCacheFile(configPath, 'utf8');\n const parsed = Json.pipe(RegistryConfig).parse(content);\n memCache.set(cacheKey, parsed);\n return parsed;\n } catch {\n logger.debug(\n { registryUrl: info.rawUrl },\n 'Could not read config.json from cloned registry',\n );\n }\n } else {\n try {\n const configUrl = joinUrlParts(info.rawUrl, 'config.json');\n const { body } = await this.http.getJson(configUrl, RegistryConfig);\n memCache.set(cacheKey, body);\n return body;\n } catch {\n logger.debug(\n { registryUrl: info.rawUrl },\n 'Could not fetch registry config.json',\n );\n }\n }\n\n return null;\n }\n\n private async _getCrateMetadata(\n info: RegistryInfo,\n packageName: string,\n ): Promise<CrateMetadata | null> {\n const registryConfig = await this.fetchRegistryConfig(info);\n if (!registryConfig?.api) {\n return null;\n }\n\n const apiBaseUrl = joinUrlParts(registryConfig.api, 'api/v1/');\n\n // The `?include=` suffix is required to avoid unnecessary database queries\n // on the crates.io server. This lets us work around the regular request\n // throttling of one request per second.\n const crateUrl = `${apiBaseUrl}crates/${packageName}?include=`;\n\n logger.trace(\n { crateUrl, packageName, registryUrl: info.rawUrl },\n 'downloading crate metadata',\n );\n\n try {\n const { body } = await this.http.getJson(crateUrl, CrateMetadataResponse);\n return body.crate;\n } catch (err) {\n logger.debug(\n { err, packageName, registryUrl: info.rawUrl },\n 'failed to download crate metadata',\n );\n }\n\n return null;\n }\n\n public getCrateMetadata(\n info: RegistryInfo,\n packageName: string,\n ): Promise<CrateMetadata | null> {\n return withCache(\n {\n namespace: `datasource-${CrateDatasource.id}-metadata`,\n key: `${info.rawUrl}/${packageName}`,\n cacheable: info.flavor === 'crates.io',\n ttlMinutes: 24 * 60, // 24 hours\n },\n () => this._getCrateMetadata(info, packageName),\n );\n }\n\n public async fetchCrateRecordsPayload(\n info: RegistryInfo,\n packageName: string,\n ): Promise<string> {\n if (info.clonePath) {\n const path = upath.join(\n info.clonePath,\n ...CrateDatasource.getIndexSuffix(packageName),\n );\n return readCacheFile(path, 'utf8');\n }\n\n // sparse index\n const packageSuffix = CrateDatasource.getIndexSuffix(\n packageName.toLowerCase(),\n );\n const crateUrl = joinUrlParts(info.rawUrl, ...packageSuffix);\n logger.trace(\n { crateUrl, packageName, registryUrl: info.rawUrl },\n 'fetching crate records from sparse index',\n );\n try {\n return (await this.http.getText(crateUrl)).body;\n } catch (err) {\n this.handleGenericErrors(err);\n }\n }\n\n /**\n * Computes the dependency URL for a crate, given\n * registry information\n */\n private static getDependencyUrl(\n info: RegistryInfo,\n packageName: string,\n ): string {\n switch (info.flavor) {\n case 'crates.io':\n return `https://crates.io/crates/${packageName}`;\n case 'cloudsmith': {\n // input: https://dl.cloudsmith.io/basic/$org/$repo/cargo/index.git\n const tokens = info.url.pathname.split('/');\n const org = tokens[2];\n const repo = tokens[3];\n return `https://cloudsmith.io/~${org}/repos/${repo}/packages/detail/cargo/${packageName}`;\n }\n default:\n return `${info.url}/${packageName}`;\n }\n }\n\n /**\n * Given a Git URL, computes a semi-human-readable name for a folder in which to\n * clone the repository.\n */\n private static cacheDirFromUrl(url: URL): string {\n const proto = url.protocol.replace(regEx(/:$/), '');\n const host = url.hostname;\n const hash = toSha256(url.pathname).substring(0, 7);\n\n return `crate-registry-${proto}-${host}-${hash}`;\n }\n\n private static isSparseRegistry(url: string): boolean {\n const parsed = parseUrl(url);\n if (!parsed) {\n return false;\n }\n return parsed.protocol.startsWith('sparse+');\n }\n\n /**\n * Fetches information about a registry, by url.\n * If no url is given, assumes crates.io.\n * If an url is given, assumes it's a valid Git repository\n * url and clones it to cache.\n */\n private static async fetchRegistryInfo({\n packageName,\n registryUrl,\n }: GetReleasesConfig): Promise<RegistryInfo | null> {\n /* v8 ignore next -- should never happen */\n if (!registryUrl) {\n return null;\n }\n\n const isSparseRegistry = CrateDatasource.isSparseRegistry(registryUrl);\n const registryFetchUrl = isSparseRegistry\n ? registryUrl.replace(regEx(/^sparse\\+/), '')\n : registryUrl;\n\n const url = parseUrl(registryFetchUrl);\n if (!url) {\n logger.debug(`Could not parse registry URL ${registryFetchUrl}`);\n return null;\n }\n\n let flavor: RegistryFlavor;\n if (url.hostname === 'index.crates.io') {\n flavor = 'crates.io';\n } else if (url.hostname === 'dl.cloudsmith.io') {\n flavor = 'cloudsmith';\n } else {\n flavor = 'other';\n }\n\n const registry: RegistryInfo = {\n flavor,\n rawUrl: registryFetchUrl,\n url,\n };\n\n if (\n registry.flavor !== 'crates.io' &&\n !GlobalConfig.get('allowCustomCrateRegistries')\n ) {\n logger.warn(\n 'crate datasource: allowCustomCrateRegistries=true is required for registries other than crates.io, bailing out',\n );\n return null;\n }\n if (!isSparseRegistry) {\n const cacheKey = `crate-datasource/registry-clone-path/${registryFetchUrl}`;\n const lockKey = registryFetchUrl;\n\n const executionTimeout = GlobalConfig.get('executionTimeout') * 60 * 1000;\n const gitTimeout = GlobalConfig.get('gitTimeout') || executionTimeout;\n const releaseLock = await acquireLock(\n lockKey,\n 'crate-registry',\n gitTimeout,\n );\n try {\n const cached = memCache.get<CloneResult>(cacheKey);\n\n if (cached?.err) {\n logger.warn(\n { err: cached.err, packageName, registryFetchUrl },\n 'Previous git clone failed, bailing out.',\n );\n return null;\n }\n\n if (cached?.clonePath) {\n registry.clonePath = cached.clonePath;\n return registry;\n }\n\n const clonePath = upath.join(\n privateCacheDir(),\n CrateDatasource.cacheDirFromUrl(url),\n );\n\n const result = await CrateDatasource.clone(\n registryFetchUrl,\n clonePath,\n packageName,\n );\n\n memCache.set(cacheKey, result);\n\n if (result.err) {\n logger.warn(\n { err: result.err, packageName, registryFetchUrl },\n 'Git clone failed, bailing out.',\n );\n return null;\n }\n\n registry.clonePath = result.clonePath;\n } finally {\n releaseLock();\n }\n }\n\n return registry;\n }\n\n private static async clone(\n registryFetchUrl: string,\n clonePath: string,\n packageName: string,\n ): Promise<CloneResult> {\n logger.info(\n { clonePath, registryFetchUrl },\n `Cloning private cargo registry`,\n );\n\n const git = createSimpleGit({\n config: { maxConcurrentProcesses: 1 },\n });\n\n try {\n await git.clone(registryFetchUrl, clonePath, {\n '--depth': 1,\n });\n return { clonePath };\n } catch (err) {\n if (\n err.message.includes(\n 'fatal: dumb http transport does not support shallow capabilities',\n )\n ) {\n logger.info(\n { packageName, registryFetchUrl },\n 'failed to shallow clone git registry, doing full clone',\n );\n try {\n await git.clone(registryFetchUrl, clonePath);\n return { clonePath };\n } catch (err) {\n logger.warn(\n { err, packageName, registryFetchUrl },\n 'failed cloning git registry',\n );\n return { err };\n }\n } else {\n logger.warn(\n { err, packageName, registryFetchUrl },\n 'failed cloning git registry',\n );\n return { err };\n }\n }\n }\n\n private static isCratesIo(registryUrl: string | undefined): boolean {\n if (!registryUrl) {\n return false;\n }\n const normalized = registryUrl.replace(regEx(/^sparse\\+/), '');\n const parsed = parseUrl(normalized);\n return parsed?.hostname === 'index.crates.io';\n }\n\n public static getIndexSuffix(packageName: string): string[] {\n const len = packageName.length;\n\n if (len === 1) {\n return ['1', packageName];\n }\n if (len === 2) {\n return ['2', packageName];\n }\n if (len === 3) {\n return ['3', packageName[0], packageName];\n }\n\n return [packageName.slice(0, 2), packageName.slice(2, 4), packageName];\n }\n\n private async _postprocessRelease(\n { packageName, registryUrl }: PostprocessReleaseConfig,\n release: Release,\n ): Promise<PostprocessReleaseResult> {\n if (release.releaseTimestamp) {\n return release;\n }\n\n // Look up the registry config from cache (populated during getReleases)\n const rawUrl = registryUrl?.replace(regEx(/^sparse\\+/), '');\n if (!rawUrl) {\n return release;\n }\n const cacheKey = `crate-datasource/registry-config/${rawUrl}`;\n const config = memCache.get<{ dl: string; api?: string }>(cacheKey);\n if (!config?.api) {\n return release;\n }\n\n const apiBaseUrl = joinUrlParts(config.api, 'api/v1/');\n const url = `${apiBaseUrl}crates/${packageName}/${release.versionOrig ?? release.version}`;\n logger.trace(\n { url, packageName, version: release.version, registryUrl },\n 'fetching crate release timestamp',\n );\n // Getting release timestamp could become unnecessary if the manual backfill of `pubtime` mentioned in\n // https://github.com/rust-lang/cargo/issues/15491 is done for all packages.\n const { body: releaseTimestamp } = await this.http.getJson(\n url,\n { cacheProvider: memCacheProvider },\n ReleaseTimestamp,\n );\n release.releaseTimestamp = releaseTimestamp;\n return release;\n }\n\n override postprocessRelease(\n config: PostprocessReleaseConfig,\n release: Release,\n ): Promise<PostprocessReleaseResult> {\n return withCache(\n {\n namespace: `datasource-crate`,\n key: `postprocessRelease:${config.registryUrl}:${config.packageName}:${release.version}`,\n ttlMinutes: 7 * 24 * 60,\n cacheable: CrateDatasource.isCratesIo(config.registryUrl ?? undefined),\n },\n () => this._postprocessRelease(config, release),\n );\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["cargoVersioning.id","memCache.get","HttpError","packageCache.get","packageCache.set"],"sources":["../../../../lib/modules/datasource/crate/index.ts"],"sourcesContent":["import { isBoolean } from '@sindresorhus/is';\nimport upath from 'upath';\nimport { GlobalConfig } from '../../../config/global.ts';\nimport { logger } from '../../../logger/index.ts';\nimport * as memCache from '../../../util/cache/memory/index.ts';\nimport * as packageCache from '../../../util/cache/package/index.ts';\nimport { withCache } from '../../../util/cache/package/with-cache.ts';\nimport { privateCacheDir, readCacheFile } from '../../../util/fs/index.ts';\nimport { createSimpleGit } from '../../../util/git/index.ts';\nimport { toSha256 } from '../../../util/hash.ts';\nimport { memCacheProvider } from '../../../util/http/cache/memory-http-cache-provider.ts';\nimport { HttpError } from '../../../util/http/index.ts';\nimport { acquireLock } from '../../../util/mutex.ts';\nimport { newlineRegex, regEx } from '../../../util/regex.ts';\nimport { Json } from '../../../util/schema-utils/index.ts';\nimport { asTimestamp } from '../../../util/timestamp.ts';\nimport { joinUrlParts, parseUrl } from '../../../util/url.ts';\nimport * as cargoVersioning from '../../versioning/cargo/index.ts';\nimport { Datasource } from '../datasource.ts';\nimport type {\n GetReleasesConfig,\n PostprocessReleaseConfig,\n PostprocessReleaseResult,\n Release,\n ReleaseResult,\n} from '../types.ts';\nimport type { CrateMetadata } from './schema.ts';\nimport {\n CrateMetadataResponse,\n RegistryConfig,\n ReleaseTimestamp,\n} from './schema.ts';\nimport type { CrateRecord, RegistryFlavor, RegistryInfo } from './types.ts';\n\ntype CloneResult =\n | {\n err: Error;\n clonePath?: undefined;\n }\n | {\n clonePath: string;\n err?: undefined;\n };\n\nexport class CrateDatasource extends Datasource {\n static readonly id = 'crate';\n\n constructor() {\n super(CrateDatasource.id);\n }\n\n override defaultRegistryUrls = ['sparse+https://index.crates.io/'];\n\n override defaultVersioning = cargoVersioning.id;\n\n override readonly sourceUrlSupport = 'package';\n override readonly sourceUrlNote =\n 'The source URL is determined from the `repository` field in the results.';\n\n override readonly releaseTimestampSupport = true;\n override readonly releaseTimestampNote =\n 'The release timestamp is determined from `pubtime` field from crates.io index if available, or `version.created_at` field from crates.io API otherwise.';\n\n private async _getReleases({\n packageName,\n registryUrl,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n /* v8 ignore if -- should never happen */\n if (!registryUrl) {\n logger.warn(\n 'crate datasource: No registryUrl specified, cannot perform getReleases',\n );\n return null;\n }\n\n const registryInfo = await CrateDatasource.fetchRegistryInfo({\n packageName,\n registryUrl,\n });\n if (!registryInfo) {\n logger.debug(`Could not fetch registry info from ${registryUrl}`);\n return null;\n }\n\n const dependencyUrl = CrateDatasource.getDependencyUrl(\n registryInfo,\n packageName,\n );\n\n const payload = await this.fetchCrateRecordsPayload(\n registryInfo,\n packageName,\n );\n const lines = payload\n .split(newlineRegex) // break into lines\n .map((line) => line.trim()) // remove whitespace\n .filter((line) => line.length !== 0) // remove empty lines\n .map((line) => JSON.parse(line) as CrateRecord); // parse\n\n const metadata = await this.getCrateMetadata(registryInfo, packageName);\n\n const result: ReleaseResult = {\n dependencyUrl,\n releases: [],\n };\n\n if (metadata?.homepage) {\n result.homepage = metadata.homepage;\n }\n\n if (metadata?.repository) {\n result.sourceUrl = metadata.repository;\n }\n\n result.releases = lines\n .map((line) => {\n const versionOrig = line.vers;\n const version = versionOrig.replace(regEx(/\\+.*$/), '');\n const release: Release = { version };\n\n if (versionOrig !== version) {\n release.versionOrig = versionOrig;\n }\n\n if (line.yanked) {\n release.isDeprecated = true;\n }\n\n if (line.rust_version) {\n release.constraints = { rust: [line.rust_version] };\n }\n\n if (line.pubtime) {\n release.releaseTimestamp = asTimestamp(line.pubtime);\n }\n\n return release;\n })\n .filter((release) => release.version);\n if (!result.releases.length) {\n return null;\n }\n\n return result;\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n return withCache(\n {\n namespace: `datasource-${CrateDatasource.id}`,\n // TODO: types (#22198)\n key: `${config.registryUrl}/${config.packageName}`,\n cacheable: CrateDatasource.isCratesIo(config.registryUrl),\n fallback: true,\n },\n () => this._getReleases(config),\n );\n }\n\n /**\n * Fetches the registry's config.json which provides the API base URL\n * and download URL template.\n * See: https://doc.rust-lang.org/cargo/reference/registry-index.html#index-configuration\n */\n private async fetchRegistryConfig(\n info: RegistryInfo,\n ): Promise<RegistryConfig | null> {\n const cacheKey = `crate-datasource/registry-config/${info.rawUrl}`;\n const cached = memCache.get<RegistryConfig>(cacheKey);\n if (cached) {\n return cached;\n }\n\n if (info.clonePath) {\n try {\n const configPath = upath.join(info.clonePath, 'config.json');\n const content = await readCacheFile(configPath, 'utf8');\n const parsed = Json.pipe(RegistryConfig).parse(content);\n memCache.set(cacheKey, parsed);\n return parsed;\n } catch {\n logger.debug(\n { registryUrl: info.rawUrl },\n 'Could not read config.json from cloned registry',\n );\n }\n } else {\n try {\n const configUrl = joinUrlParts(info.rawUrl, 'config.json');\n const { body } = await this.http.getJson(configUrl, RegistryConfig);\n memCache.set(cacheKey, body);\n return body;\n } catch {\n logger.debug(\n { registryUrl: info.rawUrl },\n 'Could not fetch registry config.json',\n );\n }\n }\n\n return null;\n }\n\n /**\n * The registry web API specification only defines publish, yank, unyank,\n * owners, search and login endpoints, see\n * https://doc.rust-lang.org/cargo/reference/registry-web-api.html.\n * `GET api/v1/crates/<name>` and `GET api/v1/crates/<name>/<version>` are\n * crates.io extensions. Registries which mirror the crates.io index keep\n * `api` pointing at crates.io and are used without probing. Any other\n * registry is probed once and remembered as unsupported when it answers\n * 404, since private registries (Artifactory, CodeArtifact, ...) advertise\n * an `api` URL for publishing only.\n */\n private static isCratesIoApi(api: string): boolean {\n return parseUrl(api)?.hostname === 'crates.io';\n }\n\n private static isNotFound(err: unknown): boolean {\n return err instanceof HttpError && err.response?.statusCode === 404;\n }\n\n /**\n * The marker is keyed by the `api` URL, since the missing endpoints are a\n * property of the API server, not of the index that points to it.\n */\n private async isReadApiUnsupported(api: string): Promise<boolean> {\n if (CrateDatasource.isCratesIoApi(api)) {\n return false;\n }\n\n const memKey = `crate-datasource/registry-api-unsupported/${api}`;\n const cached = memCache.get<boolean>(memKey);\n if (isBoolean(cached)) {\n return cached;\n }\n\n const persisted = await packageCache.get<boolean>(\n 'datasource-crate-registry-api',\n api,\n );\n const unsupported = persisted === true;\n memCache.set(memKey, unsupported);\n return unsupported;\n }\n\n private async markReadApiUnsupported(\n registryUrl: string,\n api: string,\n ): Promise<void> {\n logger.debug(\n { registryUrl, api },\n 'Registry does not implement the crates.io read API, skipping crate metadata and release timestamp lookups',\n );\n memCache.set(`crate-datasource/registry-api-unsupported/${api}`, true);\n await packageCache.set('datasource-crate-registry-api', api, true, 24 * 60);\n }\n\n private async _getCrateMetadata(\n info: RegistryInfo,\n packageName: string,\n ): Promise<CrateMetadata | null> {\n const registryConfig = await this.fetchRegistryConfig(info);\n if (!registryConfig?.api) {\n return null;\n }\n\n const { api } = registryConfig;\n if (await this.isReadApiUnsupported(api)) {\n return null;\n }\n\n const apiBaseUrl = joinUrlParts(api, 'api/v1/');\n\n // The `?include=` suffix is required to avoid unnecessary database queries\n // on the crates.io server. This lets us work around the regular request\n // throttling of one request per second.\n const crateUrl = `${apiBaseUrl}crates/${packageName}?include=`;\n\n logger.trace(\n { crateUrl, packageName, registryUrl: info.rawUrl },\n 'downloading crate metadata',\n );\n\n try {\n const { body } = await this.http.getJson(crateUrl, CrateMetadataResponse);\n return body.crate;\n } catch (err) {\n // The index lookup preceding this call found the crate, so a 404 means\n // the registry does not implement the endpoint at all\n if (\n !CrateDatasource.isCratesIoApi(api) &&\n CrateDatasource.isNotFound(err)\n ) {\n await this.markReadApiUnsupported(info.rawUrl, api);\n return null;\n }\n logger.debug(\n { err, packageName, registryUrl: info.rawUrl },\n 'failed to download crate metadata',\n );\n }\n\n return null;\n }\n\n public getCrateMetadata(\n info: RegistryInfo,\n packageName: string,\n ): Promise<CrateMetadata | null> {\n return withCache(\n {\n namespace: `datasource-${CrateDatasource.id}-metadata`,\n key: `${info.rawUrl}/${packageName}`,\n cacheable: info.flavor === 'crates.io',\n ttlMinutes: 24 * 60, // 24 hours\n },\n () => this._getCrateMetadata(info, packageName),\n );\n }\n\n public async fetchCrateRecordsPayload(\n info: RegistryInfo,\n packageName: string,\n ): Promise<string> {\n if (info.clonePath) {\n const path = upath.join(\n info.clonePath,\n ...CrateDatasource.getIndexSuffix(packageName),\n );\n return readCacheFile(path, 'utf8');\n }\n\n // sparse index\n const packageSuffix = CrateDatasource.getIndexSuffix(\n packageName.toLowerCase(),\n );\n const crateUrl = joinUrlParts(info.rawUrl, ...packageSuffix);\n logger.trace(\n { crateUrl, packageName, registryUrl: info.rawUrl },\n 'fetching crate records from sparse index',\n );\n try {\n return (await this.http.getText(crateUrl)).body;\n } catch (err) {\n this.handleGenericErrors(err);\n }\n }\n\n /**\n * Computes the dependency URL for a crate, given\n * registry information\n */\n private static getDependencyUrl(\n info: RegistryInfo,\n packageName: string,\n ): string {\n switch (info.flavor) {\n case 'crates.io':\n return `https://crates.io/crates/${packageName}`;\n case 'cloudsmith': {\n // input: https://dl.cloudsmith.io/basic/$org/$repo/cargo/index.git\n const tokens = info.url.pathname.split('/');\n const org = tokens[2];\n const repo = tokens[3];\n return `https://cloudsmith.io/~${org}/repos/${repo}/packages/detail/cargo/${packageName}`;\n }\n default:\n return `${info.url}/${packageName}`;\n }\n }\n\n /**\n * Given a Git URL, computes a semi-human-readable name for a folder in which to\n * clone the repository.\n */\n private static cacheDirFromUrl(url: URL): string {\n const proto = url.protocol.replace(regEx(/:$/), '');\n const host = url.hostname;\n const hash = toSha256(url.pathname).substring(0, 7);\n\n return `crate-registry-${proto}-${host}-${hash}`;\n }\n\n private static isSparseRegistry(url: string): boolean {\n const parsed = parseUrl(url);\n if (!parsed) {\n return false;\n }\n return parsed.protocol.startsWith('sparse+');\n }\n\n /**\n * Fetches information about a registry, by url.\n * If no url is given, assumes crates.io.\n * If an url is given, assumes it's a valid Git repository\n * url and clones it to cache.\n */\n private static async fetchRegistryInfo({\n packageName,\n registryUrl,\n }: GetReleasesConfig): Promise<RegistryInfo | null> {\n /* v8 ignore next -- should never happen */\n if (!registryUrl) {\n return null;\n }\n\n const isSparseRegistry = CrateDatasource.isSparseRegistry(registryUrl);\n const registryFetchUrl = isSparseRegistry\n ? registryUrl.replace(regEx(/^sparse\\+/), '')\n : registryUrl;\n\n const url = parseUrl(registryFetchUrl);\n if (!url) {\n logger.debug(`Could not parse registry URL ${registryFetchUrl}`);\n return null;\n }\n\n let flavor: RegistryFlavor;\n if (url.hostname === 'index.crates.io') {\n flavor = 'crates.io';\n } else if (url.hostname === 'dl.cloudsmith.io') {\n flavor = 'cloudsmith';\n } else {\n flavor = 'other';\n }\n\n const registry: RegistryInfo = {\n flavor,\n rawUrl: registryFetchUrl,\n url,\n };\n\n if (\n registry.flavor !== 'crates.io' &&\n !GlobalConfig.get('allowCustomCrateRegistries')\n ) {\n logger.warn(\n 'crate datasource: allowCustomCrateRegistries=true is required for registries other than crates.io, bailing out',\n );\n return null;\n }\n if (!isSparseRegistry) {\n const cacheKey = `crate-datasource/registry-clone-path/${registryFetchUrl}`;\n const lockKey = registryFetchUrl;\n\n const executionTimeout = GlobalConfig.get('executionTimeout') * 60 * 1000;\n const gitTimeout = GlobalConfig.get('gitTimeout') || executionTimeout;\n const releaseLock = await acquireLock(\n lockKey,\n 'crate-registry',\n gitTimeout,\n );\n try {\n const cached = memCache.get<CloneResult>(cacheKey);\n\n if (cached?.err) {\n logger.warn(\n { err: cached.err, packageName, registryFetchUrl },\n 'Previous git clone failed, bailing out.',\n );\n return null;\n }\n\n if (cached?.clonePath) {\n registry.clonePath = cached.clonePath;\n return registry;\n }\n\n const clonePath = upath.join(\n privateCacheDir(),\n CrateDatasource.cacheDirFromUrl(url),\n );\n\n const result = await CrateDatasource.clone(\n registryFetchUrl,\n clonePath,\n packageName,\n );\n\n memCache.set(cacheKey, result);\n\n if (result.err) {\n logger.warn(\n { err: result.err, packageName, registryFetchUrl },\n 'Git clone failed, bailing out.',\n );\n return null;\n }\n\n registry.clonePath = result.clonePath;\n } finally {\n releaseLock();\n }\n }\n\n return registry;\n }\n\n private static async clone(\n registryFetchUrl: string,\n clonePath: string,\n packageName: string,\n ): Promise<CloneResult> {\n logger.info(\n { clonePath, registryFetchUrl },\n `Cloning private cargo registry`,\n );\n\n const git = createSimpleGit({\n config: { maxConcurrentProcesses: 1 },\n });\n\n try {\n await git.clone(registryFetchUrl, clonePath, {\n '--depth': 1,\n });\n return { clonePath };\n } catch (err) {\n if (\n err.message.includes(\n 'fatal: dumb http transport does not support shallow capabilities',\n )\n ) {\n logger.info(\n { packageName, registryFetchUrl },\n 'failed to shallow clone git registry, doing full clone',\n );\n try {\n await git.clone(registryFetchUrl, clonePath);\n return { clonePath };\n } catch (err) {\n logger.warn(\n { err, packageName, registryFetchUrl },\n 'failed cloning git registry',\n );\n return { err };\n }\n } else {\n logger.warn(\n { err, packageName, registryFetchUrl },\n 'failed cloning git registry',\n );\n return { err };\n }\n }\n }\n\n private static isCratesIo(registryUrl: string | undefined): boolean {\n if (!registryUrl) {\n return false;\n }\n const normalized = registryUrl.replace(regEx(/^sparse\\+/), '');\n const parsed = parseUrl(normalized);\n return parsed?.hostname === 'index.crates.io';\n }\n\n public static getIndexSuffix(packageName: string): string[] {\n const len = packageName.length;\n\n if (len === 1) {\n return ['1', packageName];\n }\n if (len === 2) {\n return ['2', packageName];\n }\n if (len === 3) {\n return ['3', packageName[0], packageName];\n }\n\n return [packageName.slice(0, 2), packageName.slice(2, 4), packageName];\n }\n\n private async _postprocessRelease(\n { packageName, registryUrl }: PostprocessReleaseConfig,\n release: Release,\n ): Promise<PostprocessReleaseResult> {\n if (release.releaseTimestamp) {\n return release;\n }\n\n // Look up the registry config from cache (populated during getReleases)\n const rawUrl = registryUrl?.replace(regEx(/^sparse\\+/), '');\n if (!rawUrl) {\n return release;\n }\n const cacheKey = `crate-datasource/registry-config/${rawUrl}`;\n const config = memCache.get<RegistryConfig>(cacheKey);\n if (!config?.api) {\n return release;\n }\n\n const { api } = config;\n if (await this.isReadApiUnsupported(api)) {\n return release;\n }\n\n const apiBaseUrl = joinUrlParts(api, 'api/v1/');\n const url = `${apiBaseUrl}crates/${packageName}/${release.versionOrig ?? release.version}`;\n logger.trace(\n { url, packageName, version: release.version, registryUrl },\n 'fetching crate release timestamp',\n );\n // Getting release timestamp could become unnecessary if the manual backfill of `pubtime` mentioned in\n // https://github.com/rust-lang/cargo/issues/15491 is done for all packages.\n try {\n const { body: releaseTimestamp } = await this.http.getJson(\n url,\n { cacheProvider: memCacheProvider },\n ReleaseTimestamp,\n );\n release.releaseTimestamp = releaseTimestamp;\n } catch (err) {\n if (\n CrateDatasource.isCratesIoApi(api) ||\n !CrateDatasource.isNotFound(err)\n ) {\n throw err;\n }\n await this.markReadApiUnsupported(rawUrl, api);\n }\n return release;\n }\n\n override postprocessRelease(\n config: PostprocessReleaseConfig,\n release: Release,\n ): Promise<PostprocessReleaseResult> {\n return withCache(\n {\n namespace: `datasource-crate`,\n key: `postprocessRelease:${config.registryUrl}:${config.packageName}:${release.version}`,\n ttlMinutes: 7 * 24 * 60,\n cacheable: CrateDatasource.isCratesIo(config.registryUrl ?? undefined),\n },\n () => this._postprocessRelease(config, release),\n );\n }\n}\n"],"mappings":"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"}
@@ -1,68 +1,19 @@
1
- import { withCache } from "../../../util/cache/package/with-cache.js";
2
- import { Datasource } from "../datasource.js";
3
1
  import { ForgejoHttp } from "../../../util/http/forgejo.js";
4
- import { Commits, Tag } from "../forgejo-tags/schema.js";
5
- import { ForgejoTagsDatasource } from "../forgejo-tags/index.js";
6
- import { Releases } from "./schema.js";
2
+ import { GiteaReleasesDatasource } from "../gitea-releases/index.js";
7
3
  //#region lib/modules/datasource/forgejo-releases/index.ts
8
- var ForgejoReleasesDatasource = class ForgejoReleasesDatasource extends Datasource {
4
+ /**
5
+ * Forgejo is a fork of Gitea and serves the same API, so the lookup is
6
+ * inherited and only the identity of the datasource differs.
7
+ */
8
+ var ForgejoReleasesDatasource = class ForgejoReleasesDatasource extends GiteaReleasesDatasource {
9
9
  static id = "forgejo-releases";
10
- http = new ForgejoHttp(ForgejoReleasesDatasource.id);
11
10
  static defaultRegistryUrls = ["https://code.forgejo.org"];
12
- static cacheNamespace = `datasource-${ForgejoReleasesDatasource.id}`;
13
- releaseTimestampSupport = true;
14
- releaseTimestampNote = "The release timestamp is determined from the `published_at` field in the results.";
15
- sourceUrlSupport = "package";
16
- sourceUrlNote = "The source URL is determined by using the `packageName` and `registryUrl`.";
11
+ defaultRegistryUrls = ForgejoReleasesDatasource.defaultRegistryUrls;
12
+ cacheNamespace = "datasource-forgejo-releases";
13
+ http = new ForgejoHttp(ForgejoReleasesDatasource.id);
17
14
  constructor() {
18
15
  super(ForgejoReleasesDatasource.id);
19
16
  }
20
- async _getReleases({ registryUrl, packageName: repo }) {
21
- const url = `${ForgejoTagsDatasource.getApiUrl(registryUrl)}repos/${repo}/releases?draft=false`;
22
- const tags = (await this.http.getJson(url, { paginate: true }, Releases)).body;
23
- return {
24
- sourceUrl: ForgejoTagsDatasource.getSourceUrl(repo, registryUrl),
25
- registryUrl: ForgejoTagsDatasource.getRegistryURL(registryUrl),
26
- releases: tags.map(({ tag_name, published_at, prerelease }) => ({
27
- version: tag_name,
28
- gitRef: tag_name,
29
- releaseTimestamp: published_at,
30
- isStable: !prerelease
31
- }))
32
- };
33
- }
34
- getReleases(config) {
35
- return withCache({
36
- namespace: ForgejoReleasesDatasource.cacheNamespace,
37
- key: ForgejoTagsDatasource.getCacheKey(config.registryUrl, config.packageName, "releases"),
38
- fallback: true
39
- }, () => this._getReleases(config));
40
- }
41
- async _getTagCommit(registryUrl, repo, tag) {
42
- const url = `${ForgejoTagsDatasource.getApiUrl(registryUrl)}repos/${repo}/tags/${tag}`;
43
- const { body } = await this.http.getJson(url, Tag);
44
- return body.commit.sha;
45
- }
46
- getTagCommit(registryUrl, repo, tag) {
47
- return withCache({
48
- namespace: ForgejoReleasesDatasource.cacheNamespace,
49
- key: ForgejoTagsDatasource.getCacheKey(registryUrl, repo, `tag-${tag}`)
50
- }, () => this._getTagCommit(registryUrl, repo, tag));
51
- }
52
- async _getDigest({ packageName: repo, registryUrl }, newValue) {
53
- if (newValue?.length) return this.getTagCommit(registryUrl, repo, newValue);
54
- const url = `${ForgejoTagsDatasource.getApiUrl(registryUrl)}repos/${repo}/commits?stat=false&verification=false&files=false&page=1&limit=1`;
55
- const { body } = await this.http.getJson(url, Commits);
56
- if (body.length === 0) return null;
57
- return body[0].sha;
58
- }
59
- getDigest(config, newValue) {
60
- return withCache({
61
- namespace: ForgejoReleasesDatasource.cacheNamespace,
62
- key: ForgejoTagsDatasource.getCacheKey(config.registryUrl, config.packageName, "digest"),
63
- fallback: true
64
- }, () => this._getDigest(config, newValue));
65
- }
66
17
  };
67
18
  //#endregion
68
19
  export { ForgejoReleasesDatasource };
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../../lib/modules/datasource/forgejo-releases/index.ts"],"sourcesContent":["import type { PackageCacheNamespace } from '../../../util/cache/package/types.ts';\nimport { withCache } from '../../../util/cache/package/with-cache.ts';\nimport { ForgejoHttp } from '../../../util/http/forgejo.ts';\nimport { Datasource } from '../datasource.ts';\nimport { ForgejoTagsDatasource } from '../forgejo-tags/index.ts';\nimport { Commits, Tag } from '../forgejo-tags/schema.ts';\nimport type {\n DigestConfig,\n GetReleasesConfig,\n ReleaseResult,\n} from '../types.ts';\nimport { Releases } from './schema.ts';\n\nexport class ForgejoReleasesDatasource extends Datasource {\n static readonly id = 'forgejo-releases';\n\n override http = new ForgejoHttp(ForgejoReleasesDatasource.id);\n\n static readonly defaultRegistryUrls = ['https://code.forgejo.org'];\n\n private static readonly cacheNamespace: PackageCacheNamespace = `datasource-${ForgejoReleasesDatasource.id}`;\n\n override readonly releaseTimestampSupport = true;\n override readonly releaseTimestampNote =\n 'The release timestamp is determined from the `published_at` field in the results.';\n override readonly sourceUrlSupport = 'package';\n override readonly sourceUrlNote =\n 'The source URL is determined by using the `packageName` and `registryUrl`.';\n\n constructor() {\n super(ForgejoReleasesDatasource.id);\n }\n\n // getReleases fetches list of tags for the repository\n private async _getReleases({\n registryUrl,\n packageName: repo,\n }: GetReleasesConfig): Promise<ReleaseResult | null> {\n const url = `${ForgejoTagsDatasource.getApiUrl(\n registryUrl,\n )}repos/${repo}/releases?draft=false`;\n const tags = (\n await this.http.getJson(\n url,\n {\n paginate: true,\n },\n Releases,\n )\n ).body;\n\n const dependency: ReleaseResult = {\n sourceUrl: ForgejoTagsDatasource.getSourceUrl(repo, registryUrl),\n registryUrl: ForgejoTagsDatasource.getRegistryURL(registryUrl),\n releases: tags.map(({ tag_name, published_at, prerelease }) => ({\n version: tag_name,\n gitRef: tag_name,\n releaseTimestamp: published_at,\n isStable: !prerelease,\n })),\n };\n\n return dependency;\n }\n\n getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {\n return withCache(\n {\n namespace: ForgejoReleasesDatasource.cacheNamespace,\n key: ForgejoTagsDatasource.getCacheKey(\n config.registryUrl,\n config.packageName,\n 'releases',\n ),\n fallback: true,\n },\n () => this._getReleases(config),\n );\n }\n\n // getTagCommit fetched the commit has for specified tag\n private async _getTagCommit(\n registryUrl: string | undefined,\n repo: string,\n tag: string,\n ): Promise<string | null> {\n const url = `${ForgejoTagsDatasource.getApiUrl(\n registryUrl,\n )}repos/${repo}/tags/${tag}`;\n\n const { body } = await this.http.getJson(url, Tag);\n\n return body.commit.sha;\n }\n\n getTagCommit(\n registryUrl: string | undefined,\n repo: string,\n tag: string,\n ): Promise<string | null> {\n return withCache(\n {\n namespace: ForgejoReleasesDatasource.cacheNamespace,\n key: ForgejoTagsDatasource.getCacheKey(registryUrl, repo, `tag-${tag}`),\n },\n () => this._getTagCommit(registryUrl, repo, tag),\n );\n }\n\n // getDigest fetched the latest commit for repository main branch\n // however, if newValue is provided, then getTagCommit is called\n private async _getDigest(\n { packageName: repo, registryUrl }: DigestConfig,\n newValue?: string,\n ): Promise<string | null> {\n if (newValue?.length) {\n return this.getTagCommit(registryUrl, repo, newValue);\n }\n\n const url = `${ForgejoTagsDatasource.getApiUrl(\n registryUrl,\n )}repos/${repo}/commits?stat=false&verification=false&files=false&page=1&limit=1`;\n const { body } = await this.http.getJson(url, Commits);\n\n if (body.length === 0) {\n return null;\n }\n\n return body[0].sha;\n }\n\n override getDigest(\n config: DigestConfig,\n newValue?: string,\n ): Promise<string | null> {\n return withCache(\n {\n namespace: ForgejoReleasesDatasource.cacheNamespace,\n key: ForgejoTagsDatasource.getCacheKey(\n config.registryUrl,\n config.packageName,\n 'digest',\n ),\n fallback: true,\n },\n () => this._getDigest(config, newValue),\n );\n }\n}\n"],"mappings":";;;;;;;AAaA,IAAa,4BAAb,MAAa,kCAAkC,WAAW;CACxD,OAAgB,KAAK;CAErB,OAAgB,IAAI,YAAY,0BAA0B,EAAE;CAE5D,OAAgB,sBAAsB,CAAC,0BAA0B;CAEjE,OAAwB,iBAAwC,cAAc,0BAA0B;CAExG,0BAA4C;CAC5C,uBACE;CACF,mBAAqC;CACrC,gBACE;CAEF,cAAc;EACZ,MAAM,0BAA0B,EAAE;CACpC;CAGA,MAAc,aAAa,EACzB,aACA,aAAa,QACsC;EACnD,MAAM,MAAM,GAAG,sBAAsB,UACnC,WACF,EAAE,QAAQ,KAAK;EACf,MAAM,QACJ,MAAM,KAAK,KAAK,QACd,KACA,EACE,UAAU,KACZ,GACA,QACF,EAAA,CACA;EAaF,OAAO;GAVL,WAAW,sBAAsB,aAAa,MAAM,WAAW;GAC/D,aAAa,sBAAsB,eAAe,WAAW;GAC7D,UAAU,KAAK,KAAK,EAAE,UAAU,cAAc,kBAAkB;IAC9D,SAAS;IACT,QAAQ;IACR,kBAAkB;IAClB,UAAU,CAAC;GACb,EAAE;EAGY;CAClB;CAEA,YAAY,QAA0D;EACpE,OAAO,UACL;GACE,WAAW,0BAA0B;GACrC,KAAK,sBAAsB,YACzB,OAAO,aACP,OAAO,aACP,UACF;GACA,UAAU;EACZ,SACM,KAAK,aAAa,MAAM,CAChC;CACF;CAGA,MAAc,cACZ,aACA,MACA,KACwB;EACxB,MAAM,MAAM,GAAG,sBAAsB,UACnC,WACF,EAAE,QAAQ,KAAK,QAAQ;EAEvB,MAAM,EAAE,SAAS,MAAM,KAAK,KAAK,QAAQ,KAAK,GAAG;EAEjD,OAAO,KAAK,OAAO;CACrB;CAEA,aACE,aACA,MACA,KACwB;EACxB,OAAO,UACL;GACE,WAAW,0BAA0B;GACrC,KAAK,sBAAsB,YAAY,aAAa,MAAM,OAAO,KAAK;EACxE,SACM,KAAK,cAAc,aAAa,MAAM,GAAG,CACjD;CACF;CAIA,MAAc,WACZ,EAAE,aAAa,MAAM,eACrB,UACwB;EACxB,IAAI,UAAU,QACZ,OAAO,KAAK,aAAa,aAAa,MAAM,QAAQ;EAGtD,MAAM,MAAM,GAAG,sBAAsB,UACnC,WACF,EAAE,QAAQ,KAAK;EACf,MAAM,EAAE,SAAS,MAAM,KAAK,KAAK,QAAQ,KAAK,OAAO;EAErD,IAAI,KAAK,WAAW,GAClB,OAAO;EAGT,OAAO,KAAK,EAAE,CAAC;CACjB;CAEA,UACE,QACA,UACwB;EACxB,OAAO,UACL;GACE,WAAW,0BAA0B;GACrC,KAAK,sBAAsB,YACzB,OAAO,aACP,OAAO,aACP,QACF;GACA,UAAU;EACZ,SACM,KAAK,WAAW,QAAQ,QAAQ,CACxC;CACF;AACF"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../../lib/modules/datasource/forgejo-releases/index.ts"],"sourcesContent":["import type { DatasourceName } from '../../../datasource-list.generated.ts';\nimport type { PackageCacheNamespace } from '../../../util/cache/package/types.ts';\nimport { ForgejoHttp } from '../../../util/http/forgejo.ts';\nimport { GiteaReleasesDatasource } from '../gitea-releases/index.ts';\n\n/**\n * Forgejo is a fork of Gitea and serves the same API, so the lookup is\n * inherited and only the identity of the datasource differs.\n */\nexport class ForgejoReleasesDatasource extends GiteaReleasesDatasource {\n static override readonly id: DatasourceName = 'forgejo-releases';\n\n static override readonly defaultRegistryUrls = ['https://code.forgejo.org'];\n\n override readonly defaultRegistryUrls =\n ForgejoReleasesDatasource.defaultRegistryUrls;\n\n protected override readonly cacheNamespace: PackageCacheNamespace =\n 'datasource-forgejo-releases';\n\n override http = new ForgejoHttp(ForgejoReleasesDatasource.id);\n\n constructor() {\n super(ForgejoReleasesDatasource.id);\n }\n}\n"],"mappings":";;;;;;;AASA,IAAa,4BAAb,MAAa,kCAAkC,wBAAwB;CACrE,OAAyB,KAAqB;CAE9C,OAAyB,sBAAsB,CAAC,0BAA0B;CAE1E,sBACE,0BAA0B;CAE5B,iBACE;CAEF,OAAgB,IAAI,YAAY,0BAA0B,EAAE;CAE5D,cAAc;EACZ,MAAM,0BAA0B,EAAE;CACpC;AACF"}
@@ -1,82 +1,19 @@
1
- import { regEx } from "../../../util/regex.js";
2
- import { ensureTrailingSlash } from "../../../util/url.js";
3
- import { withCache } from "../../../util/cache/package/with-cache.js";
4
- import { Datasource } from "../datasource.js";
5
1
  import { ForgejoHttp } from "../../../util/http/forgejo.js";
6
- import { Commits, Tag, Tags } from "./schema.js";
2
+ import { GiteaTagsDatasource } from "../gitea-tags/index.js";
7
3
  //#region lib/modules/datasource/forgejo-tags/index.ts
8
- var ForgejoTagsDatasource = class ForgejoTagsDatasource extends Datasource {
4
+ /**
5
+ * Forgejo is a fork of Gitea and serves the same API, so the lookup is
6
+ * inherited and only the identity of the datasource differs.
7
+ */
8
+ var ForgejoTagsDatasource = class ForgejoTagsDatasource extends GiteaTagsDatasource {
9
9
  static id = "forgejo-tags";
10
- http = new ForgejoHttp(ForgejoTagsDatasource.id);
11
10
  static defaultRegistryUrls = ["https://code.forgejo.org"];
12
- static cacheNamespace = `datasource-${ForgejoTagsDatasource.id}`;
13
- releaseTimestampSupport = true;
14
- releaseTimestampNote = "The release timestamp is determined from the `created` field in the results.";
15
- sourceUrlSupport = "package";
16
- sourceUrlNote = "The source URL is determined by using the `packageName` and `registryUrl`.";
11
+ defaultRegistryUrls = ForgejoTagsDatasource.defaultRegistryUrls;
12
+ cacheNamespace = "datasource-forgejo-tags";
13
+ http = new ForgejoHttp(ForgejoTagsDatasource.id);
17
14
  constructor() {
18
15
  super(ForgejoTagsDatasource.id);
19
16
  }
20
- static getRegistryURL(registryUrl) {
21
- return registryUrl ?? this.defaultRegistryUrls[0];
22
- }
23
- static getApiUrl(registryUrl) {
24
- const res = ForgejoTagsDatasource.getRegistryURL(registryUrl).replace(regEx(/\/api\/v1$/), "");
25
- return `${ensureTrailingSlash(res)}api/v1/`;
26
- }
27
- static getCacheKey(registryUrl, repo, type) {
28
- return `${ForgejoTagsDatasource.getRegistryURL(registryUrl)}:${repo}:${type}`;
29
- }
30
- static getSourceUrl(packageName, registryUrl) {
31
- const url = ForgejoTagsDatasource.getRegistryURL(registryUrl);
32
- return `${ensureTrailingSlash(url)}${packageName}`;
33
- }
34
- async _getReleases({ registryUrl, packageName: repo }) {
35
- const url = `${ForgejoTagsDatasource.getApiUrl(registryUrl)}repos/${repo}/tags`;
36
- const tags = (await this.http.getJson(url, { paginate: true }, Tags)).body;
37
- return {
38
- sourceUrl: ForgejoTagsDatasource.getSourceUrl(repo, registryUrl),
39
- registryUrl: ForgejoTagsDatasource.getRegistryURL(registryUrl),
40
- releases: tags.map(({ name, commit }) => ({
41
- version: name,
42
- gitRef: name,
43
- newDigest: commit.sha,
44
- releaseTimestamp: commit.created
45
- }))
46
- };
47
- }
48
- getReleases(config) {
49
- return withCache({
50
- namespace: ForgejoTagsDatasource.cacheNamespace,
51
- key: ForgejoTagsDatasource.getCacheKey(config.registryUrl, config.packageName, "tags"),
52
- fallback: true
53
- }, () => this._getReleases(config));
54
- }
55
- async _getTagCommit(registryUrl, repo, tag) {
56
- const url = `${ForgejoTagsDatasource.getApiUrl(registryUrl)}repos/${repo}/tags/${tag}`;
57
- const { body } = await this.http.getJson(url, Tag);
58
- return body.commit.sha;
59
- }
60
- getTagCommit(registryUrl, repo, tag) {
61
- return withCache({
62
- namespace: ForgejoTagsDatasource.cacheNamespace,
63
- key: ForgejoTagsDatasource.getCacheKey(registryUrl, repo, `tag-${tag}`)
64
- }, () => this._getTagCommit(registryUrl, repo, tag));
65
- }
66
- async _getDigest({ packageName: repo, registryUrl }, newValue) {
67
- if (newValue?.length) return this.getTagCommit(registryUrl, repo, newValue);
68
- const url = `${ForgejoTagsDatasource.getApiUrl(registryUrl)}repos/${repo}/commits?stat=false&verification=false&files=false&page=1&limit=1`;
69
- const { body } = await this.http.getJson(url, Commits);
70
- if (body.length === 0) return null;
71
- return body[0].sha;
72
- }
73
- getDigest(config, newValue) {
74
- return withCache({
75
- namespace: ForgejoTagsDatasource.cacheNamespace,
76
- key: ForgejoTagsDatasource.getCacheKey(config.registryUrl, config.packageName, "digest"),
77
- fallback: true
78
- }, () => this._getDigest(config, newValue));
79
- }
80
17
  };
81
18
  //#endregion
82
19
  export { ForgejoTagsDatasource };