renovate 44.105.4 → 44.106.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 (72) hide show
  1. package/dist/config/presets/gitea/common.js +4 -2
  2. package/dist/config/presets/gitea/common.js.map +1 -1
  3. package/dist/config/presets/github/index.js +7 -9
  4. package/dist/config/presets/github/index.js.map +1 -1
  5. package/dist/config/presets/gitlab/index.js +4 -15
  6. package/dist/config/presets/gitlab/index.js.map +1 -1
  7. package/dist/constants/platforms.js +94 -42
  8. package/dist/constants/platforms.js.map +1 -1
  9. package/dist/modules/manager/github-actions/extract.js +5 -8
  10. package/dist/modules/manager/github-actions/extract.js.map +1 -1
  11. package/dist/modules/manager/pre-commit/extract.js +13 -13
  12. package/dist/modules/manager/pre-commit/extract.js.map +1 -1
  13. package/dist/modules/platform/bitbucket/files.js +19 -0
  14. package/dist/modules/platform/bitbucket/files.js.map +1 -0
  15. package/dist/modules/platform/bitbucket/index.js +2 -3
  16. package/dist/modules/platform/bitbucket/index.js.map +1 -1
  17. package/dist/modules/platform/gitea/files.js +40 -0
  18. package/dist/modules/platform/gitea/files.js.map +1 -0
  19. package/dist/modules/platform/gitea/gitea-helper.js +2 -7
  20. package/dist/modules/platform/gitea/gitea-helper.js.map +1 -1
  21. package/dist/modules/platform/gitea/index.js +5 -4
  22. package/dist/modules/platform/gitea/index.js.map +1 -1
  23. package/dist/modules/platform/gitea/labels.js.map +1 -1
  24. package/dist/modules/platform/gitea/pr-cache.js.map +1 -1
  25. package/dist/modules/platform/github/files.js +21 -0
  26. package/dist/modules/platform/github/files.js.map +1 -0
  27. package/dist/modules/platform/github/index.js +3 -5
  28. package/dist/modules/platform/github/index.js.map +1 -1
  29. package/dist/modules/platform/gitlab/files.js +23 -0
  30. package/dist/modules/platform/gitlab/files.js.map +1 -0
  31. package/dist/modules/platform/gitlab/index.js +3 -4
  32. package/dist/modules/platform/gitlab/index.js.map +1 -1
  33. package/dist/util/common.js +11 -16
  34. package/dist/util/common.js.map +1 -1
  35. package/dist/util/http/auth.js +5 -5
  36. package/dist/util/http/auth.js.map +1 -1
  37. package/dist/util/http/host-rules.js +7 -7
  38. package/dist/util/http/host-rules.js.map +1 -1
  39. package/dist/workers/repository/update/pr/body/index.js +3 -6
  40. package/dist/workers/repository/update/pr/body/index.js.map +1 -1
  41. package/dist/workers/repository/update/pr/changelog/bitbucket/source.js +28 -5
  42. package/dist/workers/repository/update/pr/changelog/bitbucket/source.js.map +1 -1
  43. package/dist/workers/repository/update/pr/changelog/bitbucket-server/source.js +30 -6
  44. package/dist/workers/repository/update/pr/changelog/bitbucket-server/source.js.map +1 -1
  45. package/dist/workers/repository/update/pr/changelog/forgejo/source.js +7 -4
  46. package/dist/workers/repository/update/pr/changelog/forgejo/source.js.map +1 -1
  47. package/dist/workers/repository/update/pr/changelog/gitea/source.js +51 -7
  48. package/dist/workers/repository/update/pr/changelog/gitea/source.js.map +1 -1
  49. package/dist/workers/repository/update/pr/changelog/github/source.js +53 -8
  50. package/dist/workers/repository/update/pr/changelog/github/source.js.map +1 -1
  51. package/dist/workers/repository/update/pr/changelog/gitlab/source.js +37 -5
  52. package/dist/workers/repository/update/pr/changelog/gitlab/source.js.map +1 -1
  53. package/dist/workers/repository/update/pr/changelog/release-notes.js +13 -51
  54. package/dist/workers/repository/update/pr/changelog/release-notes.js.map +1 -1
  55. package/dist/workers/repository/update/pr/changelog/source.js +17 -5
  56. package/dist/workers/repository/update/pr/changelog/source.js.map +1 -1
  57. package/package.json +1 -1
  58. package/renovate-schema.json +2 -2
  59. package/dist/workers/repository/update/pr/changelog/bitbucket/index.js +0 -36
  60. package/dist/workers/repository/update/pr/changelog/bitbucket/index.js.map +0 -1
  61. package/dist/workers/repository/update/pr/changelog/bitbucket-server/index.js +0 -33
  62. package/dist/workers/repository/update/pr/changelog/bitbucket-server/index.js.map +0 -1
  63. package/dist/workers/repository/update/pr/changelog/forgejo/index.js +0 -9
  64. package/dist/workers/repository/update/pr/changelog/forgejo/index.js.map +0 -1
  65. package/dist/workers/repository/update/pr/changelog/gitea/common.js +0 -56
  66. package/dist/workers/repository/update/pr/changelog/gitea/common.js.map +0 -1
  67. package/dist/workers/repository/update/pr/changelog/gitea/index.js +0 -9
  68. package/dist/workers/repository/update/pr/changelog/gitea/index.js.map +0 -1
  69. package/dist/workers/repository/update/pr/changelog/github/index.js +0 -56
  70. package/dist/workers/repository/update/pr/changelog/github/index.js.map +0 -1
  71. package/dist/workers/repository/update/pr/changelog/gitlab/index.js +0 -40
  72. package/dist/workers/repository/update/pr/changelog/gitlab/index.js.map +0 -1
@@ -1,7 +1,9 @@
1
1
  import { logger } from "../../../logger/index.js";
2
+ import { joinUrlParts } from "../../../util/url.js";
2
3
  import { ExternalHostError } from "../../../types/errors/external-host-error.js";
3
4
  import { PRESET_DEP_NOT_FOUND, PRESET_INVALID, fetchPreset, parsePreset } from "../util.js";
4
- import { getRepoContents } from "../../../modules/platform/gitea/gitea-helper.js";
5
+ import { getRepoFile } from "../../../modules/platform/gitea/files.js";
6
+ import { API_PATH } from "../../../modules/platform/gitea/utils.js";
5
7
  //#region lib/config/presets/gitea/common.ts
6
8
  /**
7
9
  * Builds the preset source of a platform which speaks the Gitea API.
@@ -13,7 +15,7 @@ function createPresetSource(http, defaultEndpoint) {
13
15
  async function fetchJSONFile(repo, fileName, endpoint, tag) {
14
16
  let res;
15
17
  try {
16
- res = await getRepoContents(http, repo, fileName, tag, { baseUrl: endpoint });
18
+ res = await getRepoFile(http, repo, fileName, tag, { baseUrl: joinUrlParts(endpoint, API_PATH) });
17
19
  } catch (err) {
18
20
  if (err instanceof ExternalHostError) throw err;
19
21
  logger.debug(`Preset file ${fileName} not found in ${repo}: ${err.message}`);
@@ -1 +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
+ {"version":3,"file":"common.js","names":[],"sources":["../../../../lib/config/presets/gitea/common.ts"],"sourcesContent":["import { logger } from '../../../logger/index.ts';\nimport { getRepoFile } from '../../../modules/platform/gitea/files.ts';\nimport type { RepoContents } from '../../../modules/platform/gitea/schema.ts';\nimport { API_PATH } from '../../../modules/platform/gitea/utils.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 { joinUrlParts } from '../../../util/url.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 getRepoFile(http, repo, fileName, tag, {\n baseUrl: joinUrlParts(endpoint, API_PATH),\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":";;;;;;;;;;;;;AAuCA,SAAgB,mBACd,MACA,iBACmB;CACnB,eAAe,cACb,MACA,UACA,UACA,KAC0B;EAC1B,IAAI;EACJ,IAAI;GACF,MAAM,MAAM,YAAY,MAAM,MAAM,UAAU,KAAK,EACjD,SAAS,aAAa,UAAU,QAAQ,EAC1C,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,27 +1,25 @@
1
- import { fromBase64 } from "../../../util/string.js";
2
1
  import { logger } from "../../../logger/index.js";
3
2
  import { ExternalHostError } from "../../../types/errors/external-host-error.js";
4
3
  import { PRESET_DEP_NOT_FOUND, fetchPreset, parsePreset } from "../util.js";
5
4
  import { GithubHttp } from "../../../util/http/github.js";
6
5
  import { repoCacheProvider } from "../../../util/http/cache/repository-http-cache-provider.js";
7
- import { isNonEmptyString } from "@sindresorhus/is";
6
+ import { getRepoFile } from "../../../modules/platform/github/files.js";
8
7
  //#region lib/config/presets/github/index.ts
9
8
  const Endpoint = "https://api.github.com/";
10
9
  const http = new GithubHttp();
11
10
  async function fetchJSONFile(repo, fileName, endpoint, tag) {
12
- let ref = "";
13
- if (isNonEmptyString(tag)) ref = `?ref=${tag}`;
14
- const url = `${endpoint}repos/${repo}/contents/${fileName}${ref}`;
15
- logger.trace({ url }, `Preset URL`);
16
- let res;
11
+ let content;
17
12
  try {
18
- res = await http.getJsonUnchecked(url, { cacheProvider: repoCacheProvider });
13
+ content = await getRepoFile(http, repo, fileName, tag, {
14
+ baseUrl: endpoint,
15
+ cacheProvider: repoCacheProvider
16
+ });
19
17
  } catch (err) {
20
18
  if (err instanceof ExternalHostError) throw err;
21
19
  logger.debug(`Preset file ${fileName} not found in ${repo}: ${err.message}`);
22
20
  throw new Error(PRESET_DEP_NOT_FOUND);
23
21
  }
24
- return parsePreset(fromBase64(res.body.content), fileName);
22
+ return parsePreset(content, fileName);
25
23
  }
26
24
  function getPresetFromEndpoint(repo, filePreset, presetPath, endpoint = Endpoint, tag) {
27
25
  return fetchPreset({
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../../lib/config/presets/github/index.ts"],"sourcesContent":["import { isNonEmptyString } from '@sindresorhus/is';\nimport { logger } from '../../../logger/index.ts';\nimport { ExternalHostError } from '../../../types/errors/external-host-error.ts';\nimport type { Nullish } from '../../../types/index.ts';\nimport { repoCacheProvider } from '../../../util/http/cache/repository-http-cache-provider.ts';\nimport { GithubHttp } from '../../../util/http/github.ts';\nimport { fromBase64 } from '../../../util/string.ts';\nimport type { Preset, PresetConfig } from '../types.ts';\nimport { PRESET_DEP_NOT_FOUND, fetchPreset, parsePreset } from '../util.ts';\n\nexport const Endpoint = 'https://api.github.com/';\n\nconst http = new GithubHttp();\n\nexport async function fetchJSONFile(\n repo: string,\n fileName: string,\n endpoint: string,\n tag?: string,\n): Promise<Nullish<Preset>> {\n let ref = '';\n if (isNonEmptyString(tag)) {\n ref = `?ref=${tag}`;\n }\n const url = `${endpoint}repos/${repo}/contents/${fileName}${ref}`;\n logger.trace({ url }, `Preset URL`);\n let res: { body: { content: string } };\n try {\n res = await http.getJsonUnchecked(url, {\n cacheProvider: repoCacheProvider,\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 return parsePreset(fromBase64(res.body.content), 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":";;;;;;;;AAUA,MAAa,WAAW;AAExB,MAAM,OAAO,IAAI,WAAW;AAE5B,eAAsB,cACpB,MACA,UACA,UACA,KAC0B;CAC1B,IAAI,MAAM;CACV,IAAI,iBAAiB,GAAG,GACtB,MAAM,QAAQ;CAEhB,MAAM,MAAM,GAAG,SAAS,QAAQ,KAAK,YAAY,WAAW;CAC5D,OAAO,MAAM,EAAE,IAAI,GAAG,YAAY;CAClC,IAAI;CACJ,IAAI;EACF,MAAM,MAAM,KAAK,iBAAiB,KAAK,EACrC,eAAe,kBACjB,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,OAAO,YAAY,WAAW,IAAI,KAAK,OAAO,GAAG,QAAQ;AAC3D;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/github/index.ts"],"sourcesContent":["import { logger } from '../../../logger/index.ts';\nimport { getRepoFile } from '../../../modules/platform/github/files.ts';\nimport { ExternalHostError } from '../../../types/errors/external-host-error.ts';\nimport type { Nullish } from '../../../types/index.ts';\nimport { repoCacheProvider } from '../../../util/http/cache/repository-http-cache-provider.ts';\nimport { GithubHttp } from '../../../util/http/github.ts';\nimport type { Preset, PresetConfig } from '../types.ts';\nimport { PRESET_DEP_NOT_FOUND, fetchPreset, parsePreset } from '../util.ts';\n\nexport const Endpoint = 'https://api.github.com/';\n\nconst http = new GithubHttp();\n\nexport async function fetchJSONFile(\n repo: string,\n fileName: string,\n endpoint: string,\n tag?: string,\n): Promise<Nullish<Preset>> {\n let content: string;\n try {\n content = await getRepoFile(http, repo, fileName, tag, {\n baseUrl: endpoint,\n cacheProvider: repoCacheProvider,\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 return parsePreset(content, 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":";;;;;;;AASA,MAAa,WAAW;AAExB,MAAM,OAAO,IAAI,WAAW;AAE5B,eAAsB,cACpB,MACA,UACA,UACA,KAC0B;CAC1B,IAAI;CACJ,IAAI;EACF,UAAU,MAAM,YAAY,MAAM,MAAM,UAAU,KAAK;GACrD,SAAS;GACT,eAAe;EACjB,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,OAAO,YAAY,SAAS,QAAQ;AACtC;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"}
@@ -2,31 +2,20 @@ import { logger } from "../../../logger/index.js";
2
2
  import { ExternalHostError } from "../../../types/errors/external-host-error.js";
3
3
  import { PRESET_DEP_NOT_FOUND, fetchPreset, parsePreset } from "../util.js";
4
4
  import { GitlabHttp } from "../../../util/http/gitlab.js";
5
- import { isNonEmptyString } from "@sindresorhus/is";
5
+ import { getRepoFile } from "../../../modules/platform/gitlab/files.js";
6
6
  //#region lib/config/presets/gitlab/index.ts
7
7
  const gitlabApi = new GitlabHttp();
8
8
  const Endpoint = "https://gitlab.com/api/v4/";
9
- async function getDefaultBranchName(urlEncodedPkgName, endpoint) {
10
- return (await gitlabApi.getJsonUnchecked(`${endpoint}projects/${urlEncodedPkgName}`)).body.default_branch ?? "master";
11
- }
12
9
  async function fetchJSONFile(repo, fileName, endpoint, tag) {
13
- let url = endpoint;
14
- let ref = "";
15
- let res;
10
+ let content;
16
11
  try {
17
- const urlEncodedRepo = encodeURIComponent(repo);
18
- const urlEncodedPkgName = encodeURIComponent(fileName);
19
- if (isNonEmptyString(tag)) ref = `?ref=${tag}`;
20
- else ref = `?ref=${await getDefaultBranchName(urlEncodedRepo, endpoint)}`;
21
- url += `projects/${urlEncodedRepo}/repository/files/${urlEncodedPkgName}/raw${ref}`;
22
- logger.trace({ url }, `Preset URL`);
23
- res = await gitlabApi.getText(url);
12
+ content = await getRepoFile(gitlabApi, encodeURIComponent(repo), fileName, tag, { baseUrl: endpoint });
24
13
  } catch (err) {
25
14
  if (err instanceof ExternalHostError) throw err;
26
15
  logger.debug(`Preset file ${fileName} not found in ${repo}: ${err.message}`);
27
16
  throw new Error(PRESET_DEP_NOT_FOUND);
28
17
  }
29
- return parsePreset(res.body, fileName);
18
+ return parsePreset(content, fileName);
30
19
  }
31
20
  function getPresetFromEndpoint(repo, presetName, presetPath, endpoint = Endpoint, tag) {
32
21
  return fetchPreset({
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../../lib/config/presets/gitlab/index.ts"],"sourcesContent":["import { isNonEmptyString } from '@sindresorhus/is';\nimport { logger } from '../../../logger/index.ts';\nimport { ExternalHostError } from '../../../types/errors/external-host-error.ts';\nimport type { Nullish } from '../../../types/index.ts';\nimport type { GitlabProject } from '../../../types/platform/gitlab/index.ts';\nimport { GitlabHttp } from '../../../util/http/gitlab.ts';\nimport type { HttpResponse } from '../../../util/http/types.ts';\nimport type { Preset, PresetConfig } from '../types.ts';\nimport { PRESET_DEP_NOT_FOUND, fetchPreset, parsePreset } from '../util.ts';\n\nconst gitlabApi = new GitlabHttp();\nexport const Endpoint = 'https://gitlab.com/api/v4/';\n\nasync function getDefaultBranchName(\n urlEncodedPkgName: string,\n endpoint: string,\n): Promise<string> {\n const res = await gitlabApi.getJsonUnchecked<GitlabProject>(\n `${endpoint}projects/${urlEncodedPkgName}`,\n );\n return res.body.default_branch ?? 'master'; // should never happen, but we keep this to ensure the current behavior\n}\n\nexport async function fetchJSONFile(\n repo: string,\n fileName: string,\n endpoint: string,\n tag?: string,\n): Promise<Nullish<Preset>> {\n let url = endpoint;\n let ref = '';\n let res: HttpResponse;\n try {\n const urlEncodedRepo = encodeURIComponent(repo);\n const urlEncodedPkgName = encodeURIComponent(fileName);\n if (isNonEmptyString(tag)) {\n ref = `?ref=${tag}`;\n } else {\n const defaultBranchName = await getDefaultBranchName(\n urlEncodedRepo,\n endpoint,\n );\n ref = `?ref=${defaultBranchName}`;\n }\n url += `projects/${urlEncodedRepo}/repository/files/${urlEncodedPkgName}/raw${ref}`;\n logger.trace({ url }, `Preset URL`);\n res = await gitlabApi.getText(url);\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 return parsePreset(res.body, fileName);\n}\n\nexport function getPresetFromEndpoint(\n repo: string,\n presetName: string,\n presetPath?: string,\n endpoint = Endpoint,\n tag?: string,\n): Promise<Nullish<Preset>> {\n return fetchPreset({\n repo,\n filePreset: presetName,\n presetPath,\n endpoint,\n tag,\n fetch: fetchJSONFile,\n });\n}\n\nexport function getPreset({\n repo,\n presetPath,\n presetName = 'default',\n tag,\n}: PresetConfig): Promise<Nullish<Preset>> {\n return getPresetFromEndpoint(repo, presetName, presetPath, Endpoint, tag);\n}\n"],"mappings":";;;;;;AAUA,MAAM,YAAY,IAAI,WAAW;AACjC,MAAa,WAAW;AAExB,eAAe,qBACb,mBACA,UACiB;CAIjB,QAAO,MAHW,UAAU,iBAC1B,GAAG,SAAS,WAAW,mBACzB,EAAA,CACW,KAAK,kBAAkB;AACpC;AAEA,eAAsB,cACpB,MACA,UACA,UACA,KAC0B;CAC1B,IAAI,MAAM;CACV,IAAI,MAAM;CACV,IAAI;CACJ,IAAI;EACF,MAAM,iBAAiB,mBAAmB,IAAI;EAC9C,MAAM,oBAAoB,mBAAmB,QAAQ;EACrD,IAAI,iBAAiB,GAAG,GACtB,MAAM,QAAQ;OAMd,MAAM,QAAQ,MAJkB,qBAC9B,gBACA,QACF;EAGF,OAAO,YAAY,eAAe,oBAAoB,kBAAkB,MAAM;EAC9E,OAAO,MAAM,EAAE,IAAI,GAAG,YAAY;EAClC,MAAM,MAAM,UAAU,QAAQ,GAAG;CACnC,SAAS,KAAK;EACZ,IAAI,eAAe,mBACjB,MAAM;EAER,OAAO,MACL,eAAe,SAAS,gBAAgB,KAAK,IAAI,IAAI,SACvD;EACA,MAAM,IAAI,MAAM,oBAAoB;CACtC;CAEA,OAAO,YAAY,IAAI,MAAM,QAAQ;AACvC;AAEA,SAAgB,sBACd,MACA,YACA,YACA,WAAW,UACX,KAC0B;CAC1B,OAAO,YAAY;EACjB;EACA,YAAY;EACZ;EACA;EACA;EACA,OAAO;CACT,CAAC;AACH;AAEA,SAAgB,UAAU,EACxB,MACA,YACA,aAAa,WACb,OACyC;CACzC,OAAO,sBAAsB,MAAM,YAAY,YAAY,UAAU,GAAG;AAC1E"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../../lib/config/presets/gitlab/index.ts"],"sourcesContent":["import { logger } from '../../../logger/index.ts';\nimport { getRepoFile } from '../../../modules/platform/gitlab/files.ts';\nimport { ExternalHostError } from '../../../types/errors/external-host-error.ts';\nimport type { Nullish } from '../../../types/index.ts';\nimport { GitlabHttp } from '../../../util/http/gitlab.ts';\nimport type { Preset, PresetConfig } from '../types.ts';\nimport { PRESET_DEP_NOT_FOUND, fetchPreset, parsePreset } from '../util.ts';\n\nconst gitlabApi = new GitlabHttp();\nexport const Endpoint = 'https://gitlab.com/api/v4/';\n\nexport async function fetchJSONFile(\n repo: string,\n fileName: string,\n endpoint: string,\n tag?: string,\n): Promise<Nullish<Preset>> {\n let content: string;\n try {\n content = await getRepoFile(\n gitlabApi,\n encodeURIComponent(repo),\n fileName,\n 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 return parsePreset(content, fileName);\n}\n\nexport function getPresetFromEndpoint(\n repo: string,\n presetName: string,\n presetPath?: string,\n endpoint = Endpoint,\n tag?: string,\n): Promise<Nullish<Preset>> {\n return fetchPreset({\n repo,\n filePreset: presetName,\n presetPath,\n endpoint,\n tag,\n fetch: fetchJSONFile,\n });\n}\n\nexport function getPreset({\n repo,\n presetPath,\n presetName = 'default',\n tag,\n}: PresetConfig): Promise<Nullish<Preset>> {\n return getPresetFromEndpoint(repo, presetName, presetPath, Endpoint, tag);\n}\n"],"mappings":";;;;;;AAQA,MAAM,YAAY,IAAI,WAAW;AACjC,MAAa,WAAW;AAExB,eAAsB,cACpB,MACA,UACA,UACA,KAC0B;CAC1B,IAAI;CACJ,IAAI;EACF,UAAU,MAAM,YACd,WACA,mBAAmB,IAAI,GACvB,UACA,KACA,EAAE,SAAS,SAAS,CACtB;CACF,SAAS,KAAK;EACZ,IAAI,eAAe,mBACjB,MAAM;EAER,OAAO,MACL,eAAe,SAAS,gBAAgB,KAAK,IAAI,IAAI,SACvD;EACA,MAAM,IAAI,MAAM,oBAAoB;CACtC;CAEA,OAAO,YAAY,SAAS,QAAQ;AACtC;AAEA,SAAgB,sBACd,MACA,YACA,YACA,WAAW,UACX,KAC0B;CAC1B,OAAO,YAAY;EACjB;EACA,YAAY;EACZ;EACA;EACA;EACA,OAAO;CACT,CAAC;AACH;AAEA,SAAgB,UAAU,EACxB,MACA,YACA,aAAa,WACb,OACyC;CACzC,OAAO,sBAAsB,MAAM,YAAY,YAAY,UAAU,GAAG;AAC1E"}
@@ -12,48 +12,100 @@ const PLATFORM_HOST_TYPES = [
12
12
  "local",
13
13
  "scm-manager"
14
14
  ];
15
- const AZURE_API_USING_HOST_TYPES = ["azure", "azure-tags"];
16
- const GITEA_API_USING_HOST_TYPES = [
17
- "gitea",
18
- "gitea-changelog",
19
- "gitea-releases",
20
- "gitea-tags"
21
- ];
22
- const FORGEJO_API_USING_HOST_TYPES = [
23
- "forgejo",
24
- "forgejo-changelog",
25
- "forgejo-releases",
26
- "forgejo-tags"
27
- ];
28
- const GITHUB_API_USING_HOST_TYPES = [
29
- "github",
30
- "github-releases",
31
- "github-release-attachments",
32
- "github-tags",
33
- "pod",
34
- "hermit",
35
- "github-changelog",
36
- "conan"
37
- ];
38
- const GITLAB_API_USING_HOST_TYPES = [
39
- "gitlab",
40
- "gitlab-releases",
41
- "gitlab-tags",
42
- "gitlab-packages",
43
- "gitlab-changelog",
44
- "pypi"
45
- ];
46
- const BITBUCKET_API_USING_HOST_TYPES = [
47
- "bitbucket",
48
- "bitbucket-changelog",
49
- "bitbucket-tags"
50
- ];
51
- const BITBUCKET_SERVER_API_USING_HOST_TYPES = [
52
- "bitbucket-server",
53
- "bitbucket-server-changelog",
54
- "bitbucket-server-tags"
55
- ];
15
+ /**
16
+ * Capabilities of each platform family, so that a consumer which detected a
17
+ * family with `detectPlatform` can look the rest up instead of carrying its own
18
+ * mapping.
19
+ *
20
+ * Keys are ordered the way `detectPlatform` used to test them; both the
21
+ * `apiUsingHostTypes` and the `knownHosts` sets are pairwise disjoint
22
+ */
23
+ const PLATFORM_FAMILIES = {
24
+ azure: {
25
+ apiUsingHostTypes: ["azure", "azure-tags"],
26
+ knownHosts: ["dev.azure.com"],
27
+ tagsDatasource: "azure-tags",
28
+ apiBaseUrl: null,
29
+ webDirPath: "tree/HEAD"
30
+ },
31
+ "bitbucket-server": {
32
+ apiUsingHostTypes: [
33
+ "bitbucket-server",
34
+ "bitbucket-server-changelog",
35
+ "bitbucket-server-tags"
36
+ ],
37
+ knownHosts: [],
38
+ tagsDatasource: "bitbucket-server-tags",
39
+ apiBaseUrl: (baseUrl) => `${baseUrl}rest/api/1.0/`,
40
+ webDirPath: "browse"
41
+ },
42
+ bitbucket: {
43
+ apiUsingHostTypes: [
44
+ "bitbucket",
45
+ "bitbucket-changelog",
46
+ "bitbucket-tags"
47
+ ],
48
+ knownHosts: ["bitbucket.org", "bitbucket.com"],
49
+ tagsDatasource: "bitbucket-tags",
50
+ apiBaseUrl: (_baseUrl) => "https://api.bitbucket.org/",
51
+ webDirPath: "src/HEAD"
52
+ },
53
+ forgejo: {
54
+ apiUsingHostTypes: [
55
+ "forgejo",
56
+ "forgejo-changelog",
57
+ "forgejo-releases",
58
+ "forgejo-tags"
59
+ ],
60
+ knownHosts: ["codeberg.org", "codefloe.com"],
61
+ tagsDatasource: "forgejo-tags",
62
+ apiBaseUrl: (baseUrl) => `${baseUrl}api/v1/`,
63
+ webDirPath: "tree/HEAD"
64
+ },
65
+ gitea: {
66
+ apiUsingHostTypes: [
67
+ "gitea",
68
+ "gitea-changelog",
69
+ "gitea-releases",
70
+ "gitea-tags"
71
+ ],
72
+ knownHosts: ["gitea.com"],
73
+ tagsDatasource: "gitea-tags",
74
+ apiBaseUrl: (baseUrl) => `${baseUrl}api/v1/`,
75
+ webDirPath: "tree/HEAD"
76
+ },
77
+ github: {
78
+ apiUsingHostTypes: [
79
+ "github",
80
+ "github-releases",
81
+ "github-release-attachments",
82
+ "github-tags",
83
+ "pod",
84
+ "hermit",
85
+ "github-changelog",
86
+ "conan"
87
+ ],
88
+ knownHosts: ["github.com"],
89
+ tagsDatasource: "github-tags",
90
+ apiBaseUrl: (baseUrl) => baseUrl.startsWith("https://github.com/") ? "https://api.github.com/" : `${baseUrl}api/v3/`,
91
+ webDirPath: "tree/HEAD"
92
+ },
93
+ gitlab: {
94
+ apiUsingHostTypes: [
95
+ "gitlab",
96
+ "gitlab-releases",
97
+ "gitlab-tags",
98
+ "gitlab-packages",
99
+ "gitlab-changelog",
100
+ "pypi"
101
+ ],
102
+ knownHosts: ["gitlab.com"],
103
+ tagsDatasource: "gitlab-tags",
104
+ apiBaseUrl: (baseUrl) => `${baseUrl}api/v4/`,
105
+ webDirPath: "tree/HEAD"
106
+ }
107
+ };
56
108
  //#endregion
57
- export { AZURE_API_USING_HOST_TYPES, BITBUCKET_API_USING_HOST_TYPES, BITBUCKET_SERVER_API_USING_HOST_TYPES, FORGEJO_API_USING_HOST_TYPES, GITEA_API_USING_HOST_TYPES, GITHUB_API_USING_HOST_TYPES, GITLAB_API_USING_HOST_TYPES, PLATFORM_HOST_TYPES };
109
+ export { PLATFORM_FAMILIES, PLATFORM_HOST_TYPES };
58
110
 
59
111
  //# sourceMappingURL=platforms.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"platforms.js","names":[],"sources":["../../lib/constants/platforms.ts"],"sourcesContent":["export const PLATFORM_HOST_TYPES = [\n 'azure',\n 'bitbucket',\n 'bitbucket-server',\n 'codecommit',\n 'forgejo',\n 'gerrit',\n 'gitea',\n 'github',\n 'gitlab',\n 'local',\n 'scm-manager',\n] as const;\n\nexport type PlatformId = (typeof PLATFORM_HOST_TYPES)[number];\n\nexport const AZURE_API_USING_HOST_TYPES = ['azure', 'azure-tags'];\n\nexport const GITEA_API_USING_HOST_TYPES = [\n 'gitea',\n 'gitea-changelog',\n 'gitea-releases',\n 'gitea-tags',\n];\n\nexport const FORGEJO_API_USING_HOST_TYPES = [\n 'forgejo',\n 'forgejo-changelog',\n 'forgejo-releases',\n 'forgejo-tags',\n];\n\nexport const GITHUB_API_USING_HOST_TYPES = [\n 'github',\n 'github-releases',\n 'github-release-attachments',\n 'github-tags',\n 'pod',\n 'hermit',\n 'github-changelog',\n 'conan',\n // DEPRECATED: do not add additional datasource-specific entries here, if they use `api.github.com` to look up new versions\n];\n\nexport const GITLAB_API_USING_HOST_TYPES = [\n 'gitlab',\n 'gitlab-releases',\n 'gitlab-tags',\n 'gitlab-packages',\n 'gitlab-changelog',\n 'pypi',\n];\n\nexport const BITBUCKET_API_USING_HOST_TYPES = [\n 'bitbucket',\n 'bitbucket-changelog',\n 'bitbucket-tags',\n];\n\nexport const BITBUCKET_SERVER_API_USING_HOST_TYPES = [\n 'bitbucket-server',\n 'bitbucket-server-changelog',\n 'bitbucket-server-tags',\n];\n"],"mappings":";AAAA,MAAa,sBAAsB;CACjC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAIA,MAAa,6BAA6B,CAAC,SAAS,YAAY;AAEhE,MAAa,6BAA6B;CACxC;CACA;CACA;CACA;AACF;AAEA,MAAa,+BAA+B;CAC1C;CACA;CACA;CACA;AACF;AAEA,MAAa,8BAA8B;CACzC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AAEF;AAEA,MAAa,8BAA8B;CACzC;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,MAAa,iCAAiC;CAC5C;CACA;CACA;AACF;AAEA,MAAa,wCAAwC;CACnD;CACA;CACA;AACF"}
1
+ {"version":3,"file":"platforms.js","names":[],"sources":["../../lib/constants/platforms.ts"],"sourcesContent":["export const PLATFORM_HOST_TYPES = [\n 'azure',\n 'bitbucket',\n 'bitbucket-server',\n 'codecommit',\n 'forgejo',\n 'gerrit',\n 'gitea',\n 'github',\n 'gitlab',\n 'local',\n 'scm-manager',\n] as const;\n\nexport type PlatformId = (typeof PLATFORM_HOST_TYPES)[number];\n\n/**\n * The platform families Renovate can recognize from a URL.\n *\n * A family groups every flavour of one platform - the vendor's public instance\n * plus any self-hosted installation - because they share an API, a tags\n * datasource and a web UI layout.\n *\n * Every family is a `PlatformId`, but not every platform is a family:\n * `codecommit`, `gerrit`, `local` and `scm-manager` are never recognized from a\n * URL, so they have nothing to describe here.\n */\nconst PLATFORM_FAMILY_IDS = [\n 'azure',\n 'bitbucket',\n 'bitbucket-server',\n 'forgejo',\n 'gitea',\n 'github',\n 'gitlab',\n] as const satisfies readonly PlatformId[];\n\nexport type PlatformFamilyId = (typeof PLATFORM_FAMILY_IDS)[number];\n\nexport interface PlatformFamily {\n /**\n * `hostType` values which authenticate against this family's API, so a\n * hostRule for any of them identifies a host as belonging to the family.\n */\n apiUsingHostTypes: string[];\n\n /**\n * Hostnames `detectPlatform` recognizes as this family outright. Hosts which\n * are not listed are matched by its name heuristics or by a hostRule, so this\n * is a recognition list and says nothing about datasource defaults.\n */\n knownHosts: string[];\n\n /** Datasource which looks up git tags on this family. */\n tagsDatasource: `${PlatformFamilyId}-tags`;\n\n /**\n * Derives the API base URL from the web base URL, which always ends in a\n * slash. `null` for families Renovate has no API client for.\n */\n apiBaseUrl: ((baseUrl: string) => string) | null;\n\n /** Web UI path from a repository root to a directory at the default branch. */\n webDirPath: string;\n}\n\n/**\n * Capabilities of each platform family, so that a consumer which detected a\n * family with `detectPlatform` can look the rest up instead of carrying its own\n * mapping.\n *\n * Keys are ordered the way `detectPlatform` used to test them; both the\n * `apiUsingHostTypes` and the `knownHosts` sets are pairwise disjoint\n */\nexport const PLATFORM_FAMILIES = {\n azure: {\n apiUsingHostTypes: ['azure', 'azure-tags'],\n knownHosts: ['dev.azure.com'],\n tagsDatasource: 'azure-tags',\n apiBaseUrl: null,\n webDirPath: 'tree/HEAD',\n },\n 'bitbucket-server': {\n apiUsingHostTypes: [\n 'bitbucket-server',\n 'bitbucket-server-changelog',\n 'bitbucket-server-tags',\n ],\n // Bitbucket Data Center is self-hosted only.\n knownHosts: [] as string[],\n tagsDatasource: 'bitbucket-server-tags',\n apiBaseUrl: (baseUrl: string) => `${baseUrl}rest/api/1.0/`,\n webDirPath: 'browse',\n },\n bitbucket: {\n apiUsingHostTypes: ['bitbucket', 'bitbucket-changelog', 'bitbucket-tags'],\n knownHosts: ['bitbucket.org', 'bitbucket.com'],\n tagsDatasource: 'bitbucket-tags',\n // Bitbucket Cloud serves every instance from one API host.\n apiBaseUrl: (_baseUrl: string) => 'https://api.bitbucket.org/',\n webDirPath: 'src/HEAD',\n },\n forgejo: {\n apiUsingHostTypes: [\n 'forgejo',\n 'forgejo-changelog',\n 'forgejo-releases',\n 'forgejo-tags',\n ],\n knownHosts: ['codeberg.org', 'codefloe.com'],\n tagsDatasource: 'forgejo-tags',\n apiBaseUrl: (baseUrl: string) => `${baseUrl}api/v1/`,\n webDirPath: 'tree/HEAD',\n },\n gitea: {\n apiUsingHostTypes: [\n 'gitea',\n 'gitea-changelog',\n 'gitea-releases',\n 'gitea-tags',\n ],\n knownHosts: ['gitea.com'],\n tagsDatasource: 'gitea-tags',\n apiBaseUrl: (baseUrl: string) => `${baseUrl}api/v1/`,\n webDirPath: 'tree/HEAD',\n },\n github: {\n apiUsingHostTypes: [\n 'github',\n 'github-releases',\n 'github-release-attachments',\n 'github-tags',\n 'pod',\n 'hermit',\n 'github-changelog',\n 'conan',\n // DEPRECATED: do not add additional datasource-specific entries here, if they use `api.github.com` to look up new versions\n ],\n knownHosts: ['github.com'],\n tagsDatasource: 'github-tags',\n // Same rule as `getApiBaseUrl`, @see lib/util/github/url.ts. That helper is\n // not reused here because `lib/constants` imports nothing, and because it\n // also normalizes URLs which already carry an API path, which a base URL\n // built from a `protocol//host/` pair never does.\n apiBaseUrl: (baseUrl: string) =>\n baseUrl.startsWith('https://github.com/')\n ? 'https://api.github.com/'\n : `${baseUrl}api/v3/`,\n webDirPath: 'tree/HEAD',\n },\n gitlab: {\n apiUsingHostTypes: [\n 'gitlab',\n 'gitlab-releases',\n 'gitlab-tags',\n 'gitlab-packages',\n 'gitlab-changelog',\n 'pypi',\n ],\n knownHosts: ['gitlab.com'],\n tagsDatasource: 'gitlab-tags',\n apiBaseUrl: (baseUrl: string) => `${baseUrl}api/v4/`,\n webDirPath: 'tree/HEAD',\n },\n} satisfies Record<PlatformFamilyId, PlatformFamily>;\n"],"mappings":";AAAA,MAAa,sBAAsB;CACjC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;;;;;;;;AA8DA,MAAa,oBAAoB;CAC/B,OAAO;EACL,mBAAmB,CAAC,SAAS,YAAY;EACzC,YAAY,CAAC,eAAe;EAC5B,gBAAgB;EAChB,YAAY;EACZ,YAAY;CACd;CACA,oBAAoB;EAClB,mBAAmB;GACjB;GACA;GACA;EACF;EAEA,YAAY,CAAC;EACb,gBAAgB;EAChB,aAAa,YAAoB,GAAG,QAAQ;EAC5C,YAAY;CACd;CACA,WAAW;EACT,mBAAmB;GAAC;GAAa;GAAuB;EAAgB;EACxE,YAAY,CAAC,iBAAiB,eAAe;EAC7C,gBAAgB;EAEhB,aAAa,aAAqB;EAClC,YAAY;CACd;CACA,SAAS;EACP,mBAAmB;GACjB;GACA;GACA;GACA;EACF;EACA,YAAY,CAAC,gBAAgB,cAAc;EAC3C,gBAAgB;EAChB,aAAa,YAAoB,GAAG,QAAQ;EAC5C,YAAY;CACd;CACA,OAAO;EACL,mBAAmB;GACjB;GACA;GACA;GACA;EACF;EACA,YAAY,CAAC,WAAW;EACxB,gBAAgB;EAChB,aAAa,YAAoB,GAAG,QAAQ;EAC5C,YAAY;CACd;CACA,QAAQ;EACN,mBAAmB;GACjB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EAEF;EACA,YAAY,CAAC,YAAY;EACzB,gBAAgB;EAKhB,aAAa,YACX,QAAQ,WAAW,qBAAqB,IACpC,4BACA,GAAG,QAAQ;EACjB,YAAY;CACd;CACA,QAAQ;EACN,mBAAmB;GACjB;GACA;GACA;GACA;GACA;GACA;EACF;EACA,YAAY,CAAC,YAAY;EACzB,gBAAgB;EAChB,aAAa,YAAoB,GAAG,QAAQ;EAC5C,YAAY;CACd;AACF"}
@@ -2,14 +2,13 @@ import { get, set } from "../../../util/cache/memory/index.js";
2
2
  import { newlineRegex, regEx } from "../../../util/regex.js";
3
3
  import { GlobalConfig } from "../../../config/global.js";
4
4
  import { logger, withMeta } from "../../../logger/index.js";
5
+ import { PLATFORM_FAMILIES } from "../../../constants/platforms.js";
5
6
  import { parseUrl } from "../../../util/url.js";
6
7
  import { detectPlatform } from "../../../util/common.js";
7
8
  import { readLocalFile } from "../../../util/fs/index.js";
8
9
  import { api } from "../../versioning/docker/index.js";
9
10
  import { id } from "../../versioning/exact/index.js";
10
11
  import { id as id$1 } from "../../versioning/github-actions/index.js";
11
- import { GiteaTagsDatasource } from "../../datasource/gitea-tags/index.js";
12
- import { ForgejoTagsDatasource } from "../../datasource/forgejo-tags/index.js";
13
12
  import { GithubDigestDatasource } from "../../datasource/github-digest/index.js";
14
13
  import { GithubRunnersDatasource } from "../../datasource/github-runners/index.js";
15
14
  import { GithubTagsDatasource } from "../../datasource/github-tags/index.js";
@@ -104,14 +103,12 @@ function extractWithRegex(content, config) {
104
103
  return deps;
105
104
  }
106
105
  function detectDatasource(registryUrl) {
107
- switch (detectPlatform(registryUrl)) {
108
- case "forgejo": return {
109
- registryUrls: [registryUrl],
110
- datasource: ForgejoTagsDatasource.id
111
- };
106
+ const platform = detectPlatform(registryUrl);
107
+ switch (platform) {
108
+ case "forgejo":
112
109
  case "gitea": return {
113
110
  registryUrls: [registryUrl],
114
- datasource: GiteaTagsDatasource.id
111
+ datasource: PLATFORM_FAMILIES[platform].tagsDatasource
115
112
  };
116
113
  case "github": return { registryUrls: [registryUrl] };
117
114
  }
@@ -1 +1 @@
1
- {"version":3,"file":"extract.js","names":["githubActionsVersioning.id","exactVersioning.id","memCache.get"],"sources":["../../../../lib/modules/manager/github-actions/extract.ts"],"sourcesContent":["import is from '@sindresorhus/is';\nimport { GlobalConfig } from '../../../config/global.ts';\nimport { logger, withMeta } from '../../../logger/index.ts';\nimport * as memCache from '../../../util/cache/memory/index.ts';\nimport { detectPlatform } from '../../../util/common.ts';\nimport { readLocalFile } from '../../../util/fs/index.ts';\nimport { newlineRegex, regEx } from '../../../util/regex.ts';\nimport { parseUrl } from '../../../util/url.ts';\nimport { ForgejoTagsDatasource } from '../../datasource/forgejo-tags/index.ts';\nimport { GiteaTagsDatasource } from '../../datasource/gitea-tags/index.ts';\nimport { GithubDigestDatasource } from '../../datasource/github-digest/index.ts';\nimport { GithubRunnersDatasource } from '../../datasource/github-runners/index.ts';\nimport { GithubTagsDatasource } from '../../datasource/github-tags/index.ts';\nimport * as dockerVersioning from '../../versioning/docker/index.ts';\nimport * as exactVersioning from '../../versioning/exact/index.ts';\nimport * as githubActionsVersioning from '../../versioning/github-actions/index.ts';\nimport { getDep } from '../dockerfile/extract.ts';\nimport type {\n ExtractConfig,\n PackageDependency,\n PackageFileContent,\n} from '../types.ts';\nimport { actionsLockFile, isLockfileManaged } from './common.ts';\nimport { isSha, isShortSha, parseUsesLine, versionLikeRe } from './parse.ts';\nimport type { UsesStep } from './schema.ts';\nimport { ActionsLockfile, CommunityActions, Workflow } from './schema.ts';\nimport type {\n DockerReference,\n LockfileState,\n RepositoryReference,\n} from './types.ts';\n\n// detects if we run against a Github Enterprise Server and adds the URL to the beginning of the registryURLs for looking up Actions\n// This reflects the behavior of how GitHub looks up Actions\n// First on the Enterprise Server, then on GitHub.com\nfunction detectCustomGitHubRegistryUrlsForActions(): PackageDependency {\n const endpoint = GlobalConfig.get('endpoint');\n const registryUrls = ['https://github.com'];\n if (endpoint && GlobalConfig.get('platform') === 'github') {\n const parsedEndpoint = parseUrl(endpoint);\n if (!parsedEndpoint) {\n logger.warn({ endpoint }, 'Failed to parse endpoint url');\n return {};\n }\n\n if (\n parsedEndpoint.host !== 'github.com' &&\n parsedEndpoint.host !== 'api.github.com'\n ) {\n registryUrls.unshift(\n `${parsedEndpoint.protocol}//${parsedEndpoint.host}`,\n );\n return { registryUrls };\n }\n }\n return {};\n}\n\nfunction extractDockerAction(\n actionRef: DockerReference,\n config: ExtractConfig,\n): PackageDependency {\n const dep = getDep(actionRef.originalRef, true, config.registryAliases);\n dep.depType = 'docker';\n dep.replaceString = actionRef.originalRef;\n return dep;\n}\n\nconst reusableWorkflowPathRe = regEx(/^\\.github\\/workflows\\/[^/]+\\.ya?ml$/);\n\nfunction extractRepositoryAction(\n actionRef: RepositoryReference,\n parsed: ReturnType<typeof parseUsesLine> & object,\n customRegistryUrlsPackageDependency: PackageDependency,\n): PackageDependency {\n const {\n replaceString: valueString,\n quote,\n commentData,\n commentPrecedingWhitespace,\n } = parsed;\n const {\n owner,\n repo,\n path: subPath,\n ref,\n hostname,\n isExplicitHostname,\n } = actionRef;\n\n const registryUrl = isExplicitHostname ? `https://${hostname}/` : '';\n const packageName = `${owner}/${repo}`;\n const depName = `${registryUrl}${packageName}`;\n const pathSuffix = subPath ? `/${subPath}` : '';\n const commentWs = commentPrecedingWhitespace || ' ';\n const isReusableWorkflow = !!subPath && reusableWorkflowPathRe.test(subPath);\n\n const dep: PackageDependency = {\n depName,\n commitMessageTopic: '{{{depName}}} action',\n versioning: githubActionsVersioning.id,\n depType: isReusableWorkflow ? 'workflow' : 'action',\n replaceString: valueString,\n autoReplaceStringTemplate: `${quote}{{depName}}${pathSuffix}@{{#if newDigest}}{{newDigest}}${quote}{{#if newValue}}${commentWs}# {{newValue}}{{/if}}{{/if}}{{#unless newDigest}}{{newValue}}${quote}{{/unless}}`,\n ...(isExplicitHostname\n ? detectDatasource(registryUrl)\n : customRegistryUrlsPackageDependency),\n };\n\n if (packageName !== depName) {\n dep.packageName = packageName;\n }\n\n // Extend replaceString to include relevant comment portions:\n // - Pinned version or ref: include only up to the matched token (truncate trailing text)\n // - Ratchet exclude: include the full comment to preserve the marker\n const pinComment =\n commentData.pinnedVersion ??\n (isSha(ref) || isShortSha(ref) ? commentData.ref : undefined);\n if (\n pinComment &&\n !is.undefined(commentData.index) &&\n !is.undefined(commentData.matchedString)\n ) {\n const cleanComment = parsed.commentString.slice(1);\n const matchEndIndex = commentData.index + commentData.matchedString.length;\n const commentSuffix = cleanComment.slice(0, matchEndIndex);\n dep.replaceString = `${valueString}${commentPrecedingWhitespace}#${commentSuffix}`;\n } else if (commentData.ratchetExclude) {\n dep.replaceString =\n valueString + commentPrecedingWhitespace + parsed.commentString;\n }\n\n if (isSha(ref)) {\n dep.currentValue = commentData.pinnedVersion ?? commentData.ref;\n dep.currentDigest = ref;\n } else if (isShortSha(ref)) {\n dep.currentValue = commentData.pinnedVersion ?? commentData.ref;\n dep.currentDigestShort = ref;\n } else {\n dep.currentValue = ref;\n }\n\n if (!dep.currentValue) {\n dep.enabled = false;\n dep.skipReason = 'unversioned-reference';\n }\n\n const isVersionLike =\n dep.currentValue && versionLikeRe.test(dep.currentValue);\n if (!dep.datasource && dep.currentValue && !isVersionLike) {\n dep.datasource = GithubDigestDatasource.id;\n dep.versioning = exactVersioning.id;\n }\n\n dep.datasource ??= GithubTagsDatasource.id;\n\n return dep;\n}\n\nfunction extractWithRegex(\n content: string,\n config: ExtractConfig,\n): PackageDependency[] {\n const customRegistryUrlsPackageDependency =\n detectCustomGitHubRegistryUrlsForActions();\n logger.trace('github-actions.extractWithRegex()');\n const deps: PackageDependency[] = [];\n\n for (const line of content.split(newlineRegex)) {\n if (line.trim().startsWith('#')) {\n continue;\n }\n\n const parsed = parseUsesLine(line);\n if (!parsed?.actionRef) {\n continue;\n }\n\n const { actionRef } = parsed;\n\n if (actionRef.kind === 'docker') {\n deps.push(extractDockerAction(actionRef, config));\n continue;\n }\n\n // v8 ignore else -- the parsed ref is either a docker or a repository ref\n if (actionRef.kind === 'repository') {\n deps.push(\n extractRepositoryAction(\n actionRef,\n parsed,\n customRegistryUrlsPackageDependency,\n ),\n );\n }\n }\n\n return deps;\n}\n\nfunction detectDatasource(registryUrl: string): PackageDependency {\n const platform = detectPlatform(registryUrl);\n\n switch (platform) {\n case 'forgejo':\n return {\n registryUrls: [registryUrl],\n datasource: ForgejoTagsDatasource.id,\n };\n case 'gitea':\n return {\n registryUrls: [registryUrl],\n datasource: GiteaTagsDatasource.id,\n };\n case 'github':\n return { registryUrls: [registryUrl] };\n }\n\n return {\n skipReason: 'unsupported-url',\n };\n}\n\nconst runnerVersionRegex = regEx(\n /^\\s*(?<depName>[a-zA-Z]+)-(?<currentValue>[^\\s]+)/,\n);\n\nfunction extractRunner(runner: string): PackageDependency | null {\n const runnerVersionGroups = runnerVersionRegex.exec(runner)?.groups;\n if (!runnerVersionGroups) {\n return null;\n }\n\n const { depName, currentValue } = runnerVersionGroups;\n\n if (!GithubRunnersDatasource.isValidRunner(depName, currentValue)) {\n return null;\n }\n\n const dependency: PackageDependency = {\n depName,\n currentValue,\n replaceString: `${depName}-${currentValue}`,\n depType: 'github-runner',\n datasource: GithubRunnersDatasource.id,\n autoReplaceStringTemplate: '{{depName}}-{{newValue}}',\n };\n\n if (!dockerVersioning.api.isValid(currentValue)) {\n dependency.skipReason = 'invalid-version';\n }\n\n return dependency;\n}\n\nfunction extractSteps(steps: UsesStep[]): PackageDependency[] {\n const deps: PackageDependency[] = [];\n\n for (const step of steps) {\n const res = CommunityActions.safeParse(step);\n if (res.success) {\n deps.push(...res.data);\n }\n }\n\n return deps;\n}\n\nfunction extractWithYAMLParser(\n content: string,\n packageFile: string,\n config: ExtractConfig,\n): PackageDependency[] {\n logger.trace('github-actions.extractWithYAMLParser()');\n\n const obj = withMeta({ packageFile }, () => Workflow.parse(content));\n if (!obj) {\n return [];\n }\n\n if ('runs' in obj && obj.runs.steps) {\n return extractSteps(obj.runs.steps);\n }\n\n if (!('jobs' in obj)) {\n return [];\n }\n\n const deps: PackageDependency[] = [];\n\n for (const job of Object.values(obj.jobs)) {\n if (job.container) {\n const dep = getDep(job.container, true, config.registryAliases);\n // v8 ignore else -- `getDep()` always returns a dep\n if (dep) {\n dep.depType = 'container';\n deps.push(dep);\n }\n }\n\n for (const service of job.services) {\n const dep = getDep(service, true, config.registryAliases);\n // v8 ignore else -- `getDep()` always returns a dep\n if (dep) {\n dep.depType = 'service';\n deps.push(dep);\n }\n }\n\n for (const runner of job['runs-on']) {\n const dep = extractRunner(runner);\n if (dep) {\n deps.push(dep);\n }\n }\n\n deps.push(...extractSteps(job.steps));\n }\n\n return deps;\n}\n\nasync function readLockfile(): Promise<LockfileState> {\n const content = await readLocalFile(actionsLockFile, 'utf8');\n if (!content) {\n return { type: 'missing' };\n }\n\n const parsed = ActionsLockfile.safeParse(content);\n if (!parsed.success) {\n // `updateActionsLockfile` warns about this once per branch, so stay quiet\n logger.debug(`Failed to parse ${actionsLockFile}`);\n return { type: 'unparseable' };\n }\n\n return { type: 'parsed', onboardedWorkflows: parsed.data.workflows };\n}\n\n/**\n * A repository has a single lock file, but can have any number of package files, so read and parse it only once.\n */\nfunction getLockfile(): Promise<LockfileState> {\n const cacheKey = `github-actions:${actionsLockFile}`;\n const cached = memCache.get<Promise<LockfileState> | undefined>(cacheKey);\n if (cached !== undefined) {\n return cached;\n }\n\n const result = readLockfile();\n memCache.set(cacheKey, result);\n return result;\n}\n\n/**\n * Whether `gh actions-lock` owns the digests in this file.\n *\n * It rewrites the workflows it manages back to plain refs when regenerating, so an inline digest pin would be stripped straight back out.\n */\nasync function isManagedByLockfile(packageFile: string): Promise<boolean> {\n const lockfile = await getLockfile();\n\n switch (lockfile.type) {\n case 'missing':\n return false;\n case 'unparseable':\n // The tool refuses to regenerate a lock file which it cannot parse, so treat everything as managed: pinning inline here would raise a PR which pins the digests the tool owns and leaves the lock file stale.\n return true;\n case 'parsed':\n return isLockfileManaged(packageFile, lockfile.onboardedWorkflows);\n }\n}\n\nexport async function extractPackageFile(\n content: string,\n packageFile: string,\n config: ExtractConfig = {}, // TODO: enforce ExtractConfig\n): Promise<PackageFileContent | null> {\n logger.trace(`github-actions.extractPackageFile(${packageFile})`);\n\n const deps = [\n ...extractWithRegex(content, config),\n ...extractWithYAMLParser(content, packageFile, config),\n ];\n\n if (!deps.length) {\n return null;\n }\n\n const res: PackageFileContent = { deps };\n\n if (await isManagedByLockfile(packageFile)) {\n // Deliberately no `lockFiles`: nothing here goes through `updateArtifacts`, and `matchFileNames` also tests `lockFiles`, so declaring it would make a negated rule such as `!.github/workflows/release.yml` match every workflow through the lock file path.\n for (const dep of deps) {\n // The lock file only records `OWNER/REPO@REF` pins, so a `docker://` image in a `uses:` is still ours to pin.\n if (dep.depType === 'action' || dep.depType === 'workflow') {\n dep.digestManagedExternally = true;\n }\n }\n }\n\n return res;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"extract.js","names":["githubActionsVersioning.id","exactVersioning.id","memCache.get"],"sources":["../../../../lib/modules/manager/github-actions/extract.ts"],"sourcesContent":["import is from '@sindresorhus/is';\nimport { GlobalConfig } from '../../../config/global.ts';\nimport { PLATFORM_FAMILIES } from '../../../constants/index.ts';\nimport { logger, withMeta } from '../../../logger/index.ts';\nimport * as memCache from '../../../util/cache/memory/index.ts';\nimport { detectPlatform } from '../../../util/common.ts';\nimport { readLocalFile } from '../../../util/fs/index.ts';\nimport { newlineRegex, regEx } from '../../../util/regex.ts';\nimport { parseUrl } from '../../../util/url.ts';\nimport { GithubDigestDatasource } from '../../datasource/github-digest/index.ts';\nimport { GithubRunnersDatasource } from '../../datasource/github-runners/index.ts';\nimport { GithubTagsDatasource } from '../../datasource/github-tags/index.ts';\nimport * as dockerVersioning from '../../versioning/docker/index.ts';\nimport * as exactVersioning from '../../versioning/exact/index.ts';\nimport * as githubActionsVersioning from '../../versioning/github-actions/index.ts';\nimport { getDep } from '../dockerfile/extract.ts';\nimport type {\n ExtractConfig,\n PackageDependency,\n PackageFileContent,\n} from '../types.ts';\nimport { actionsLockFile, isLockfileManaged } from './common.ts';\nimport { isSha, isShortSha, parseUsesLine, versionLikeRe } from './parse.ts';\nimport type { UsesStep } from './schema.ts';\nimport { ActionsLockfile, CommunityActions, Workflow } from './schema.ts';\nimport type {\n DockerReference,\n LockfileState,\n RepositoryReference,\n} from './types.ts';\n\n// detects if we run against a Github Enterprise Server and adds the URL to the beginning of the registryURLs for looking up Actions\n// This reflects the behavior of how GitHub looks up Actions\n// First on the Enterprise Server, then on GitHub.com\nfunction detectCustomGitHubRegistryUrlsForActions(): PackageDependency {\n const endpoint = GlobalConfig.get('endpoint');\n const registryUrls = ['https://github.com'];\n if (endpoint && GlobalConfig.get('platform') === 'github') {\n const parsedEndpoint = parseUrl(endpoint);\n if (!parsedEndpoint) {\n logger.warn({ endpoint }, 'Failed to parse endpoint url');\n return {};\n }\n\n if (\n parsedEndpoint.host !== 'github.com' &&\n parsedEndpoint.host !== 'api.github.com'\n ) {\n registryUrls.unshift(\n `${parsedEndpoint.protocol}//${parsedEndpoint.host}`,\n );\n return { registryUrls };\n }\n }\n return {};\n}\n\nfunction extractDockerAction(\n actionRef: DockerReference,\n config: ExtractConfig,\n): PackageDependency {\n const dep = getDep(actionRef.originalRef, true, config.registryAliases);\n dep.depType = 'docker';\n dep.replaceString = actionRef.originalRef;\n return dep;\n}\n\nconst reusableWorkflowPathRe = regEx(/^\\.github\\/workflows\\/[^/]+\\.ya?ml$/);\n\nfunction extractRepositoryAction(\n actionRef: RepositoryReference,\n parsed: ReturnType<typeof parseUsesLine> & object,\n customRegistryUrlsPackageDependency: PackageDependency,\n): PackageDependency {\n const {\n replaceString: valueString,\n quote,\n commentData,\n commentPrecedingWhitespace,\n } = parsed;\n const {\n owner,\n repo,\n path: subPath,\n ref,\n hostname,\n isExplicitHostname,\n } = actionRef;\n\n const registryUrl = isExplicitHostname ? `https://${hostname}/` : '';\n const packageName = `${owner}/${repo}`;\n const depName = `${registryUrl}${packageName}`;\n const pathSuffix = subPath ? `/${subPath}` : '';\n const commentWs = commentPrecedingWhitespace || ' ';\n const isReusableWorkflow = !!subPath && reusableWorkflowPathRe.test(subPath);\n\n const dep: PackageDependency = {\n depName,\n commitMessageTopic: '{{{depName}}} action',\n versioning: githubActionsVersioning.id,\n depType: isReusableWorkflow ? 'workflow' : 'action',\n replaceString: valueString,\n autoReplaceStringTemplate: `${quote}{{depName}}${pathSuffix}@{{#if newDigest}}{{newDigest}}${quote}{{#if newValue}}${commentWs}# {{newValue}}{{/if}}{{/if}}{{#unless newDigest}}{{newValue}}${quote}{{/unless}}`,\n ...(isExplicitHostname\n ? detectDatasource(registryUrl)\n : customRegistryUrlsPackageDependency),\n };\n\n if (packageName !== depName) {\n dep.packageName = packageName;\n }\n\n // Extend replaceString to include relevant comment portions:\n // - Pinned version or ref: include only up to the matched token (truncate trailing text)\n // - Ratchet exclude: include the full comment to preserve the marker\n const pinComment =\n commentData.pinnedVersion ??\n (isSha(ref) || isShortSha(ref) ? commentData.ref : undefined);\n if (\n pinComment &&\n !is.undefined(commentData.index) &&\n !is.undefined(commentData.matchedString)\n ) {\n const cleanComment = parsed.commentString.slice(1);\n const matchEndIndex = commentData.index + commentData.matchedString.length;\n const commentSuffix = cleanComment.slice(0, matchEndIndex);\n dep.replaceString = `${valueString}${commentPrecedingWhitespace}#${commentSuffix}`;\n } else if (commentData.ratchetExclude) {\n dep.replaceString =\n valueString + commentPrecedingWhitespace + parsed.commentString;\n }\n\n if (isSha(ref)) {\n dep.currentValue = commentData.pinnedVersion ?? commentData.ref;\n dep.currentDigest = ref;\n } else if (isShortSha(ref)) {\n dep.currentValue = commentData.pinnedVersion ?? commentData.ref;\n dep.currentDigestShort = ref;\n } else {\n dep.currentValue = ref;\n }\n\n if (!dep.currentValue) {\n dep.enabled = false;\n dep.skipReason = 'unversioned-reference';\n }\n\n const isVersionLike =\n dep.currentValue && versionLikeRe.test(dep.currentValue);\n if (!dep.datasource && dep.currentValue && !isVersionLike) {\n dep.datasource = GithubDigestDatasource.id;\n dep.versioning = exactVersioning.id;\n }\n\n dep.datasource ??= GithubTagsDatasource.id;\n\n return dep;\n}\n\nfunction extractWithRegex(\n content: string,\n config: ExtractConfig,\n): PackageDependency[] {\n const customRegistryUrlsPackageDependency =\n detectCustomGitHubRegistryUrlsForActions();\n logger.trace('github-actions.extractWithRegex()');\n const deps: PackageDependency[] = [];\n\n for (const line of content.split(newlineRegex)) {\n if (line.trim().startsWith('#')) {\n continue;\n }\n\n const parsed = parseUsesLine(line);\n if (!parsed?.actionRef) {\n continue;\n }\n\n const { actionRef } = parsed;\n\n if (actionRef.kind === 'docker') {\n deps.push(extractDockerAction(actionRef, config));\n continue;\n }\n\n // v8 ignore else -- the parsed ref is either a docker or a repository ref\n if (actionRef.kind === 'repository') {\n deps.push(\n extractRepositoryAction(\n actionRef,\n parsed,\n customRegistryUrlsPackageDependency,\n ),\n );\n }\n }\n\n return deps;\n}\n\nfunction detectDatasource(registryUrl: string): PackageDependency {\n const platform = detectPlatform(registryUrl);\n\n switch (platform) {\n case 'forgejo':\n case 'gitea':\n return {\n registryUrls: [registryUrl],\n datasource: PLATFORM_FAMILIES[platform].tagsDatasource,\n };\n case 'github':\n // GitHub is left without a datasource on purpose: `extractRepositoryAction`\n // then picks `github-digest` or `github-tags` from the ref it parsed.\n return { registryUrls: [registryUrl] };\n }\n\n return {\n skipReason: 'unsupported-url',\n };\n}\n\nconst runnerVersionRegex = regEx(\n /^\\s*(?<depName>[a-zA-Z]+)-(?<currentValue>[^\\s]+)/,\n);\n\nfunction extractRunner(runner: string): PackageDependency | null {\n const runnerVersionGroups = runnerVersionRegex.exec(runner)?.groups;\n if (!runnerVersionGroups) {\n return null;\n }\n\n const { depName, currentValue } = runnerVersionGroups;\n\n if (!GithubRunnersDatasource.isValidRunner(depName, currentValue)) {\n return null;\n }\n\n const dependency: PackageDependency = {\n depName,\n currentValue,\n replaceString: `${depName}-${currentValue}`,\n depType: 'github-runner',\n datasource: GithubRunnersDatasource.id,\n autoReplaceStringTemplate: '{{depName}}-{{newValue}}',\n };\n\n if (!dockerVersioning.api.isValid(currentValue)) {\n dependency.skipReason = 'invalid-version';\n }\n\n return dependency;\n}\n\nfunction extractSteps(steps: UsesStep[]): PackageDependency[] {\n const deps: PackageDependency[] = [];\n\n for (const step of steps) {\n const res = CommunityActions.safeParse(step);\n if (res.success) {\n deps.push(...res.data);\n }\n }\n\n return deps;\n}\n\nfunction extractWithYAMLParser(\n content: string,\n packageFile: string,\n config: ExtractConfig,\n): PackageDependency[] {\n logger.trace('github-actions.extractWithYAMLParser()');\n\n const obj = withMeta({ packageFile }, () => Workflow.parse(content));\n if (!obj) {\n return [];\n }\n\n if ('runs' in obj && obj.runs.steps) {\n return extractSteps(obj.runs.steps);\n }\n\n if (!('jobs' in obj)) {\n return [];\n }\n\n const deps: PackageDependency[] = [];\n\n for (const job of Object.values(obj.jobs)) {\n if (job.container) {\n const dep = getDep(job.container, true, config.registryAliases);\n // v8 ignore else -- `getDep()` always returns a dep\n if (dep) {\n dep.depType = 'container';\n deps.push(dep);\n }\n }\n\n for (const service of job.services) {\n const dep = getDep(service, true, config.registryAliases);\n // v8 ignore else -- `getDep()` always returns a dep\n if (dep) {\n dep.depType = 'service';\n deps.push(dep);\n }\n }\n\n for (const runner of job['runs-on']) {\n const dep = extractRunner(runner);\n if (dep) {\n deps.push(dep);\n }\n }\n\n deps.push(...extractSteps(job.steps));\n }\n\n return deps;\n}\n\nasync function readLockfile(): Promise<LockfileState> {\n const content = await readLocalFile(actionsLockFile, 'utf8');\n if (!content) {\n return { type: 'missing' };\n }\n\n const parsed = ActionsLockfile.safeParse(content);\n if (!parsed.success) {\n // `updateActionsLockfile` warns about this once per branch, so stay quiet\n logger.debug(`Failed to parse ${actionsLockFile}`);\n return { type: 'unparseable' };\n }\n\n return { type: 'parsed', onboardedWorkflows: parsed.data.workflows };\n}\n\n/**\n * A repository has a single lock file, but can have any number of package files, so read and parse it only once.\n */\nfunction getLockfile(): Promise<LockfileState> {\n const cacheKey = `github-actions:${actionsLockFile}`;\n const cached = memCache.get<Promise<LockfileState> | undefined>(cacheKey);\n if (cached !== undefined) {\n return cached;\n }\n\n const result = readLockfile();\n memCache.set(cacheKey, result);\n return result;\n}\n\n/**\n * Whether `gh actions-lock` owns the digests in this file.\n *\n * It rewrites the workflows it manages back to plain refs when regenerating, so an inline digest pin would be stripped straight back out.\n */\nasync function isManagedByLockfile(packageFile: string): Promise<boolean> {\n const lockfile = await getLockfile();\n\n switch (lockfile.type) {\n case 'missing':\n return false;\n case 'unparseable':\n // The tool refuses to regenerate a lock file which it cannot parse, so treat everything as managed: pinning inline here would raise a PR which pins the digests the tool owns and leaves the lock file stale.\n return true;\n case 'parsed':\n return isLockfileManaged(packageFile, lockfile.onboardedWorkflows);\n }\n}\n\nexport async function extractPackageFile(\n content: string,\n packageFile: string,\n config: ExtractConfig = {}, // TODO: enforce ExtractConfig\n): Promise<PackageFileContent | null> {\n logger.trace(`github-actions.extractPackageFile(${packageFile})`);\n\n const deps = [\n ...extractWithRegex(content, config),\n ...extractWithYAMLParser(content, packageFile, config),\n ];\n\n if (!deps.length) {\n return null;\n }\n\n const res: PackageFileContent = { deps };\n\n if (await isManagedByLockfile(packageFile)) {\n // Deliberately no `lockFiles`: nothing here goes through `updateArtifacts`, and `matchFileNames` also tests `lockFiles`, so declaring it would make a negated rule such as `!.github/workflows/release.yml` match every workflow through the lock file path.\n for (const dep of deps) {\n // The lock file only records `OWNER/REPO@REF` pins, so a `docker://` image in a `uses:` is still ours to pin.\n if (dep.depType === 'action' || dep.depType === 'workflow') {\n dep.digestManagedExternally = true;\n }\n }\n }\n\n return res;\n}\n"],"mappings":"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"}
@@ -1,18 +1,22 @@
1
1
  import { newlineRegex, regEx } from "../../../util/regex.js";
2
2
  import { coerceArray } from "../../../util/array.js";
3
3
  import { logger } from "../../../logger/index.js";
4
+ import { PLATFORM_FAMILIES } from "../../../constants/platforms.js";
4
5
  import { find } from "../../../util/host-rules.js";
5
6
  import { detectPlatform } from "../../../util/common.js";
6
7
  import { parseSingleYaml } from "../../../util/yaml.js";
7
- import { ForgejoTagsDatasource } from "../../datasource/forgejo-tags/index.js";
8
- import { GithubTagsDatasource } from "../../datasource/github-tags/index.js";
9
- import { GitlabTagsDatasource } from "../../datasource/gitlab-tags/index.js";
10
8
  import { extractDependency as extractDependency$1 } from "../npm/extract/common/dependency.js";
11
9
  import { parseLine } from "../gomod/line-parser.js";
12
10
  import { pep508ToPackageDependency } from "../pep621/utils.js";
13
11
  import { matchesPrecommitConfigHeuristic, matchesPrecommitDependencyHeuristic } from "./parsing.js";
14
12
  import { isEmptyObject, isNonEmptyObject, isPlainObject } from "@sindresorhus/is";
15
13
  //#region lib/modules/manager/pre-commit/extract.ts
14
+ /** The platform families pre-commit can resolve `rev:` tags on. */
15
+ const supportedFamilies = [
16
+ "forgejo",
17
+ "github",
18
+ "gitlab"
19
+ ];
16
20
  /**
17
21
  * Determines the datasource(id) to be used for this dependency
18
22
  * @param repository the full git url, ie git@github.com/user/project.
@@ -31,7 +35,7 @@ function determineDatasource(repository, hostname) {
31
35
  hostname
32
36
  }, "Found forgejo dependency with custom registryUrl");
33
37
  return {
34
- datasource: ForgejoTagsDatasource.id,
38
+ datasource: PLATFORM_FAMILIES.forgejo.tagsDatasource,
35
39
  registryUrls: [`https://${hostname}`]
36
40
  };
37
41
  }
@@ -40,14 +44,14 @@ function determineDatasource(repository, hostname) {
40
44
  repository,
41
45
  hostname
42
46
  }, "Found github dependency");
43
- return { datasource: GithubTagsDatasource.id };
47
+ return { datasource: PLATFORM_FAMILIES.github.tagsDatasource };
44
48
  }
45
49
  if (hostname === "gitlab.com") {
46
50
  logger.debug({
47
51
  repository,
48
52
  hostname
49
53
  }, "Found gitlab dependency");
50
- return { datasource: GitlabTagsDatasource.id };
54
+ return { datasource: PLATFORM_FAMILIES.gitlab.tagsDatasource };
51
55
  }
52
56
  if (platform === "gitlab") {
53
57
  logger.debug({
@@ -55,7 +59,7 @@ function determineDatasource(repository, hostname) {
55
59
  hostname
56
60
  }, "Found gitlab dependency with custom registryUrl");
57
61
  return {
58
- datasource: GitlabTagsDatasource.id,
62
+ datasource: PLATFORM_FAMILIES.gitlab.tagsDatasource,
59
63
  registryUrls: [`https://${hostname}`]
60
64
  };
61
65
  }
@@ -71,11 +75,7 @@ function determineDatasource(repository, hostname) {
71
75
  registryUrls: [hostname]
72
76
  };
73
77
  }
74
- for (const [hostType, sourceId] of [
75
- ["forgejo", ForgejoTagsDatasource.id],
76
- ["github", GithubTagsDatasource.id],
77
- ["gitlab", GitlabTagsDatasource.id]
78
- ]) if (isNonEmptyObject(find({
78
+ for (const hostType of supportedFamilies) if (isNonEmptyObject(find({
79
79
  hostType,
80
80
  url: hostUrl
81
81
  }))) {
@@ -85,7 +85,7 @@ function determineDatasource(repository, hostname) {
85
85
  hostType
86
86
  }, `Provided hostname matches a ${hostType} hostrule.`);
87
87
  return {
88
- datasource: sourceId,
88
+ datasource: PLATFORM_FAMILIES[hostType].tagsDatasource,
89
89
  registryUrls: [hostname]
90
90
  };
91
91
  }
@@ -1 +1 @@
1
- {"version":3,"file":"extract.js","names":["npmExtractDependency"],"sources":["../../../../lib/modules/manager/pre-commit/extract.ts"],"sourcesContent":["import {\n isEmptyObject,\n isNonEmptyObject,\n isPlainObject,\n} from '@sindresorhus/is';\nimport { logger } from '../../../logger/index.ts';\nimport type { SkipReason } from '../../../types/index.ts';\nimport { coerceArray } from '../../../util/array.ts';\nimport { detectPlatform } from '../../../util/common.ts';\nimport { find } from '../../../util/host-rules.ts';\nimport { newlineRegex, regEx } from '../../../util/regex.ts';\nimport { parseSingleYaml } from '../../../util/yaml.ts';\nimport { ForgejoTagsDatasource } from '../../datasource/forgejo-tags/index.ts';\nimport { GithubTagsDatasource } from '../../datasource/github-tags/index.ts';\nimport { GitlabTagsDatasource } from '../../datasource/gitlab-tags/index.ts';\nimport { parseLine } from '../gomod/line-parser.ts';\nimport { extractDependency as npmExtractDependency } from '../npm/extract/common/dependency.ts';\nimport { pep508ToPackageDependency } from '../pep621/utils.ts';\nimport type { PackageDependency, PackageFileContent } from '../types.ts';\nimport {\n matchesPrecommitConfigHeuristic,\n matchesPrecommitDependencyHeuristic,\n} from './parsing.ts';\nimport type { PreCommitConfig } from './types.ts';\n\n/**\n * Determines the datasource(id) to be used for this dependency\n * @param repository the full git url, ie git@github.com/user/project.\n * Used in debug statements to clearly indicate the related dependency.\n * @param hostname the hostname (ie github.com)\n * Used to determine which renovate datasource should be used.\n * Is matched literally against `github.com` and `gitlab.com`.\n * If that doesn't match, `hostRules.find()` is used to find related sources.\n * In that case, the hostname is passed on as registryUrl to the corresponding datasource.\n */\nfunction determineDatasource(\n repository: string,\n hostname: string,\n): { datasource?: string; registryUrls?: string[]; skipReason?: SkipReason } {\n const platform = detectPlatform(repository);\n\n if (platform === 'forgejo') {\n logger.debug(\n { repository, hostname },\n 'Found forgejo dependency with custom registryUrl',\n );\n return {\n datasource: ForgejoTagsDatasource.id,\n registryUrls: [`https://${hostname}`],\n };\n }\n if (hostname === 'github.com' || platform === 'github') {\n logger.debug({ repository, hostname }, 'Found github dependency');\n return { datasource: GithubTagsDatasource.id };\n }\n if (hostname === 'gitlab.com') {\n logger.debug({ repository, hostname }, 'Found gitlab dependency');\n return { datasource: GitlabTagsDatasource.id };\n }\n if (platform === 'gitlab') {\n logger.debug(\n { repository, hostname },\n 'Found gitlab dependency with custom registryUrl',\n );\n return {\n datasource: GitlabTagsDatasource.id,\n registryUrls: [`https://${hostname}`],\n };\n }\n const hostUrl = `https://${hostname}`;\n const res = find({ url: hostUrl });\n if (isEmptyObject(res)) {\n // 1 check, to possibly prevent 3 failures in combined query of hostType & url.\n logger.debug(\n { repository, hostUrl },\n 'Provided hostname does not match any hostRules. Ignoring',\n );\n return { skipReason: 'unknown-registry', registryUrls: [hostname] };\n }\n for (const [hostType, sourceId] of [\n ['forgejo', ForgejoTagsDatasource.id],\n ['github', GithubTagsDatasource.id],\n ['gitlab', GitlabTagsDatasource.id],\n ]) {\n if (isNonEmptyObject(find({ hostType, url: hostUrl }))) {\n logger.debug(\n { repository, hostUrl, hostType },\n `Provided hostname matches a ${hostType} hostrule.`,\n );\n return { datasource: sourceId, registryUrls: [hostname] };\n }\n }\n logger.debug(\n { repository, registry: hostUrl },\n 'Provided hostname did not match any of the hostRules of hostType forgejo, github nor gitlab',\n );\n return { skipReason: 'unknown-registry', registryUrls: [hostname] };\n}\n\nconst gitUrlRegex = regEx(/\\.git$/i);\n\n// Matches: rev: <digest><whitespace># frozen: <version>\nconst revLineWithFrozenCommentRegex = regEx(\n /^\\s*rev:\\s*(?<replaceString>(?<currentDigest>[a-f0-9]{40})(?<commentWhiteSpaces>\\s+)#\\s*frozen:\\s*(?<currentValue>\\S+))/,\n);\n\ninterface RegexDep {\n currentDigest: string;\n currentValue: string;\n replaceString: string;\n autoReplaceStringTemplate: string;\n}\n\nfunction extractWithRegex(content: string): Map<string, RegexDep> {\n logger.trace('pre-commit.extractWithRegex()');\n const regexDeps = new Map<string, RegexDep>();\n\n for (const line of content.split(newlineRegex)) {\n if (line.trim().startsWith('#')) {\n continue;\n }\n\n const match = revLineWithFrozenCommentRegex.exec(line);\n if (match?.groups) {\n const { currentDigest, currentValue, replaceString, commentWhiteSpaces } =\n match.groups;\n\n // Store by digest to correlate with YAML-extracted deps later\n regexDeps.set(currentDigest, {\n currentDigest,\n currentValue,\n replaceString,\n autoReplaceStringTemplate: `{{newDigest}}${commentWhiteSpaces}# frozen: {{newValue}}`,\n });\n }\n }\n\n return regexDeps;\n}\n\nfunction extractDependency(tag: string, repository: string): PackageDependency {\n logger.debug(`Found version ${tag}`);\n\n const urlMatchers = [\n // This splits \"http://my.github.com/user/repo\" -> \"my.github.com\" \"user/repo\n regEx('^https?://(?<hostname>[^/]+)/(?<depName>\\\\S*)'),\n // This splits \"git@private.registry.com:user/repo\" -> \"private.registry.com\" \"user/repo\n regEx('^git@(?<hostname>[^:]+):(?<depName>\\\\S*)'),\n // This split \"git://github.com/pre-commit/pre-commit-hooks\" -> \"github.com\" \"pre-commit/pre-commit-hooks\"\n regEx(/^git:\\/\\/(?<hostname>[^/]+)\\/(?<depName>\\S*)/),\n // This splits \"ssh://git@github.com/pre-commit/pre-commit-hooks\" -> \"github.com\" \"pre-commit/pre-commit-hooks\"\n regEx(/^ssh:\\/\\/git@(?<hostname>[^/]+)\\/(?<depName>\\S*)/),\n ];\n for (const urlMatcher of urlMatchers) {\n const match = urlMatcher.exec(repository);\n if (match?.groups) {\n const hostname = match.groups.hostname;\n const depName = match.groups.depName.replace(gitUrlRegex, '');\n const sourceDef = determineDatasource(repository, hostname);\n return {\n ...sourceDef,\n depName,\n depType: 'repository',\n packageName: depName,\n currentValue: tag,\n };\n }\n }\n logger.info(\n { repository },\n 'Could not separate hostname from full dependency url.',\n );\n return {\n depName: undefined,\n depType: 'repository',\n datasource: undefined,\n packageName: undefined,\n skipReason: 'invalid-url',\n currentValue: tag,\n };\n}\n\n/**\n * Find all supported dependencies in the pre-commit yaml object.\n *\n * @param precommitFile the parsed yaml config file\n * @param regexDeps Map of regex-extracted deps keyed by digest for enrichment\n */\nfunction findDependencies(\n precommitFile: PreCommitConfig,\n regexDeps: Map<string, RegexDep>,\n): PackageDependency[] {\n if (!precommitFile.repos) {\n logger.debug(`No repos section found, skipping file`);\n return [];\n }\n const packageDependencies: PackageDependency[] = [];\n\n for (const item of precommitFile.repos) {\n // meta hooks is defined from pre-commit and doesn't support `additional_dependencies`\n if (item.repo !== 'meta') {\n for (const hook of coerceArray(item.hooks)) {\n // normally language are not defined in yaml\n // only support it when it's explicitly defined.\n // this avoid to parse hooks from pre-commit-hooks.yaml from git repo\n if (hook.language === 'node') {\n hook.additional_dependencies?.map((req) => {\n const match = regEx('^(?<name>.+)@(?<range>.+)$').exec(req);\n if (!match?.groups) {\n return;\n }\n\n const depType = 'pre-commit-node';\n const dep = npmExtractDependency(\n depType,\n match.groups.name,\n match.groups.range,\n );\n packageDependencies.push({\n depType,\n depName: match.groups.name,\n packageName: match.groups.name,\n ...dep,\n });\n });\n } else if (hook.language === 'python') {\n hook.additional_dependencies?.map((req) => {\n const dep = pep508ToPackageDependency('pre-commit-python', req);\n if (dep) {\n packageDependencies.push(dep);\n }\n });\n } else if (hook.language === 'golang') {\n hook.additional_dependencies?.map((req) => {\n // Convert dependency into a gomod require line to use the gomod line parser\n const requireLine = `require ${req.replace('@', ' ')}`;\n const dep = parseLine(requireLine);\n if (dep) {\n const depType = 'pre-commit-golang';\n packageDependencies.push({\n ...dep,\n depType,\n });\n }\n });\n }\n }\n }\n\n if (matchesPrecommitDependencyHeuristic(item)) {\n logger.trace(item, 'Matched pre-commit dependency spec');\n const repository = String(item.repo);\n const tag = String(item.rev);\n const dep = extractDependency(tag, repository);\n\n // Check if this rev has regex-extracted formatting info\n const regexDep = regexDeps.get(tag);\n if (regexDep) {\n // Enrich with formatting info from regex extraction\n dep.currentDigest = regexDep.currentDigest;\n dep.currentValue = regexDep.currentValue;\n dep.replaceString = regexDep.replaceString;\n dep.autoReplaceStringTemplate = regexDep.autoReplaceStringTemplate;\n }\n\n packageDependencies.push(dep);\n } else {\n logger.trace(item, 'Did not find pre-commit repo spec');\n }\n }\n return packageDependencies;\n}\n\nexport function extractPackageFile(\n content: string,\n packageFile: string,\n): PackageFileContent | null {\n type ParsedContent = Record<string, unknown> | PreCommitConfig;\n let parsedContent: ParsedContent;\n try {\n // TODO: use schema (#9610)\n parsedContent = parseSingleYaml(content);\n } catch (err) {\n logger.debug(\n { filename: packageFile, err },\n 'Failed to parse pre-commit config YAML',\n );\n return null;\n }\n if (!isPlainObject<Record<string, unknown>>(parsedContent)) {\n logger.debug(\n { packageFile },\n `Parsing of pre-commit config YAML returned invalid result`,\n );\n return null;\n }\n if (!matchesPrecommitConfigHeuristic(parsedContent)) {\n logger.debug(\n { packageFile },\n `File does not look like a pre-commit config file`,\n );\n return null;\n }\n try {\n const regexDeps = extractWithRegex(content);\n const deps = findDependencies(parsedContent, regexDeps);\n if (deps.length) {\n logger.trace({ deps }, 'Found dependencies in pre-commit config');\n return { deps };\n }\n } catch (err) /* istanbul ignore next */ {\n logger.debug(\n { packageFile, err },\n 'Error scanning parsed pre-commit config',\n );\n }\n return null;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"extract.js","names":["npmExtractDependency"],"sources":["../../../../lib/modules/manager/pre-commit/extract.ts"],"sourcesContent":["import {\n isEmptyObject,\n isNonEmptyObject,\n isPlainObject,\n} from '@sindresorhus/is';\nimport type { PlatformFamilyId } from '../../../constants/index.ts';\nimport { PLATFORM_FAMILIES } from '../../../constants/index.ts';\nimport { logger } from '../../../logger/index.ts';\nimport type { SkipReason } from '../../../types/index.ts';\nimport { coerceArray } from '../../../util/array.ts';\nimport { detectPlatform } from '../../../util/common.ts';\nimport { find } from '../../../util/host-rules.ts';\nimport { newlineRegex, regEx } from '../../../util/regex.ts';\nimport { parseSingleYaml } from '../../../util/yaml.ts';\nimport { parseLine } from '../gomod/line-parser.ts';\nimport { extractDependency as npmExtractDependency } from '../npm/extract/common/dependency.ts';\nimport { pep508ToPackageDependency } from '../pep621/utils.ts';\nimport type { PackageDependency, PackageFileContent } from '../types.ts';\nimport {\n matchesPrecommitConfigHeuristic,\n matchesPrecommitDependencyHeuristic,\n} from './parsing.ts';\nimport type { PreCommitConfig } from './types.ts';\n\n/** The platform families pre-commit can resolve `rev:` tags on. */\nconst supportedFamilies = [\n 'forgejo',\n 'github',\n 'gitlab',\n] as const satisfies PlatformFamilyId[];\n\n/**\n * Determines the datasource(id) to be used for this dependency\n * @param repository the full git url, ie git@github.com/user/project.\n * Used in debug statements to clearly indicate the related dependency.\n * @param hostname the hostname (ie github.com)\n * Used to determine which renovate datasource should be used.\n * Is matched literally against `github.com` and `gitlab.com`.\n * If that doesn't match, `hostRules.find()` is used to find related sources.\n * In that case, the hostname is passed on as registryUrl to the corresponding datasource.\n */\nfunction determineDatasource(\n repository: string,\n hostname: string,\n): { datasource?: string; registryUrls?: string[]; skipReason?: SkipReason } {\n const platform = detectPlatform(repository);\n\n if (platform === 'forgejo') {\n logger.debug(\n { repository, hostname },\n 'Found forgejo dependency with custom registryUrl',\n );\n return {\n datasource: PLATFORM_FAMILIES.forgejo.tagsDatasource,\n registryUrls: [`https://${hostname}`],\n };\n }\n if (hostname === 'github.com' || platform === 'github') {\n logger.debug({ repository, hostname }, 'Found github dependency');\n return { datasource: PLATFORM_FAMILIES.github.tagsDatasource };\n }\n if (hostname === 'gitlab.com') {\n logger.debug({ repository, hostname }, 'Found gitlab dependency');\n return { datasource: PLATFORM_FAMILIES.gitlab.tagsDatasource };\n }\n if (platform === 'gitlab') {\n logger.debug(\n { repository, hostname },\n 'Found gitlab dependency with custom registryUrl',\n );\n return {\n datasource: PLATFORM_FAMILIES.gitlab.tagsDatasource,\n registryUrls: [`https://${hostname}`],\n };\n }\n const hostUrl = `https://${hostname}`;\n const res = find({ url: hostUrl });\n if (isEmptyObject(res)) {\n // 1 check, to possibly prevent 3 failures in combined query of hostType & url.\n logger.debug(\n { repository, hostUrl },\n 'Provided hostname does not match any hostRules. Ignoring',\n );\n return { skipReason: 'unknown-registry', registryUrls: [hostname] };\n }\n for (const hostType of supportedFamilies) {\n if (isNonEmptyObject(find({ hostType, url: hostUrl }))) {\n logger.debug(\n { repository, hostUrl, hostType },\n `Provided hostname matches a ${hostType} hostrule.`,\n );\n return {\n datasource: PLATFORM_FAMILIES[hostType].tagsDatasource,\n registryUrls: [hostname],\n };\n }\n }\n logger.debug(\n { repository, registry: hostUrl },\n 'Provided hostname did not match any of the hostRules of hostType forgejo, github nor gitlab',\n );\n return { skipReason: 'unknown-registry', registryUrls: [hostname] };\n}\n\nconst gitUrlRegex = regEx(/\\.git$/i);\n\n// Matches: rev: <digest><whitespace># frozen: <version>\nconst revLineWithFrozenCommentRegex = regEx(\n /^\\s*rev:\\s*(?<replaceString>(?<currentDigest>[a-f0-9]{40})(?<commentWhiteSpaces>\\s+)#\\s*frozen:\\s*(?<currentValue>\\S+))/,\n);\n\ninterface RegexDep {\n currentDigest: string;\n currentValue: string;\n replaceString: string;\n autoReplaceStringTemplate: string;\n}\n\nfunction extractWithRegex(content: string): Map<string, RegexDep> {\n logger.trace('pre-commit.extractWithRegex()');\n const regexDeps = new Map<string, RegexDep>();\n\n for (const line of content.split(newlineRegex)) {\n if (line.trim().startsWith('#')) {\n continue;\n }\n\n const match = revLineWithFrozenCommentRegex.exec(line);\n if (match?.groups) {\n const { currentDigest, currentValue, replaceString, commentWhiteSpaces } =\n match.groups;\n\n // Store by digest to correlate with YAML-extracted deps later\n regexDeps.set(currentDigest, {\n currentDigest,\n currentValue,\n replaceString,\n autoReplaceStringTemplate: `{{newDigest}}${commentWhiteSpaces}# frozen: {{newValue}}`,\n });\n }\n }\n\n return regexDeps;\n}\n\nfunction extractDependency(tag: string, repository: string): PackageDependency {\n logger.debug(`Found version ${tag}`);\n\n const urlMatchers = [\n // This splits \"http://my.github.com/user/repo\" -> \"my.github.com\" \"user/repo\n regEx('^https?://(?<hostname>[^/]+)/(?<depName>\\\\S*)'),\n // This splits \"git@private.registry.com:user/repo\" -> \"private.registry.com\" \"user/repo\n regEx('^git@(?<hostname>[^:]+):(?<depName>\\\\S*)'),\n // This split \"git://github.com/pre-commit/pre-commit-hooks\" -> \"github.com\" \"pre-commit/pre-commit-hooks\"\n regEx(/^git:\\/\\/(?<hostname>[^/]+)\\/(?<depName>\\S*)/),\n // This splits \"ssh://git@github.com/pre-commit/pre-commit-hooks\" -> \"github.com\" \"pre-commit/pre-commit-hooks\"\n regEx(/^ssh:\\/\\/git@(?<hostname>[^/]+)\\/(?<depName>\\S*)/),\n ];\n for (const urlMatcher of urlMatchers) {\n const match = urlMatcher.exec(repository);\n if (match?.groups) {\n const hostname = match.groups.hostname;\n const depName = match.groups.depName.replace(gitUrlRegex, '');\n const sourceDef = determineDatasource(repository, hostname);\n return {\n ...sourceDef,\n depName,\n depType: 'repository',\n packageName: depName,\n currentValue: tag,\n };\n }\n }\n logger.info(\n { repository },\n 'Could not separate hostname from full dependency url.',\n );\n return {\n depName: undefined,\n depType: 'repository',\n datasource: undefined,\n packageName: undefined,\n skipReason: 'invalid-url',\n currentValue: tag,\n };\n}\n\n/**\n * Find all supported dependencies in the pre-commit yaml object.\n *\n * @param precommitFile the parsed yaml config file\n * @param regexDeps Map of regex-extracted deps keyed by digest for enrichment\n */\nfunction findDependencies(\n precommitFile: PreCommitConfig,\n regexDeps: Map<string, RegexDep>,\n): PackageDependency[] {\n if (!precommitFile.repos) {\n logger.debug(`No repos section found, skipping file`);\n return [];\n }\n const packageDependencies: PackageDependency[] = [];\n\n for (const item of precommitFile.repos) {\n // meta hooks is defined from pre-commit and doesn't support `additional_dependencies`\n if (item.repo !== 'meta') {\n for (const hook of coerceArray(item.hooks)) {\n // normally language are not defined in yaml\n // only support it when it's explicitly defined.\n // this avoid to parse hooks from pre-commit-hooks.yaml from git repo\n if (hook.language === 'node') {\n hook.additional_dependencies?.map((req) => {\n const match = regEx('^(?<name>.+)@(?<range>.+)$').exec(req);\n if (!match?.groups) {\n return;\n }\n\n const depType = 'pre-commit-node';\n const dep = npmExtractDependency(\n depType,\n match.groups.name,\n match.groups.range,\n );\n packageDependencies.push({\n depType,\n depName: match.groups.name,\n packageName: match.groups.name,\n ...dep,\n });\n });\n } else if (hook.language === 'python') {\n hook.additional_dependencies?.map((req) => {\n const dep = pep508ToPackageDependency('pre-commit-python', req);\n if (dep) {\n packageDependencies.push(dep);\n }\n });\n } else if (hook.language === 'golang') {\n hook.additional_dependencies?.map((req) => {\n // Convert dependency into a gomod require line to use the gomod line parser\n const requireLine = `require ${req.replace('@', ' ')}`;\n const dep = parseLine(requireLine);\n if (dep) {\n const depType = 'pre-commit-golang';\n packageDependencies.push({\n ...dep,\n depType,\n });\n }\n });\n }\n }\n }\n\n if (matchesPrecommitDependencyHeuristic(item)) {\n logger.trace(item, 'Matched pre-commit dependency spec');\n const repository = String(item.repo);\n const tag = String(item.rev);\n const dep = extractDependency(tag, repository);\n\n // Check if this rev has regex-extracted formatting info\n const regexDep = regexDeps.get(tag);\n if (regexDep) {\n // Enrich with formatting info from regex extraction\n dep.currentDigest = regexDep.currentDigest;\n dep.currentValue = regexDep.currentValue;\n dep.replaceString = regexDep.replaceString;\n dep.autoReplaceStringTemplate = regexDep.autoReplaceStringTemplate;\n }\n\n packageDependencies.push(dep);\n } else {\n logger.trace(item, 'Did not find pre-commit repo spec');\n }\n }\n return packageDependencies;\n}\n\nexport function extractPackageFile(\n content: string,\n packageFile: string,\n): PackageFileContent | null {\n type ParsedContent = Record<string, unknown> | PreCommitConfig;\n let parsedContent: ParsedContent;\n try {\n // TODO: use schema (#9610)\n parsedContent = parseSingleYaml(content);\n } catch (err) {\n logger.debug(\n { filename: packageFile, err },\n 'Failed to parse pre-commit config YAML',\n );\n return null;\n }\n if (!isPlainObject<Record<string, unknown>>(parsedContent)) {\n logger.debug(\n { packageFile },\n `Parsing of pre-commit config YAML returned invalid result`,\n );\n return null;\n }\n if (!matchesPrecommitConfigHeuristic(parsedContent)) {\n logger.debug(\n { packageFile },\n `File does not look like a pre-commit config file`,\n );\n return null;\n }\n try {\n const regexDeps = extractWithRegex(content);\n const deps = findDependencies(parsedContent, regexDeps);\n if (deps.length) {\n logger.trace({ deps }, 'Found dependencies in pre-commit config');\n return { deps };\n }\n } catch (err) /* istanbul ignore next */ {\n logger.debug(\n { packageFile, err },\n 'Error scanning parsed pre-commit config',\n );\n }\n return null;\n}\n"],"mappings":"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"}