renovate 44.99.0 → 44.101.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 (29) hide show
  1. package/dist/modules/manager/bun/artifacts.js +25 -22
  2. package/dist/modules/manager/bun/artifacts.js.map +1 -1
  3. package/dist/modules/manager/deno/artifacts.js +38 -35
  4. package/dist/modules/manager/deno/artifacts.js.map +1 -1
  5. package/dist/modules/manager/devbox/artifacts.js +6 -6
  6. package/dist/modules/manager/devbox/artifacts.js.map +1 -1
  7. package/dist/modules/manager/dockerfile/apk.js +3 -65
  8. package/dist/modules/manager/dockerfile/apk.js.map +1 -1
  9. package/dist/modules/manager/dockerfile/deb.js +108 -0
  10. package/dist/modules/manager/dockerfile/deb.js.map +1 -0
  11. package/dist/modules/manager/dockerfile/extract.js +2 -1
  12. package/dist/modules/manager/dockerfile/extract.js.map +1 -1
  13. package/dist/modules/manager/dockerfile/index.js +6 -1
  14. package/dist/modules/manager/dockerfile/index.js.map +1 -1
  15. package/dist/modules/manager/dockerfile/run-command.js +92 -0
  16. package/dist/modules/manager/dockerfile/run-command.js.map +1 -0
  17. package/dist/modules/manager/fingerprint.generated.js +3 -3
  18. package/dist/modules/manager/fingerprint.generated.js.map +1 -1
  19. package/dist/modules/manager/npm/artifacts.js +20 -22
  20. package/dist/modules/manager/npm/artifacts.js.map +1 -1
  21. package/dist/modules/manager/npm/post-update/index.js +113 -116
  22. package/dist/modules/manager/npm/post-update/index.js.map +1 -1
  23. package/dist/modules/manager/npm/utils.js +15 -1
  24. package/dist/modules/manager/npm/utils.js.map +1 -1
  25. package/dist/modules/manager/types.d.ts +6 -2
  26. package/dist/modules/manager/util.js +9 -2
  27. package/dist/modules/manager/util.js.map +1 -1
  28. package/package.json +1 -1
  29. package/renovate-schema.json +2 -2
@@ -1,11 +1,11 @@
1
1
  import "../../../constants/error-messages.js";
2
2
  import { GlobalConfig } from "../../../config/global.js";
3
3
  import { logger } from "../../../logger/index.js";
4
- import { deleteLocalFile, readLocalFile, writeLocalFile } from "../../../util/fs/index.js";
4
+ import { readLocalFile } from "../../../util/fs/index.js";
5
5
  import { exec } from "../../../util/exec/index.js";
6
- import { artifactErrorResult, fileAddition, resolveToolConstraint } from "../util.js";
6
+ import { artifactErrorResult, resolveToolConstraint, updateLockFile } from "../util.js";
7
7
  import { processHostRules } from "../npm/post-update/rules.js";
8
- import { getNpmrcContent, resetNpmrcContent, updateNpmrcContent } from "../npm/utils.js";
8
+ import { withNpmrcHostRules } from "../npm/utils.js";
9
9
  import { isEmptyArray } from "@sindresorhus/is";
10
10
  import upath from "upath";
11
11
  //#region lib/modules/manager/bun/artifacts.ts
@@ -28,27 +28,30 @@ async function updateArtifacts(updateArtifact) {
28
28
  return null;
29
29
  }
30
30
  const pkgFileDir = upath.dirname(packageFileName);
31
- const npmrcContent = await getNpmrcContent(pkgFileDir);
32
31
  const { additionalNpmrcContent } = processHostRules();
33
- await updateNpmrcContent(pkgFileDir, npmrcContent, additionalNpmrcContent);
34
32
  try {
35
- await writeLocalFile(packageFileName, newPackageFileContent);
36
- if (isLockFileMaintenance) await deleteLocalFile(lockFileName);
37
- let cmd = "bun install";
38
- if (!GlobalConfig.get("allowScripts") || config.ignoreScripts) cmd += " --ignore-scripts";
39
- const execOptions = {
40
- cwdFile: lockFileName,
41
- docker: {},
42
- toolConstraints: [{
43
- toolName: "bun",
44
- constraint: await resolveToolConstraint(config, "bun")
45
- }]
46
- };
47
- await exec(cmd, execOptions);
48
- await resetNpmrcContent(pkgFileDir, npmrcContent);
49
- const newLockFileContent = await readLocalFile(lockFileName);
50
- if (!newLockFileContent || Buffer.compare(oldLockFileContent, newLockFileContent) === 0) return null;
51
- return [fileAddition(lockFileName, newLockFileContent)];
33
+ return await withNpmrcHostRules(pkgFileDir, additionalNpmrcContent, async () => {
34
+ let cmd = "bun install";
35
+ if (!GlobalConfig.get("allowScripts") || config.ignoreScripts) cmd += " --ignore-scripts";
36
+ const execOptions = {
37
+ cwdFile: lockFileName,
38
+ docker: {},
39
+ toolConstraints: [{
40
+ toolName: "bun",
41
+ constraint: await resolveToolConstraint(config, "bun")
42
+ }]
43
+ };
44
+ return await updateLockFile({
45
+ lockFileName,
46
+ existingLockFileContent: oldLockFileContent,
47
+ packageFile: {
48
+ path: packageFileName,
49
+ contents: newPackageFileContent
50
+ },
51
+ deleteLockFile: isLockFileMaintenance,
52
+ run: () => exec(cmd, execOptions)
53
+ });
54
+ });
52
55
  } catch (err) {
53
56
  if (err.message === "temporary-error") throw err;
54
57
  logger.warn({
@@ -1 +1 @@
1
- {"version":3,"file":"artifacts.js","names":[],"sources":["../../../../lib/modules/manager/bun/artifacts.ts"],"sourcesContent":["import { isEmptyArray } from '@sindresorhus/is';\nimport upath from 'upath';\nimport { GlobalConfig } from '../../../config/global.ts';\nimport { TEMPORARY_ERROR } from '../../../constants/error-messages.ts';\nimport { logger } from '../../../logger/index.ts';\nimport { exec } from '../../../util/exec/index.ts';\nimport type { ExecOptions } from '../../../util/exec/types.ts';\nimport {\n deleteLocalFile,\n readLocalFile,\n writeLocalFile,\n} from '../../../util/fs/index.ts';\nimport { processHostRules } from '../npm/post-update/rules.ts';\nimport {\n getNpmrcContent,\n resetNpmrcContent,\n updateNpmrcContent,\n} from '../npm/utils.ts';\nimport type { UpdateArtifact, UpdateArtifactsResult } from '../types.ts';\nimport {\n artifactErrorResult,\n fileAddition,\n resolveToolConstraint,\n} from '../util.ts';\n\nexport async function updateArtifacts(\n updateArtifact: UpdateArtifact,\n): Promise<UpdateArtifactsResult[] | null> {\n const { packageFileName, updatedDeps, newPackageFileContent, config } =\n updateArtifact;\n logger.debug(`bun.updateArtifacts(${packageFileName})`);\n const { isLockFileMaintenance } = config;\n\n if (isEmptyArray(updatedDeps) && !isLockFileMaintenance) {\n logger.debug('No updated bun deps - returning null');\n return null;\n }\n\n // Find the first bun dependency in order to handle mixed manager updates,\n // eventually falling back to the first lock file from config.\n const lockFileName =\n updatedDeps.find((dep) => dep.manager === 'bun')?.lockFiles?.[0] ??\n config.lockFiles?.[0];\n\n if (!lockFileName) {\n logger.debug(`bun: No lock file found`);\n return null;\n }\n\n const oldLockFileContent = await readLocalFile(lockFileName);\n if (!oldLockFileContent) {\n logger.debug(`No ${lockFileName} found`);\n return null;\n }\n\n const pkgFileDir = upath.dirname(packageFileName);\n const npmrcContent = await getNpmrcContent(pkgFileDir);\n const { additionalNpmrcContent } = processHostRules();\n await updateNpmrcContent(pkgFileDir, npmrcContent, additionalNpmrcContent);\n\n try {\n await writeLocalFile(packageFileName, newPackageFileContent);\n if (isLockFileMaintenance) {\n await deleteLocalFile(lockFileName);\n }\n\n let cmd = 'bun install';\n\n if (!GlobalConfig.get('allowScripts') || config.ignoreScripts) {\n cmd += ' --ignore-scripts';\n }\n\n const execOptions: ExecOptions = {\n cwdFile: lockFileName,\n docker: {},\n toolConstraints: [\n {\n toolName: 'bun',\n constraint: await resolveToolConstraint(config, 'bun'),\n },\n ],\n };\n\n await exec(cmd, execOptions);\n await resetNpmrcContent(pkgFileDir, npmrcContent);\n\n const newLockFileContent = await readLocalFile(lockFileName);\n if (\n !newLockFileContent ||\n Buffer.compare(oldLockFileContent, newLockFileContent) === 0\n ) {\n return null;\n }\n return [fileAddition(lockFileName, newLockFileContent)];\n } catch (err) {\n if (err.message === TEMPORARY_ERROR) {\n throw err;\n }\n logger.warn({ lockfile: lockFileName, err }, `Failed to update lock file`);\n return artifactErrorResult(lockFileName, err);\n }\n}\n"],"mappings":";;;;;;;;;;;AAyBA,eAAsB,gBACpB,gBACyC;CACzC,MAAM,EAAE,iBAAiB,aAAa,uBAAuB,WAC3D;CACF,OAAO,MAAM,uBAAuB,gBAAgB,EAAE;CACtD,MAAM,EAAE,0BAA0B;CAElC,IAAI,aAAa,WAAW,KAAK,CAAC,uBAAuB;EACvD,OAAO,MAAM,sCAAsC;EACnD,OAAO;CACT;CAIA,MAAM,eACJ,YAAY,MAAM,QAAQ,IAAI,YAAY,KAAK,CAAC,EAAE,YAAY,MAC9D,OAAO,YAAY;CAErB,IAAI,CAAC,cAAc;EACjB,OAAO,MAAM,yBAAyB;EACtC,OAAO;CACT;CAEA,MAAM,qBAAqB,MAAM,cAAc,YAAY;CAC3D,IAAI,CAAC,oBAAoB;EACvB,OAAO,MAAM,MAAM,aAAa,OAAO;EACvC,OAAO;CACT;CAEA,MAAM,aAAa,MAAM,QAAQ,eAAe;CAChD,MAAM,eAAe,MAAM,gBAAgB,UAAU;CACrD,MAAM,EAAE,2BAA2B,iBAAiB;CACpD,MAAM,mBAAmB,YAAY,cAAc,sBAAsB;CAEzE,IAAI;EACF,MAAM,eAAe,iBAAiB,qBAAqB;EAC3D,IAAI,uBACF,MAAM,gBAAgB,YAAY;EAGpC,IAAI,MAAM;EAEV,IAAI,CAAC,aAAa,IAAI,cAAc,KAAK,OAAO,eAC9C,OAAO;EAGT,MAAM,cAA2B;GAC/B,SAAS;GACT,QAAQ,CAAC;GACT,iBAAiB,CACf;IACE,UAAU;IACV,YAAY,MAAM,sBAAsB,QAAQ,KAAK;GACvD,CACF;EACF;EAEA,MAAM,KAAK,KAAK,WAAW;EAC3B,MAAM,kBAAkB,YAAY,YAAY;EAEhD,MAAM,qBAAqB,MAAM,cAAc,YAAY;EAC3D,IACE,CAAC,sBACD,OAAO,QAAQ,oBAAoB,kBAAkB,MAAM,GAE3D,OAAO;EAET,OAAO,CAAC,aAAa,cAAc,kBAAkB,CAAC;CACxD,SAAS,KAAK;EACZ,IAAI,IAAI,YAAA,mBACN,MAAM;EAER,OAAO,KAAK;GAAE,UAAU;GAAc;EAAI,GAAG,4BAA4B;EACzE,OAAO,oBAAoB,cAAc,GAAG;CAC9C;AACF"}
1
+ {"version":3,"file":"artifacts.js","names":[],"sources":["../../../../lib/modules/manager/bun/artifacts.ts"],"sourcesContent":["import { isEmptyArray } from '@sindresorhus/is';\nimport upath from 'upath';\nimport { GlobalConfig } from '../../../config/global.ts';\nimport { TEMPORARY_ERROR } from '../../../constants/error-messages.ts';\nimport { logger } from '../../../logger/index.ts';\nimport { exec } from '../../../util/exec/index.ts';\nimport type { ExecOptions } from '../../../util/exec/types.ts';\nimport { readLocalFile } from '../../../util/fs/index.ts';\nimport { processHostRules } from '../npm/post-update/rules.ts';\nimport { withNpmrcHostRules } from '../npm/utils.ts';\nimport type { UpdateArtifact, UpdateArtifactsResult } from '../types.ts';\nimport {\n artifactErrorResult,\n resolveToolConstraint,\n updateLockFile,\n} from '../util.ts';\n\nexport async function updateArtifacts(\n updateArtifact: UpdateArtifact,\n): Promise<UpdateArtifactsResult[] | null> {\n const { packageFileName, updatedDeps, newPackageFileContent, config } =\n updateArtifact;\n logger.debug(`bun.updateArtifacts(${packageFileName})`);\n const { isLockFileMaintenance } = config;\n\n if (isEmptyArray(updatedDeps) && !isLockFileMaintenance) {\n logger.debug('No updated bun deps - returning null');\n return null;\n }\n\n // Find the first bun dependency in order to handle mixed manager updates,\n // eventually falling back to the first lock file from config.\n const lockFileName =\n updatedDeps.find((dep) => dep.manager === 'bun')?.lockFiles?.[0] ??\n config.lockFiles?.[0];\n\n if (!lockFileName) {\n logger.debug(`bun: No lock file found`);\n return null;\n }\n\n const oldLockFileContent = await readLocalFile(lockFileName);\n if (!oldLockFileContent) {\n logger.debug(`No ${lockFileName} found`);\n return null;\n }\n\n const pkgFileDir = upath.dirname(packageFileName);\n const { additionalNpmrcContent } = processHostRules();\n\n try {\n return await withNpmrcHostRules(\n pkgFileDir,\n additionalNpmrcContent,\n async () => {\n let cmd = 'bun install';\n\n if (!GlobalConfig.get('allowScripts') || config.ignoreScripts) {\n cmd += ' --ignore-scripts';\n }\n\n const execOptions: ExecOptions = {\n cwdFile: lockFileName,\n docker: {},\n toolConstraints: [\n {\n toolName: 'bun',\n constraint: await resolveToolConstraint(config, 'bun'),\n },\n ],\n };\n\n return await updateLockFile({\n lockFileName,\n existingLockFileContent: oldLockFileContent,\n packageFile: {\n path: packageFileName,\n contents: newPackageFileContent,\n },\n deleteLockFile: isLockFileMaintenance,\n run: () => exec(cmd, execOptions),\n });\n },\n );\n } catch (err) {\n if (err.message === TEMPORARY_ERROR) {\n throw err;\n }\n logger.warn({ lockfile: lockFileName, err }, `Failed to update lock file`);\n return artifactErrorResult(lockFileName, err);\n }\n}\n"],"mappings":";;;;;;;;;;;AAiBA,eAAsB,gBACpB,gBACyC;CACzC,MAAM,EAAE,iBAAiB,aAAa,uBAAuB,WAC3D;CACF,OAAO,MAAM,uBAAuB,gBAAgB,EAAE;CACtD,MAAM,EAAE,0BAA0B;CAElC,IAAI,aAAa,WAAW,KAAK,CAAC,uBAAuB;EACvD,OAAO,MAAM,sCAAsC;EACnD,OAAO;CACT;CAIA,MAAM,eACJ,YAAY,MAAM,QAAQ,IAAI,YAAY,KAAK,CAAC,EAAE,YAAY,MAC9D,OAAO,YAAY;CAErB,IAAI,CAAC,cAAc;EACjB,OAAO,MAAM,yBAAyB;EACtC,OAAO;CACT;CAEA,MAAM,qBAAqB,MAAM,cAAc,YAAY;CAC3D,IAAI,CAAC,oBAAoB;EACvB,OAAO,MAAM,MAAM,aAAa,OAAO;EACvC,OAAO;CACT;CAEA,MAAM,aAAa,MAAM,QAAQ,eAAe;CAChD,MAAM,EAAE,2BAA2B,iBAAiB;CAEpD,IAAI;EACF,OAAO,MAAM,mBACX,YACA,wBACA,YAAY;GACV,IAAI,MAAM;GAEV,IAAI,CAAC,aAAa,IAAI,cAAc,KAAK,OAAO,eAC9C,OAAO;GAGT,MAAM,cAA2B;IAC/B,SAAS;IACT,QAAQ,CAAC;IACT,iBAAiB,CACf;KACE,UAAU;KACV,YAAY,MAAM,sBAAsB,QAAQ,KAAK;IACvD,CACF;GACF;GAEA,OAAO,MAAM,eAAe;IAC1B;IACA,yBAAyB;IACzB,aAAa;KACX,MAAM;KACN,UAAU;IACZ;IACA,gBAAgB;IAChB,WAAW,KAAK,KAAK,WAAW;GAClC,CAAC;EACH,CACF;CACF,SAAS,KAAK;EACZ,IAAI,IAAI,YAAA,mBACN,MAAM;EAER,OAAO,KAAK;GAAE,UAAU;GAAc;EAAI,GAAG,4BAA4B;EACzE,OAAO,oBAAoB,cAAc,GAAG;CAC9C;AACF"}
@@ -1,11 +1,11 @@
1
1
  import "../../../constants/error-messages.js";
2
2
  import { logger } from "../../../logger/index.js";
3
3
  import { findAll } from "../../../util/host-rules.js";
4
- import { deleteLocalFile, readLocalFile, writeLocalFile } from "../../../util/fs/index.js";
4
+ import { readLocalFile } from "../../../util/fs/index.js";
5
5
  import { exec } from "../../../util/exec/index.js";
6
- import { artifactError, artifactErrorResult, fileAddition, resolveToolConstraint } from "../util.js";
6
+ import { artifactError, artifactErrorResult, resolveToolConstraint, updateLockFile } from "../util.js";
7
7
  import { processHostRules } from "../npm/post-update/rules.js";
8
- import { getNpmrcContent, resetNpmrcContent, updateNpmrcContent } from "../npm/utils.js";
8
+ import { withNpmrcHostRules } from "../npm/utils.js";
9
9
  import { isEmptyArray } from "@sindresorhus/is";
10
10
  import { quote } from "shlex";
11
11
  import upath from "upath";
@@ -13,7 +13,7 @@ import upath from "upath";
13
13
  async function updateArtifacts(updateArtifact) {
14
14
  const { packageFileName, updatedDeps, newPackageFileContent, config } = updateArtifact;
15
15
  logger.debug(`deno.updateArtifacts(${packageFileName})`);
16
- const isLockFileMaintenance = config.updateType === "lockFileMaintenance";
16
+ const { isLockFileMaintenance } = config;
17
17
  if (isEmptyArray(updatedDeps) && !isLockFileMaintenance) {
18
18
  logger.debug("No updated deno deps - returning null");
19
19
  return null;
@@ -38,38 +38,41 @@ async function updateArtifacts(updateArtifact) {
38
38
  }
39
39
  const pkgFileDir = upath.dirname(packageFileName);
40
40
  const { additionalNpmrcContent } = processHostRules();
41
- const npmrcContent = await getNpmrcContent(pkgFileDir);
42
- await updateNpmrcContent(pkgFileDir, npmrcContent, additionalNpmrcContent);
43
41
  try {
44
- await writeLocalFile(packageFileName, newPackageFileContent);
45
- if (isLockFileMaintenance) await deleteLocalFile(lockFileName);
46
- const execOptions = {
47
- cwdFile: updatedDeps.find((dep) => dep.managerData?.importMapReferrer)?.managerData?.importMapReferrer ?? packageFileName,
48
- docker: {},
49
- toolConstraints: [{
50
- toolName: "deno",
51
- constraint: await resolveToolConstraint(config, "deno")
52
- }]
53
- };
54
- let command = "deno install --frozen=false";
55
- const defaultImportHosts = [
56
- "deno.land:443",
57
- "esm.sh:443",
58
- "jsr.io:443",
59
- "cdn.jsdelivr.net:443",
60
- "raw.githubusercontent.com:443",
61
- "gist.githubusercontent.com:443"
62
- ];
63
- const additionalImportHosts = findAll({ hostType: "npm" }).filter((rule) => rule.resolvedHost).map((rule) => rule.resolvedHost);
64
- if (additionalImportHosts.length > 0) {
65
- const importHosts = [.../* @__PURE__ */ new Set([...defaultImportHosts, ...additionalImportHosts])].join(",");
66
- command += ` --allow-import=${quote(importHosts)}`;
67
- }
68
- await exec(command, execOptions);
69
- await resetNpmrcContent(pkgFileDir, npmrcContent);
70
- const newLockFileContent = await readLocalFile(lockFileName);
71
- if (!newLockFileContent || Buffer.compare(oldLockFileContent, newLockFileContent) === 0) return null;
72
- return [fileAddition(lockFileName, newLockFileContent)];
42
+ return await withNpmrcHostRules(pkgFileDir, additionalNpmrcContent, async () => {
43
+ const execOptions = {
44
+ cwdFile: updatedDeps.find((dep) => dep.managerData?.importMapReferrer)?.managerData?.importMapReferrer ?? packageFileName,
45
+ docker: {},
46
+ toolConstraints: [{
47
+ toolName: "deno",
48
+ constraint: await resolveToolConstraint(config, "deno")
49
+ }]
50
+ };
51
+ let command = "deno install --frozen=false";
52
+ const defaultImportHosts = [
53
+ "deno.land:443",
54
+ "esm.sh:443",
55
+ "jsr.io:443",
56
+ "cdn.jsdelivr.net:443",
57
+ "raw.githubusercontent.com:443",
58
+ "gist.githubusercontent.com:443"
59
+ ];
60
+ const additionalImportHosts = findAll({ hostType: "npm" }).filter((rule) => rule.resolvedHost).map((rule) => rule.resolvedHost);
61
+ if (additionalImportHosts.length > 0) {
62
+ const importHosts = [.../* @__PURE__ */ new Set([...defaultImportHosts, ...additionalImportHosts])].join(",");
63
+ command += ` --allow-import=${quote(importHosts)}`;
64
+ }
65
+ return await updateLockFile({
66
+ lockFileName,
67
+ existingLockFileContent: oldLockFileContent,
68
+ packageFile: {
69
+ path: packageFileName,
70
+ contents: newPackageFileContent
71
+ },
72
+ deleteLockFile: isLockFileMaintenance,
73
+ run: () => exec(command, execOptions)
74
+ });
75
+ });
73
76
  } catch (err) {
74
77
  if (err.message === "temporary-error") throw err;
75
78
  logger.warn({
@@ -1 +1 @@
1
- {"version":3,"file":"artifacts.js","names":["hostRules\n .findAll"],"sources":["../../../../lib/modules/manager/deno/artifacts.ts"],"sourcesContent":["import { isEmptyArray } from '@sindresorhus/is';\nimport { quote } from 'shlex';\nimport upath from 'upath';\nimport { TEMPORARY_ERROR } from '../../../constants/error-messages.ts';\nimport { logger } from '../../../logger/index.ts';\nimport { exec } from '../../../util/exec/index.ts';\nimport type { ExecOptions } from '../../../util/exec/types.ts';\nimport {\n deleteLocalFile,\n readLocalFile,\n writeLocalFile,\n} from '../../../util/fs/index.ts';\nimport * as hostRules from '../../../util/host-rules.ts';\nimport { processHostRules } from '../npm/post-update/rules.ts';\nimport {\n getNpmrcContent,\n resetNpmrcContent,\n updateNpmrcContent,\n} from '../npm/utils.ts';\nimport type { UpdateArtifact, UpdateArtifactsResult } from '../types.ts';\nimport {\n artifactError,\n artifactErrorResult,\n fileAddition,\n resolveToolConstraint,\n} from '../util.ts';\nimport type { DenoManagerData } from './types.ts';\n\nexport async function updateArtifacts(\n updateArtifact: UpdateArtifact<DenoManagerData>,\n): Promise<UpdateArtifactsResult[] | null> {\n const { packageFileName, updatedDeps, newPackageFileContent, config } =\n updateArtifact;\n logger.debug(`deno.updateArtifacts(${packageFileName})`);\n const isLockFileMaintenance = config.updateType === 'lockFileMaintenance';\n\n if (isEmptyArray(updatedDeps) && !isLockFileMaintenance) {\n logger.debug('No updated deno deps - returning null');\n return null;\n }\n\n // falling back for lockFileMaintenance\n const lockFileName = updatedDeps[0]?.lockFiles?.[0] ?? config.lockFiles?.[0];\n\n if (!lockFileName) {\n logger.debug('No lock file found. Skipping artifact update.');\n return null;\n }\n\n const oldLockFileContent = await readLocalFile(lockFileName);\n if (!oldLockFileContent) {\n logger.debug(`Failed to read ${lockFileName}. Skipping artifact update.`);\n return [artifactError(lockFileName, `Failed to read \"${lockFileName}\"`)];\n }\n\n for (const updateDep of updatedDeps) {\n if (\n updateDep.depType === 'tasks' ||\n updateDep.depType === 'tasks.command'\n ) {\n logger.warn(\n {\n depType: updateDep.depType,\n depName: updateDep.depName,\n lockFileName,\n },\n \"Dependency can't be updated with a lock file\",\n );\n return [\n artifactError(\n lockFileName,\n `depType: \"${updateDep.depType}\", depName: \"${updateDep.depName}\" can't be updated with a lock file: \"${lockFileName}\"`,\n ),\n ];\n }\n }\n\n const pkgFileDir = upath.dirname(packageFileName);\n const { additionalNpmrcContent } = processHostRules();\n const npmrcContent = await getNpmrcContent(pkgFileDir);\n await updateNpmrcContent(pkgFileDir, npmrcContent, additionalNpmrcContent);\n\n try {\n await writeLocalFile(packageFileName, newPackageFileContent);\n\n if (isLockFileMaintenance) {\n await deleteLocalFile(lockFileName);\n }\n\n // run from its referred deno.json/deno.jsonc location if import map is used\n const importMapReferrerDep = updatedDeps.find(\n (dep) => dep.managerData?.importMapReferrer,\n );\n const cwdFile =\n importMapReferrerDep?.managerData?.importMapReferrer ?? packageFileName;\n\n const execOptions: ExecOptions = {\n cwdFile,\n docker: {},\n toolConstraints: [\n {\n toolName: 'deno',\n constraint: await resolveToolConstraint(config, 'deno'),\n },\n ],\n };\n\n // \"deno install\" don't execute lifecycle scripts of package.json by default\n // https://docs.deno.com/runtime/reference/cli/install/#native-node.js-addons\n // deno.json(c) could have the `lock.frozen` field\n // we should always override the `frozen` flag due to if it would be specified true\n let command = 'deno install --frozen=false';\n\n // defaults as per https://docs.deno.com/runtime/fundamentals/security/#importing-from-the-web\n const defaultImportHosts = [\n 'deno.land:443',\n 'esm.sh:443',\n 'jsr.io:443',\n 'cdn.jsdelivr.net:443',\n 'raw.githubusercontent.com:443',\n 'gist.githubusercontent.com:443',\n ];\n const additionalImportHosts = hostRules\n .findAll({ hostType: 'npm' })\n .filter((rule) => rule.resolvedHost)\n .map((rule) => rule.resolvedHost);\n\n if (additionalImportHosts.length > 0) {\n // combine default and additional import hosts, removing duplicates\n const importHosts = [\n ...new Set([...defaultImportHosts, ...additionalImportHosts]),\n ].join(',');\n\n command += ` --allow-import=${quote(importHosts)}`;\n }\n\n // TODO: appending `--lockfile-only` is better to reduce disk usage\n // https://docs.deno.com/runtime/reference/cli/install/#options-lockfile-only\n await exec(command, execOptions);\n await resetNpmrcContent(pkgFileDir, npmrcContent);\n\n const newLockFileContent = await readLocalFile(lockFileName);\n if (\n !newLockFileContent ||\n Buffer.compare(oldLockFileContent, newLockFileContent) === 0\n ) {\n return null;\n }\n\n return [fileAddition(lockFileName, newLockFileContent)];\n } catch (err) {\n if (err.message === TEMPORARY_ERROR) {\n throw err;\n }\n logger.warn({ lockfile: lockFileName, err }, `Failed to update lock file`);\n return artifactErrorResult(lockFileName, err);\n }\n}\n"],"mappings":";;;;;;;;;;;;AA4BA,eAAsB,gBACpB,gBACyC;CACzC,MAAM,EAAE,iBAAiB,aAAa,uBAAuB,WAC3D;CACF,OAAO,MAAM,wBAAwB,gBAAgB,EAAE;CACvD,MAAM,wBAAwB,OAAO,eAAe;CAEpD,IAAI,aAAa,WAAW,KAAK,CAAC,uBAAuB;EACvD,OAAO,MAAM,uCAAuC;EACpD,OAAO;CACT;CAGA,MAAM,eAAe,YAAY,EAAE,EAAE,YAAY,MAAM,OAAO,YAAY;CAE1E,IAAI,CAAC,cAAc;EACjB,OAAO,MAAM,+CAA+C;EAC5D,OAAO;CACT;CAEA,MAAM,qBAAqB,MAAM,cAAc,YAAY;CAC3D,IAAI,CAAC,oBAAoB;EACvB,OAAO,MAAM,kBAAkB,aAAa,4BAA4B;EACxE,OAAO,CAAC,cAAc,cAAc,mBAAmB,aAAa,EAAE,CAAC;CACzE;CAEA,KAAK,MAAM,aAAa,aACtB,IACE,UAAU,YAAY,WACtB,UAAU,YAAY,iBACtB;EACA,OAAO,KACL;GACE,SAAS,UAAU;GACnB,SAAS,UAAU;GACnB;EACF,GACA,8CACF;EACA,OAAO,CACL,cACE,cACA,aAAa,UAAU,QAAQ,eAAe,UAAU,QAAQ,wCAAwC,aAAa,EACvH,CACF;CACF;CAGF,MAAM,aAAa,MAAM,QAAQ,eAAe;CAChD,MAAM,EAAE,2BAA2B,iBAAiB;CACpD,MAAM,eAAe,MAAM,gBAAgB,UAAU;CACrD,MAAM,mBAAmB,YAAY,cAAc,sBAAsB;CAEzE,IAAI;EACF,MAAM,eAAe,iBAAiB,qBAAqB;EAE3D,IAAI,uBACF,MAAM,gBAAgB,YAAY;EAUpC,MAAM,cAA2B;GAC/B,SAP2B,YAAY,MACtC,QAAQ,IAAI,aAAa,iBAGP,CAAC,EAAE,aAAa,qBAAqB;GAIxD,QAAQ,CAAC;GACT,iBAAiB,CACf;IACE,UAAU;IACV,YAAY,MAAM,sBAAsB,QAAQ,MAAM;GACxD,CACF;EACF;EAMA,IAAI,UAAU;EAGd,MAAM,qBAAqB;GACzB;GACA;GACA;GACA;GACA;GACA;EACF;EACA,MAAM,wBAAwBA,QACnB,EAAE,UAAU,MAAM,CAAC,CAAC,CAC5B,QAAQ,SAAS,KAAK,YAAY,CAAC,CACnC,KAAK,SAAS,KAAK,YAAY;EAElC,IAAI,sBAAsB,SAAS,GAAG;GAEpC,MAAM,cAAc,CAClB,mBAAG,IAAI,IAAI,CAAC,GAAG,oBAAoB,GAAG,qBAAqB,CAAC,CAC9D,CAAC,CAAC,KAAK,GAAG;GAEV,WAAW,mBAAmB,MAAM,WAAW;EACjD;EAIA,MAAM,KAAK,SAAS,WAAW;EAC/B,MAAM,kBAAkB,YAAY,YAAY;EAEhD,MAAM,qBAAqB,MAAM,cAAc,YAAY;EAC3D,IACE,CAAC,sBACD,OAAO,QAAQ,oBAAoB,kBAAkB,MAAM,GAE3D,OAAO;EAGT,OAAO,CAAC,aAAa,cAAc,kBAAkB,CAAC;CACxD,SAAS,KAAK;EACZ,IAAI,IAAI,YAAA,mBACN,MAAM;EAER,OAAO,KAAK;GAAE,UAAU;GAAc;EAAI,GAAG,4BAA4B;EACzE,OAAO,oBAAoB,cAAc,GAAG;CAC9C;AACF"}
1
+ {"version":3,"file":"artifacts.js","names":["hostRules\n .findAll"],"sources":["../../../../lib/modules/manager/deno/artifacts.ts"],"sourcesContent":["import { isEmptyArray } from '@sindresorhus/is';\nimport { quote } from 'shlex';\nimport upath from 'upath';\nimport { TEMPORARY_ERROR } from '../../../constants/error-messages.ts';\nimport { logger } from '../../../logger/index.ts';\nimport { exec } from '../../../util/exec/index.ts';\nimport type { ExecOptions } from '../../../util/exec/types.ts';\nimport { readLocalFile } from '../../../util/fs/index.ts';\nimport * as hostRules from '../../../util/host-rules.ts';\nimport { processHostRules } from '../npm/post-update/rules.ts';\nimport { withNpmrcHostRules } from '../npm/utils.ts';\nimport type { UpdateArtifact, UpdateArtifactsResult } from '../types.ts';\nimport {\n artifactError,\n artifactErrorResult,\n resolveToolConstraint,\n updateLockFile,\n} from '../util.ts';\nimport type { DenoManagerData } from './types.ts';\n\nexport async function updateArtifacts(\n updateArtifact: UpdateArtifact<DenoManagerData>,\n): Promise<UpdateArtifactsResult[] | null> {\n const { packageFileName, updatedDeps, newPackageFileContent, config } =\n updateArtifact;\n logger.debug(`deno.updateArtifacts(${packageFileName})`);\n const { isLockFileMaintenance } = config;\n\n if (isEmptyArray(updatedDeps) && !isLockFileMaintenance) {\n logger.debug('No updated deno deps - returning null');\n return null;\n }\n\n // falling back for lockFileMaintenance\n const lockFileName = updatedDeps[0]?.lockFiles?.[0] ?? config.lockFiles?.[0];\n\n if (!lockFileName) {\n logger.debug('No lock file found. Skipping artifact update.');\n return null;\n }\n\n const oldLockFileContent = await readLocalFile(lockFileName);\n if (!oldLockFileContent) {\n logger.debug(`Failed to read ${lockFileName}. Skipping artifact update.`);\n return [artifactError(lockFileName, `Failed to read \"${lockFileName}\"`)];\n }\n\n for (const updateDep of updatedDeps) {\n if (\n updateDep.depType === 'tasks' ||\n updateDep.depType === 'tasks.command'\n ) {\n logger.warn(\n {\n depType: updateDep.depType,\n depName: updateDep.depName,\n lockFileName,\n },\n \"Dependency can't be updated with a lock file\",\n );\n return [\n artifactError(\n lockFileName,\n `depType: \"${updateDep.depType}\", depName: \"${updateDep.depName}\" can't be updated with a lock file: \"${lockFileName}\"`,\n ),\n ];\n }\n }\n\n const pkgFileDir = upath.dirname(packageFileName);\n const { additionalNpmrcContent } = processHostRules();\n\n try {\n return await withNpmrcHostRules(\n pkgFileDir,\n additionalNpmrcContent,\n async () => {\n // run from its referred deno.json/deno.jsonc location if import map is used\n const importMapReferrerDep = updatedDeps.find(\n (dep) => dep.managerData?.importMapReferrer,\n );\n const cwdFile =\n importMapReferrerDep?.managerData?.importMapReferrer ??\n packageFileName;\n\n const execOptions: ExecOptions = {\n cwdFile,\n docker: {},\n toolConstraints: [\n {\n toolName: 'deno',\n constraint: await resolveToolConstraint(config, 'deno'),\n },\n ],\n };\n\n // \"deno install\" don't execute lifecycle scripts of package.json by default\n // https://docs.deno.com/runtime/reference/cli/install/#native-node.js-addons\n // deno.json(c) could have the `lock.frozen` field\n // we should always override the `frozen` flag due to if it would be specified true\n let command = 'deno install --frozen=false';\n\n // defaults as per https://docs.deno.com/runtime/fundamentals/security/#importing-from-the-web\n const defaultImportHosts = [\n 'deno.land:443',\n 'esm.sh:443',\n 'jsr.io:443',\n 'cdn.jsdelivr.net:443',\n 'raw.githubusercontent.com:443',\n 'gist.githubusercontent.com:443',\n ];\n const additionalImportHosts = hostRules\n .findAll({ hostType: 'npm' })\n .filter((rule) => rule.resolvedHost)\n .map((rule) => rule.resolvedHost);\n\n if (additionalImportHosts.length > 0) {\n // combine default and additional import hosts, removing duplicates\n const importHosts = [\n ...new Set([...defaultImportHosts, ...additionalImportHosts]),\n ].join(',');\n\n command += ` --allow-import=${quote(importHosts)}`;\n }\n\n // TODO: appending `--lockfile-only` is better to reduce disk usage\n // https://docs.deno.com/runtime/reference/cli/install/#options-lockfile-only\n return await updateLockFile({\n lockFileName,\n existingLockFileContent: oldLockFileContent,\n packageFile: {\n path: packageFileName,\n contents: newPackageFileContent,\n },\n deleteLockFile: isLockFileMaintenance,\n run: () => exec(command, execOptions),\n });\n },\n );\n } catch (err) {\n if (err.message === TEMPORARY_ERROR) {\n throw err;\n }\n logger.warn({ lockfile: lockFileName, err }, `Failed to update lock file`);\n return artifactErrorResult(lockFileName, err);\n }\n}\n"],"mappings":";;;;;;;;;;;;AAoBA,eAAsB,gBACpB,gBACyC;CACzC,MAAM,EAAE,iBAAiB,aAAa,uBAAuB,WAC3D;CACF,OAAO,MAAM,wBAAwB,gBAAgB,EAAE;CACvD,MAAM,EAAE,0BAA0B;CAElC,IAAI,aAAa,WAAW,KAAK,CAAC,uBAAuB;EACvD,OAAO,MAAM,uCAAuC;EACpD,OAAO;CACT;CAGA,MAAM,eAAe,YAAY,EAAE,EAAE,YAAY,MAAM,OAAO,YAAY;CAE1E,IAAI,CAAC,cAAc;EACjB,OAAO,MAAM,+CAA+C;EAC5D,OAAO;CACT;CAEA,MAAM,qBAAqB,MAAM,cAAc,YAAY;CAC3D,IAAI,CAAC,oBAAoB;EACvB,OAAO,MAAM,kBAAkB,aAAa,4BAA4B;EACxE,OAAO,CAAC,cAAc,cAAc,mBAAmB,aAAa,EAAE,CAAC;CACzE;CAEA,KAAK,MAAM,aAAa,aACtB,IACE,UAAU,YAAY,WACtB,UAAU,YAAY,iBACtB;EACA,OAAO,KACL;GACE,SAAS,UAAU;GACnB,SAAS,UAAU;GACnB;EACF,GACA,8CACF;EACA,OAAO,CACL,cACE,cACA,aAAa,UAAU,QAAQ,eAAe,UAAU,QAAQ,wCAAwC,aAAa,EACvH,CACF;CACF;CAGF,MAAM,aAAa,MAAM,QAAQ,eAAe;CAChD,MAAM,EAAE,2BAA2B,iBAAiB;CAEpD,IAAI;EACF,OAAO,MAAM,mBACX,YACA,wBACA,YAAY;GASV,MAAM,cAA2B;IAC/B,SAR2B,YAAY,MACtC,QAAQ,IAAI,aAAa,iBAGP,CAAC,EAAE,aAAa,qBACnC;IAIA,QAAQ,CAAC;IACT,iBAAiB,CACf;KACE,UAAU;KACV,YAAY,MAAM,sBAAsB,QAAQ,MAAM;IACxD,CACF;GACF;GAMA,IAAI,UAAU;GAGd,MAAM,qBAAqB;IACzB;IACA;IACA;IACA;IACA;IACA;GACF;GACA,MAAM,wBAAwBA,QACnB,EAAE,UAAU,MAAM,CAAC,CAAC,CAC5B,QAAQ,SAAS,KAAK,YAAY,CAAC,CACnC,KAAK,SAAS,KAAK,YAAY;GAElC,IAAI,sBAAsB,SAAS,GAAG;IAEpC,MAAM,cAAc,CAClB,mBAAG,IAAI,IAAI,CAAC,GAAG,oBAAoB,GAAG,qBAAqB,CAAC,CAC9D,CAAC,CAAC,KAAK,GAAG;IAEV,WAAW,mBAAmB,MAAM,WAAW;GACjD;GAIA,OAAO,MAAM,eAAe;IAC1B;IACA,yBAAyB;IACzB,aAAa;KACX,MAAM;KACN,UAAU;IACZ;IACA,gBAAgB;IAChB,WAAW,KAAK,SAAS,WAAW;GACtC,CAAC;EACH,CACF;CACF,SAAS,KAAK;EACZ,IAAI,IAAI,YAAA,mBACN,MAAM;EAER,OAAO,KAAK;GAAE,UAAU;GAAc;EAAI,GAAG,4BAA4B;EACzE,OAAO,oBAAoB,cAAc,GAAG;CAC9C;AACF"}
@@ -1,7 +1,7 @@
1
1
  import { logger } from "../../../logger/index.js";
2
2
  import { getSiblingFileName, readLocalFile } from "../../../util/fs/index.js";
3
3
  import { exec } from "../../../util/exec/index.js";
4
- import { artifactErrorResult, fileAddition, resolveToolConstraint } from "../util.js";
4
+ import { artifactErrorResult, resolveToolConstraint, updateLockFile } from "../util.js";
5
5
  import { isNonEmptyArray } from "@sindresorhus/is";
6
6
  import { quote } from "shlex";
7
7
  import semver from "semver";
@@ -47,11 +47,11 @@ async function updateArtifacts({ config, packageFileName, updatedDeps }) {
47
47
  return null;
48
48
  }
49
49
  try {
50
- await exec(cmd, execOptions);
51
- const newLockFileContent = await readLocalFile(lockFileName);
52
- if (!newLockFileContent || Buffer.compare(oldLockFileContent, newLockFileContent) === 0) return null;
53
- logger.trace("Returning updated devbox.lock");
54
- return [fileAddition(lockFileName, newLockFileContent)];
50
+ return await updateLockFile({
51
+ lockFileName,
52
+ existingLockFileContent: oldLockFileContent,
53
+ run: () => exec(cmd, execOptions)
54
+ });
55
55
  } catch (err) {
56
56
  logger.warn({ err }, "Error updating devbox.lock");
57
57
  return artifactErrorResult(lockFileName, err);
@@ -1 +1 @@
1
- {"version":3,"file":"artifacts.js","names":[],"sources":["../../../../lib/modules/manager/devbox/artifacts.ts"],"sourcesContent":["import { isNonEmptyArray } from '@sindresorhus/is';\nimport semver from 'semver';\nimport { quote } from 'shlex';\nimport { logger } from '../../../logger/index.ts';\nimport { exec } from '../../../util/exec/index.ts';\nimport type { ExecOptions } from '../../../util/exec/types.ts';\nimport { getSiblingFileName, readLocalFile } from '../../../util/fs/index.ts';\nimport type { UpdateArtifact, UpdateArtifactsResult } from '../types.ts';\nimport {\n artifactErrorResult,\n fileAddition,\n resolveToolConstraint,\n} from '../util.ts';\n\nexport async function updateArtifacts({\n config,\n packageFileName,\n updatedDeps,\n}: UpdateArtifact): Promise<UpdateArtifactsResult[] | null> {\n const lockFileName = getSiblingFileName(packageFileName, 'devbox.lock');\n const existingLockFileContent = await readLocalFile(lockFileName, 'utf8');\n if (!existingLockFileContent) {\n logger.debug('No devbox.lock found');\n return null;\n }\n\n const nixConstraint = await resolveToolConstraint(config, 'nix');\n const devboxConstraint = await resolveToolConstraint(config, 'devbox');\n\n const supportsNoInstall = devboxConstraint\n ? semver.intersects(devboxConstraint, '>=0.14.0')\n : true;\n\n const execOptions: ExecOptions = {\n cwdFile: packageFileName,\n toolConstraints: [\n // we are required to install nix because devbox spawns nix commands internally\n // https://github.com/renovatebot/renovate/discussions/35382\n // https://github.com/jetify-com/devbox/issues/2585\n {\n toolName: 'nix',\n constraint: nixConstraint,\n },\n {\n toolName: 'devbox',\n constraint: devboxConstraint,\n },\n ],\n docker: {},\n };\n\n const cmd = [];\n if (config.isLockFileMaintenance) {\n cmd.push(\n supportsNoInstall ? 'devbox update --no-install' : 'devbox update',\n );\n } else if (isNonEmptyArray(updatedDeps)) {\n if (supportsNoInstall) {\n const updateCommands: string[] = updatedDeps\n .map(\n (dep) =>\n dep.depName && `devbox update ${quote(dep.depName)} --no-install`,\n )\n .filter((dep): dep is string => Boolean(dep));\n if (updateCommands.length) {\n cmd.push(...updateCommands);\n } else {\n logger.trace('No updated devbox packages - returning null');\n return null;\n }\n } else {\n cmd.push('devbox install');\n }\n } else {\n logger.trace('No updated devbox packages - returning null');\n return null;\n }\n\n const oldLockFileContent = await readLocalFile(lockFileName);\n if (!oldLockFileContent) {\n logger.trace(`No ${lockFileName} found`);\n return null;\n }\n\n try {\n await exec(cmd, execOptions);\n const newLockFileContent = await readLocalFile(lockFileName);\n\n if (\n !newLockFileContent ||\n Buffer.compare(oldLockFileContent, newLockFileContent) === 0\n ) {\n return null;\n }\n logger.trace('Returning updated devbox.lock');\n return [fileAddition(lockFileName, newLockFileContent)];\n } catch (err) {\n logger.warn({ err }, 'Error updating devbox.lock');\n return artifactErrorResult(lockFileName, err);\n }\n}\n"],"mappings":";;;;;;;;AAcA,eAAsB,gBAAgB,EACpC,QACA,iBACA,eAC0D;CAC1D,MAAM,eAAe,mBAAmB,iBAAiB,aAAa;CAEtE,IAAI,CAAC,MADiC,cAAc,cAAc,MAAM,GAC1C;EAC5B,OAAO,MAAM,sBAAsB;EACnC,OAAO;CACT;CAEA,MAAM,gBAAgB,MAAM,sBAAsB,QAAQ,KAAK;CAC/D,MAAM,mBAAmB,MAAM,sBAAsB,QAAQ,QAAQ;CAErE,MAAM,oBAAoB,mBACtB,OAAO,WAAW,kBAAkB,UAAU,IAC9C;CAEJ,MAAM,cAA2B;EAC/B,SAAS;EACT,iBAAiB,CAIf;GACE,UAAU;GACV,YAAY;EACd,GACA;GACE,UAAU;GACV,YAAY;EACd,CACF;EACA,QAAQ,CAAC;CACX;CAEA,MAAM,MAAM,CAAC;CACb,IAAI,OAAO,uBACT,IAAI,KACF,oBAAoB,+BAA+B,eACrD;MACK,IAAI,gBAAgB,WAAW,GAAG;EACvC,IAAI,mBAAmB;GACrB,MAAM,iBAA2B,YAC9B,KACE,QACC,IAAI,WAAW,iBAAiB,MAAM,IAAI,OAAO,EAAE,cACvD,CAAC,CACA,QAAQ,QAAuB,QAAQ,GAAG,CAAC;GAC9C,IAAI,eAAe,QACjB,IAAI,KAAK,GAAG,cAAc;QACrB;IACL,OAAO,MAAM,6CAA6C;IAC1D,OAAO;GACT;EACF,OACE,IAAI,KAAK,gBAAgB;CAE7B,OAAO;EACL,OAAO,MAAM,6CAA6C;EAC1D,OAAO;CACT;CAEA,MAAM,qBAAqB,MAAM,cAAc,YAAY;CAC3D,IAAI,CAAC,oBAAoB;EACvB,OAAO,MAAM,MAAM,aAAa,OAAO;EACvC,OAAO;CACT;CAEA,IAAI;EACF,MAAM,KAAK,KAAK,WAAW;EAC3B,MAAM,qBAAqB,MAAM,cAAc,YAAY;EAE3D,IACE,CAAC,sBACD,OAAO,QAAQ,oBAAoB,kBAAkB,MAAM,GAE3D,OAAO;EAET,OAAO,MAAM,+BAA+B;EAC5C,OAAO,CAAC,aAAa,cAAc,kBAAkB,CAAC;CACxD,SAAS,KAAK;EACZ,OAAO,KAAK,EAAE,IAAI,GAAG,4BAA4B;EACjD,OAAO,oBAAoB,cAAc,GAAG;CAC9C;AACF"}
1
+ {"version":3,"file":"artifacts.js","names":[],"sources":["../../../../lib/modules/manager/devbox/artifacts.ts"],"sourcesContent":["import { isNonEmptyArray } from '@sindresorhus/is';\nimport semver from 'semver';\nimport { quote } from 'shlex';\nimport { logger } from '../../../logger/index.ts';\nimport { exec } from '../../../util/exec/index.ts';\nimport type { ExecOptions } from '../../../util/exec/types.ts';\nimport { getSiblingFileName, readLocalFile } from '../../../util/fs/index.ts';\nimport type { UpdateArtifact, UpdateArtifactsResult } from '../types.ts';\nimport {\n artifactErrorResult,\n resolveToolConstraint,\n updateLockFile,\n} from '../util.ts';\n\nexport async function updateArtifacts({\n config,\n packageFileName,\n updatedDeps,\n}: UpdateArtifact): Promise<UpdateArtifactsResult[] | null> {\n const lockFileName = getSiblingFileName(packageFileName, 'devbox.lock');\n const existingLockFileContent = await readLocalFile(lockFileName, 'utf8');\n if (!existingLockFileContent) {\n logger.debug('No devbox.lock found');\n return null;\n }\n\n const nixConstraint = await resolveToolConstraint(config, 'nix');\n const devboxConstraint = await resolveToolConstraint(config, 'devbox');\n\n const supportsNoInstall = devboxConstraint\n ? semver.intersects(devboxConstraint, '>=0.14.0')\n : true;\n\n const execOptions: ExecOptions = {\n cwdFile: packageFileName,\n toolConstraints: [\n // we are required to install nix because devbox spawns nix commands internally\n // https://github.com/renovatebot/renovate/discussions/35382\n // https://github.com/jetify-com/devbox/issues/2585\n {\n toolName: 'nix',\n constraint: nixConstraint,\n },\n {\n toolName: 'devbox',\n constraint: devboxConstraint,\n },\n ],\n docker: {},\n };\n\n const cmd: string[] = [];\n if (config.isLockFileMaintenance) {\n cmd.push(\n supportsNoInstall ? 'devbox update --no-install' : 'devbox update',\n );\n } else if (isNonEmptyArray(updatedDeps)) {\n if (supportsNoInstall) {\n const updateCommands: string[] = updatedDeps\n .map(\n (dep) =>\n dep.depName && `devbox update ${quote(dep.depName)} --no-install`,\n )\n .filter((dep): dep is string => Boolean(dep));\n if (updateCommands.length) {\n cmd.push(...updateCommands);\n } else {\n logger.trace('No updated devbox packages - returning null');\n return null;\n }\n } else {\n cmd.push('devbox install');\n }\n } else {\n logger.trace('No updated devbox packages - returning null');\n return null;\n }\n\n const oldLockFileContent = await readLocalFile(lockFileName);\n if (!oldLockFileContent) {\n logger.trace(`No ${lockFileName} found`);\n return null;\n }\n\n try {\n return await updateLockFile({\n lockFileName,\n existingLockFileContent: oldLockFileContent,\n run: () => exec(cmd, execOptions),\n });\n } catch (err) {\n logger.warn({ err }, 'Error updating devbox.lock');\n return artifactErrorResult(lockFileName, err);\n }\n}\n"],"mappings":";;;;;;;;AAcA,eAAsB,gBAAgB,EACpC,QACA,iBACA,eAC0D;CAC1D,MAAM,eAAe,mBAAmB,iBAAiB,aAAa;CAEtE,IAAI,CAAC,MADiC,cAAc,cAAc,MAAM,GAC1C;EAC5B,OAAO,MAAM,sBAAsB;EACnC,OAAO;CACT;CAEA,MAAM,gBAAgB,MAAM,sBAAsB,QAAQ,KAAK;CAC/D,MAAM,mBAAmB,MAAM,sBAAsB,QAAQ,QAAQ;CAErE,MAAM,oBAAoB,mBACtB,OAAO,WAAW,kBAAkB,UAAU,IAC9C;CAEJ,MAAM,cAA2B;EAC/B,SAAS;EACT,iBAAiB,CAIf;GACE,UAAU;GACV,YAAY;EACd,GACA;GACE,UAAU;GACV,YAAY;EACd,CACF;EACA,QAAQ,CAAC;CACX;CAEA,MAAM,MAAgB,CAAC;CACvB,IAAI,OAAO,uBACT,IAAI,KACF,oBAAoB,+BAA+B,eACrD;MACK,IAAI,gBAAgB,WAAW,GAAG;EACvC,IAAI,mBAAmB;GACrB,MAAM,iBAA2B,YAC9B,KACE,QACC,IAAI,WAAW,iBAAiB,MAAM,IAAI,OAAO,EAAE,cACvD,CAAC,CACA,QAAQ,QAAuB,QAAQ,GAAG,CAAC;GAC9C,IAAI,eAAe,QACjB,IAAI,KAAK,GAAG,cAAc;QACrB;IACL,OAAO,MAAM,6CAA6C;IAC1D,OAAO;GACT;EACF,OACE,IAAI,KAAK,gBAAgB;CAE7B,OAAO;EACL,OAAO,MAAM,6CAA6C;EAC1D,OAAO;CACT;CAEA,MAAM,qBAAqB,MAAM,cAAc,YAAY;CAC3D,IAAI,CAAC,oBAAoB;EACvB,OAAO,MAAM,MAAM,aAAa,OAAO;EACvC,OAAO;CACT;CAEA,IAAI;EACF,OAAO,MAAM,eAAe;GAC1B;GACA,yBAAyB;GACzB,WAAW,KAAK,KAAK,WAAW;EAClC,CAAC;CACH,SAAS,KAAK;EACZ,OAAO,KAAK,EAAE,IAAI,GAAG,4BAA4B;EACjD,OAAO,oBAAoB,cAAc,GAAG;CAC9C;AACF"}
@@ -1,21 +1,9 @@
1
- import { newlineRegex, regEx } from "../../../util/regex.js";
1
+ import { regEx } from "../../../util/regex.js";
2
2
  import { logger } from "../../../logger/index.js";
3
3
  import { api } from "../../versioning/apk/index.js";
4
4
  import { ApkDatasource } from "../../datasource/apk/index.js";
5
- import { split } from "shlex";
5
+ import { parseRunCommands } from "./run-command.js";
6
6
  //#region lib/modules/manager/dockerfile/apk.ts
7
- /** Shell operators which start a new command */
8
- const commandSeparators = [
9
- "&&",
10
- "||",
11
- ";",
12
- "|",
13
- "&",
14
- "(",
15
- ")",
16
- "{",
17
- "}"
18
- ];
19
7
  /**
20
8
  * `apk` options which consume the following argument, so that the argument is
21
9
  * not mistaken for a package name.
@@ -107,12 +95,6 @@ function extractApkAddArgs(tokens) {
107
95
  return deps;
108
96
  }
109
97
  /**
110
- * The `RUN` keyword and its flags, e.g. `RUN --mount=type=cache,target=/x `.
111
- *
112
- * Stripping these leaves the shell command that the instruction runs.
113
- */
114
- const runPrefixRegex = regEx(/^[ \t]*(?:ONBUILD[ \t]+)?RUN[ \t]+(?:--[a-z]\S*[ \t]+)*/i);
115
- /**
116
98
  * Extracts APK packages pinned by `apk add` in a `RUN` instruction, e.g.
117
99
  *
118
100
  * ```dockerfile
@@ -125,51 +107,7 @@ const runPrefixRegex = regEx(/^[ \t]*(?:ONBUILD[ \t]+)?RUN[ \t]+(?:--[a-z]\S*[ \
125
107
  * @param escapeChar the Dockerfile escape character, already regex-escaped
126
108
  */
127
109
  function extractApkDeps(instruction, escapeChar) {
128
- const joined = instruction.split(newlineRegex).filter((line) => !regEx(/^[ \t]*#/).test(line)).join("\n").replace(regEx(`${escapeChar}[ \\t]*\\r?\\n`, "g"), " ");
129
- const runPrefix = runPrefixRegex.exec(joined)?.[0];
130
- if (!runPrefix) return [];
131
- const command = joined.slice(runPrefix.length);
132
- if (!command.includes("apk")) return [];
133
- let tokens;
134
- try {
135
- tokens = split(command);
136
- } catch (err) {
137
- logger.debug({
138
- err,
139
- command
140
- }, "Failed to tokenize Dockerfile RUN command");
141
- return [];
142
- }
143
- const deps = [];
144
- let current = [];
145
- for (const token of tokens) {
146
- if (token.startsWith("#")) break;
147
- if (commandSeparators.includes(token)) {
148
- deps.push(...extractApkCommand(current));
149
- current = [];
150
- continue;
151
- }
152
- if (token.endsWith(";")) {
153
- current.push(token.slice(0, -1));
154
- deps.push(...extractApkCommand(current));
155
- current = [];
156
- continue;
157
- }
158
- current.push(token);
159
- }
160
- deps.push(...extractApkCommand(current));
161
- return deps;
162
- }
163
- /**
164
- * Finds the `apk` invocation in the tokens of a shell command, wherever it
165
- * sits - so it's found whether it's prefixed by a shell keyword
166
- * (`do`/`then`/...), a variable assignment (`DEBUG=1 apk add ...`), or a
167
- * wrapper program (`sudo`/`chroot /mnt`/`timeout 30`/...).
168
- */
169
- function extractApkCommand(tokens) {
170
- const index = tokens.findIndex((token) => token === "apk" || token.endsWith("/apk"));
171
- if (index === -1) return [];
172
- return extractApkAddArgs(tokens.slice(index + 1));
110
+ return parseRunCommands(instruction, escapeChar, ["apk"]).flatMap(extractApkAddArgs);
173
111
  }
174
112
  //#endregion
175
113
  export { extractApkDeps };
@@ -1 +1 @@
1
- {"version":3,"file":"apk.js","names":["apkVersioning"],"sources":["../../../../lib/modules/manager/dockerfile/apk.ts"],"sourcesContent":["import { split } from 'shlex';\nimport { logger } from '../../../logger/index.ts';\nimport { newlineRegex, regEx } from '../../../util/regex.ts';\nimport { ApkDatasource } from '../../datasource/apk/index.ts';\nimport { api as apkVersioning } from '../../versioning/apk/index.ts';\nimport type { PackageDependency } from '../types.ts';\n\n/** Shell operators which start a new command */\nconst commandSeparators = ['&&', '||', ';', '|', '&', '(', ')', '{', '}'];\n\n/**\n * `apk` options which consume the following argument, so that the argument is\n * not mistaken for a package name.\n *\n * Options given as `--opt=value` are a single token, so they need no entry here.\n */\nconst optionsWithValue = new Set([\n '-X',\n '--repository',\n '-t',\n '--virtual',\n '-p',\n '--root',\n '--arch',\n '--cache-dir',\n '--cache-max-age',\n '--keys-dir',\n '--repositories-file',\n '--progress-fd',\n '--timeout',\n '--wait',\n]);\n\n/**\n * An `apk` package specification, e.g. `bash`, `bash=5.2.37-r2`, `bash>5.2` or\n * `nodejs@edge=22.13.1-r0`.\n *\n * `apk` builds the constraint operator from its characters, so they may be\n * given in any order and may repeat - see the `apk` versioning module.\n *\n * The `@repoTag` suffix pins the package to a tagged repository from\n * `/etc/apk/repositories`, and is part of the spec rather than of the name.\n */\nconst apkSpecRegex = regEx(\n /^(?<name>[a-zA-Z0-9][\\w.+-]*)(?:@(?<repoTag>[\\w.-]+))?(?:(?<operator>[<>=~]+)(?<version>.+))?$/,\n);\n\nfunction parseSpec(spec: string): PackageDependency | null {\n const groups = apkSpecRegex.exec(spec)?.groups;\n if (!groups) {\n logger.trace({ spec }, 'Skipping unparseable apk package spec');\n return null;\n }\n\n const { name, operator, version } = groups;\n // the `apk` datasource defaults to `apk` versioning, so the constraint is\n // read the same way here as it is during the lookup\n const dep: PackageDependency = {\n datasource: ApkDatasource.id,\n depName: name,\n };\n\n if (!operator) {\n // Renovate has nothing to update until the user constrains the version\n dep.skipReason = 'unspecified-version';\n return dep;\n }\n\n if (version.includes('$')) {\n dep.skipReason = 'contains-variable';\n return dep;\n }\n\n // rejects an unparseable version, and the `><` identity hash operator\n const constraint = operator + version;\n if (!apkVersioning.isValid(constraint)) {\n dep.skipReason = 'unsupported-version';\n return dep;\n }\n\n // An exact pin reads better as a plain version, so the `=` stays in the\n // replaceString rather than becoming part of the value we report\n const isPin = apkVersioning.isSingleVersion(constraint);\n const currentValue = isPin ? version : constraint;\n\n dep.currentValue = currentValue;\n dep.replaceString = spec;\n // Only the value is templated, so a `@repoTag` in the spec is preserved\n dep.autoReplaceStringTemplate = `${spec.slice(0, -currentValue.length)}{{{newValue}}}`;\n return dep;\n}\n\n/** Package specs which are not registry lookups, and so are silently ignored */\nfunction isIgnoredSpec(spec: string): boolean {\n return (\n // conflict markers, e.g. `apk add foo !bar`\n spec.startsWith('!') ||\n // virtual package names, e.g. `apk add .build-deps`\n spec.startsWith('.') ||\n // local or remote `.apk` files, e.g. `apk add ./foo-1.2.3-r0.apk`\n spec.includes('/') ||\n // provider dependencies, e.g. `so:libc.musl-x86_64.so.1`, `cmd:node`\n spec.includes(':')\n );\n}\n\nfunction extractApkAddArgs(tokens: string[]): PackageDependency[] {\n const deps: PackageDependency[] = [];\n let subCommandFound = false;\n\n for (let i = 0; i < tokens.length; i += 1) {\n const token = tokens[i];\n\n if (token.startsWith('-')) {\n if (optionsWithValue.has(token)) {\n i += 1;\n }\n continue;\n }\n\n if (!subCommandFound) {\n // `apk` accepts global options before the sub-command, so the first\n // non-option token is the sub-command\n if (token !== 'add') {\n return [];\n }\n subCommandFound = true;\n continue;\n }\n\n if (isIgnoredSpec(token)) {\n logger.trace({ spec: token }, 'Skipping apk package spec');\n continue;\n }\n\n const dep = parseSpec(token);\n if (dep) {\n deps.push(dep);\n }\n }\n\n return deps;\n}\n\n/**\n * The `RUN` keyword and its flags, e.g. `RUN --mount=type=cache,target=/x `.\n *\n * Stripping these leaves the shell command that the instruction runs.\n */\nconst runPrefixRegex = regEx(\n /^[ \\t]*(?:ONBUILD[ \\t]+)?RUN[ \\t]+(?:--[a-z]\\S*[ \\t]+)*/i,\n);\n\n/**\n * Extracts APK packages pinned by `apk add` in a `RUN` instruction, e.g.\n *\n * ```dockerfile\n * RUN apk add --no-cache \\\n * bash=5.2.37-r2 \\\n * rsyslog=8.2412.0-r1\n * ```\n *\n * @param instruction the full `RUN` instruction, including any line continuations\n * @param escapeChar the Dockerfile escape character, already regex-escaped\n */\nexport function extractApkDeps(\n instruction: string,\n escapeChar: string,\n): PackageDependency[] {\n // A `#` line inside a line continuation is a Dockerfile comment, and is\n // dropped before the shell ever sees it\n const joined = instruction\n .split(newlineRegex)\n .filter((line) => !regEx(/^[ \\t]*#/).test(line))\n .join('\\n')\n .replace(regEx(`${escapeChar}[ \\\\t]*\\\\r?\\\\n`, 'g'), ' ');\n\n const runPrefix = runPrefixRegex.exec(joined)?.[0];\n if (!runPrefix) {\n return [];\n }\n\n const command = joined.slice(runPrefix.length);\n if (!command.includes('apk')) {\n return [];\n }\n\n let tokens: string[];\n try {\n tokens = split(command);\n } catch (err) {\n logger.debug({ err, command }, 'Failed to tokenize Dockerfile RUN command');\n return [];\n }\n\n const deps: PackageDependency[] = [];\n // Split the shell command into the individual commands it runs, so that only\n // the arguments of an `apk` invocation are considered\n let current: string[] = [];\n for (const token of tokens) {\n if (token.startsWith('#')) {\n // the rest of the command is a shell comment\n break;\n }\n if (commandSeparators.includes(token)) {\n deps.push(...extractApkCommand(current));\n current = [];\n continue;\n }\n // e.g. the `{1..10};` of `for iter in {1..10}; do apk add ...`, which shlex\n // keeps as one token because no whitespace precedes the `;`\n if (token.endsWith(';')) {\n current.push(token.slice(0, -1));\n deps.push(...extractApkCommand(current));\n current = [];\n continue;\n }\n current.push(token);\n }\n deps.push(...extractApkCommand(current));\n\n return deps;\n}\n\n/**\n * Finds the `apk` invocation in the tokens of a shell command, wherever it\n * sits - so it's found whether it's prefixed by a shell keyword\n * (`do`/`then`/...), a variable assignment (`DEBUG=1 apk add ...`), or a\n * wrapper program (`sudo`/`chroot /mnt`/`timeout 30`/...).\n */\nfunction extractApkCommand(tokens: string[]): PackageDependency[] {\n const index = tokens.findIndex(\n (token) => token === 'apk' || token.endsWith('/apk'),\n );\n if (index === -1) {\n return [];\n }\n return extractApkAddArgs(tokens.slice(index + 1));\n}\n"],"mappings":";;;;;;;AAQA,MAAM,oBAAoB;CAAC;CAAM;CAAM;CAAK;CAAK;CAAK;CAAK;CAAK;CAAK;AAAG;;;;;;;AAQxE,MAAM,mCAAmB,IAAI,IAAI;CAC/B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;;;;;;;;;;;AAYD,MAAM,eAAe,MACnB,gGACF;AAEA,SAAS,UAAU,MAAwC;CACzD,MAAM,SAAS,aAAa,KAAK,IAAI,CAAC,EAAE;CACxC,IAAI,CAAC,QAAQ;EACX,OAAO,MAAM,EAAE,KAAK,GAAG,uCAAuC;EAC9D,OAAO;CACT;CAEA,MAAM,EAAE,MAAM,UAAU,YAAY;CAGpC,MAAM,MAAyB;EAC7B,YAAY,cAAc;EAC1B,SAAS;CACX;CAEA,IAAI,CAAC,UAAU;EAEb,IAAI,aAAa;EACjB,OAAO;CACT;CAEA,IAAI,QAAQ,SAAS,GAAG,GAAG;EACzB,IAAI,aAAa;EACjB,OAAO;CACT;CAGA,MAAM,aAAa,WAAW;CAC9B,IAAI,CAACA,IAAc,QAAQ,UAAU,GAAG;EACtC,IAAI,aAAa;EACjB,OAAO;CACT;CAKA,MAAM,eADQA,IAAc,gBAAgB,UACnB,IAAI,UAAU;CAEvC,IAAI,eAAe;CACnB,IAAI,gBAAgB;CAEpB,IAAI,4BAA4B,GAAG,KAAK,MAAM,GAAG,CAAC,aAAa,MAAM,EAAE;CACvE,OAAO;AACT;;AAGA,SAAS,cAAc,MAAuB;CAC5C,OAEE,KAAK,WAAW,GAAG,KAEnB,KAAK,WAAW,GAAG,KAEnB,KAAK,SAAS,GAAG,KAEjB,KAAK,SAAS,GAAG;AAErB;AAEA,SAAS,kBAAkB,QAAuC;CAChE,MAAM,OAA4B,CAAC;CACnC,IAAI,kBAAkB;CAEtB,KAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK,GAAG;EACzC,MAAM,QAAQ,OAAO;EAErB,IAAI,MAAM,WAAW,GAAG,GAAG;GACzB,IAAI,iBAAiB,IAAI,KAAK,GAC5B,KAAK;GAEP;EACF;EAEA,IAAI,CAAC,iBAAiB;GAGpB,IAAI,UAAU,OACZ,OAAO,CAAC;GAEV,kBAAkB;GAClB;EACF;EAEA,IAAI,cAAc,KAAK,GAAG;GACxB,OAAO,MAAM,EAAE,MAAM,MAAM,GAAG,2BAA2B;GACzD;EACF;EAEA,MAAM,MAAM,UAAU,KAAK;EAC3B,IAAI,KACF,KAAK,KAAK,GAAG;CAEjB;CAEA,OAAO;AACT;;;;;;AAOA,MAAM,iBAAiB,MACrB,0DACF;;;;;;;;;;;;;AAcA,SAAgB,eACd,aACA,YACqB;CAGrB,MAAM,SAAS,YACZ,MAAM,YAAY,CAAC,CACnB,QAAQ,SAAS,CAAC,MAAM,UAAU,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAC/C,KAAK,IAAI,CAAC,CACV,QAAQ,MAAM,GAAG,WAAW,iBAAiB,GAAG,GAAG,GAAG;CAEzD,MAAM,YAAY,eAAe,KAAK,MAAM,CAAC,GAAG;CAChD,IAAI,CAAC,WACH,OAAO,CAAC;CAGV,MAAM,UAAU,OAAO,MAAM,UAAU,MAAM;CAC7C,IAAI,CAAC,QAAQ,SAAS,KAAK,GACzB,OAAO,CAAC;CAGV,IAAI;CACJ,IAAI;EACF,SAAS,MAAM,OAAO;CACxB,SAAS,KAAK;EACZ,OAAO,MAAM;GAAE;GAAK;EAAQ,GAAG,2CAA2C;EAC1E,OAAO,CAAC;CACV;CAEA,MAAM,OAA4B,CAAC;CAGnC,IAAI,UAAoB,CAAC;CACzB,KAAK,MAAM,SAAS,QAAQ;EAC1B,IAAI,MAAM,WAAW,GAAG,GAEtB;EAEF,IAAI,kBAAkB,SAAS,KAAK,GAAG;GACrC,KAAK,KAAK,GAAG,kBAAkB,OAAO,CAAC;GACvC,UAAU,CAAC;GACX;EACF;EAGA,IAAI,MAAM,SAAS,GAAG,GAAG;GACvB,QAAQ,KAAK,MAAM,MAAM,GAAG,EAAE,CAAC;GAC/B,KAAK,KAAK,GAAG,kBAAkB,OAAO,CAAC;GACvC,UAAU,CAAC;GACX;EACF;EACA,QAAQ,KAAK,KAAK;CACpB;CACA,KAAK,KAAK,GAAG,kBAAkB,OAAO,CAAC;CAEvC,OAAO;AACT;;;;;;;AAQA,SAAS,kBAAkB,QAAuC;CAChE,MAAM,QAAQ,OAAO,WAClB,UAAU,UAAU,SAAS,MAAM,SAAS,MAAM,CACrD;CACA,IAAI,UAAU,IACZ,OAAO,CAAC;CAEV,OAAO,kBAAkB,OAAO,MAAM,QAAQ,CAAC,CAAC;AAClD"}
1
+ {"version":3,"file":"apk.js","names":["apkVersioning"],"sources":["../../../../lib/modules/manager/dockerfile/apk.ts"],"sourcesContent":["import { logger } from '../../../logger/index.ts';\nimport { regEx } from '../../../util/regex.ts';\nimport { ApkDatasource } from '../../datasource/apk/index.ts';\nimport { api as apkVersioning } from '../../versioning/apk/index.ts';\nimport type { PackageDependency } from '../types.ts';\nimport { parseRunCommands } from './run-command.ts';\n\n/**\n * `apk` options which consume the following argument, so that the argument is\n * not mistaken for a package name.\n *\n * Options given as `--opt=value` are a single token, so they need no entry here.\n */\nconst optionsWithValue = new Set([\n '-X',\n '--repository',\n '-t',\n '--virtual',\n '-p',\n '--root',\n '--arch',\n '--cache-dir',\n '--cache-max-age',\n '--keys-dir',\n '--repositories-file',\n '--progress-fd',\n '--timeout',\n '--wait',\n]);\n\n/**\n * An `apk` package specification, e.g. `bash`, `bash=5.2.37-r2`, `bash>5.2` or\n * `nodejs@edge=22.13.1-r0`.\n *\n * `apk` builds the constraint operator from its characters, so they may be\n * given in any order and may repeat - see the `apk` versioning module.\n *\n * The `@repoTag` suffix pins the package to a tagged repository from\n * `/etc/apk/repositories`, and is part of the spec rather than of the name.\n */\nconst apkSpecRegex = regEx(\n /^(?<name>[a-zA-Z0-9][\\w.+-]*)(?:@(?<repoTag>[\\w.-]+))?(?:(?<operator>[<>=~]+)(?<version>.+))?$/,\n);\n\nfunction parseSpec(spec: string): PackageDependency | null {\n const groups = apkSpecRegex.exec(spec)?.groups;\n if (!groups) {\n logger.trace({ spec }, 'Skipping unparseable apk package spec');\n return null;\n }\n\n const { name, operator, version } = groups;\n // the `apk` datasource defaults to `apk` versioning, so the constraint is\n // read the same way here as it is during the lookup\n const dep: PackageDependency = {\n datasource: ApkDatasource.id,\n depName: name,\n };\n\n if (!operator) {\n // Renovate has nothing to update until the user constrains the version\n dep.skipReason = 'unspecified-version';\n return dep;\n }\n\n if (version.includes('$')) {\n dep.skipReason = 'contains-variable';\n return dep;\n }\n\n // rejects an unparseable version, and the `><` identity hash operator\n const constraint = operator + version;\n if (!apkVersioning.isValid(constraint)) {\n dep.skipReason = 'unsupported-version';\n return dep;\n }\n\n // An exact pin reads better as a plain version, so the `=` stays in the\n // replaceString rather than becoming part of the value we report\n const isPin = apkVersioning.isSingleVersion(constraint);\n const currentValue = isPin ? version : constraint;\n\n dep.currentValue = currentValue;\n dep.replaceString = spec;\n // Only the value is templated, so a `@repoTag` in the spec is preserved\n dep.autoReplaceStringTemplate = `${spec.slice(0, -currentValue.length)}{{{newValue}}}`;\n return dep;\n}\n\n/** Package specs which are not registry lookups, and so are silently ignored */\nfunction isIgnoredSpec(spec: string): boolean {\n return (\n // conflict markers, e.g. `apk add foo !bar`\n spec.startsWith('!') ||\n // virtual package names, e.g. `apk add .build-deps`\n spec.startsWith('.') ||\n // local or remote `.apk` files, e.g. `apk add ./foo-1.2.3-r0.apk`\n spec.includes('/') ||\n // provider dependencies, e.g. `so:libc.musl-x86_64.so.1`, `cmd:node`\n spec.includes(':')\n );\n}\n\nfunction extractApkAddArgs(tokens: string[]): PackageDependency[] {\n const deps: PackageDependency[] = [];\n let subCommandFound = false;\n\n for (let i = 0; i < tokens.length; i += 1) {\n const token = tokens[i];\n\n if (token.startsWith('-')) {\n if (optionsWithValue.has(token)) {\n i += 1;\n }\n continue;\n }\n\n if (!subCommandFound) {\n // `apk` accepts global options before the sub-command, so the first\n // non-option token is the sub-command\n if (token !== 'add') {\n return [];\n }\n subCommandFound = true;\n continue;\n }\n\n if (isIgnoredSpec(token)) {\n logger.trace({ spec: token }, 'Skipping apk package spec');\n continue;\n }\n\n const dep = parseSpec(token);\n if (dep) {\n deps.push(dep);\n }\n }\n\n return deps;\n}\n\n/**\n * Extracts APK packages pinned by `apk add` in a `RUN` instruction, e.g.\n *\n * ```dockerfile\n * RUN apk add --no-cache \\\n * bash=5.2.37-r2 \\\n * rsyslog=8.2412.0-r1\n * ```\n *\n * @param instruction the full `RUN` instruction, including any line continuations\n * @param escapeChar the Dockerfile escape character, already regex-escaped\n */\nexport function extractApkDeps(\n instruction: string,\n escapeChar: string,\n): PackageDependency[] {\n return parseRunCommands(instruction, escapeChar, ['apk']).flatMap(\n extractApkAddArgs,\n );\n}\n"],"mappings":";;;;;;;;;;;;AAaA,MAAM,mCAAmB,IAAI,IAAI;CAC/B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;;;;;;;;;;;AAYD,MAAM,eAAe,MACnB,gGACF;AAEA,SAAS,UAAU,MAAwC;CACzD,MAAM,SAAS,aAAa,KAAK,IAAI,CAAC,EAAE;CACxC,IAAI,CAAC,QAAQ;EACX,OAAO,MAAM,EAAE,KAAK,GAAG,uCAAuC;EAC9D,OAAO;CACT;CAEA,MAAM,EAAE,MAAM,UAAU,YAAY;CAGpC,MAAM,MAAyB;EAC7B,YAAY,cAAc;EAC1B,SAAS;CACX;CAEA,IAAI,CAAC,UAAU;EAEb,IAAI,aAAa;EACjB,OAAO;CACT;CAEA,IAAI,QAAQ,SAAS,GAAG,GAAG;EACzB,IAAI,aAAa;EACjB,OAAO;CACT;CAGA,MAAM,aAAa,WAAW;CAC9B,IAAI,CAACA,IAAc,QAAQ,UAAU,GAAG;EACtC,IAAI,aAAa;EACjB,OAAO;CACT;CAKA,MAAM,eADQA,IAAc,gBAAgB,UACnB,IAAI,UAAU;CAEvC,IAAI,eAAe;CACnB,IAAI,gBAAgB;CAEpB,IAAI,4BAA4B,GAAG,KAAK,MAAM,GAAG,CAAC,aAAa,MAAM,EAAE;CACvE,OAAO;AACT;;AAGA,SAAS,cAAc,MAAuB;CAC5C,OAEE,KAAK,WAAW,GAAG,KAEnB,KAAK,WAAW,GAAG,KAEnB,KAAK,SAAS,GAAG,KAEjB,KAAK,SAAS,GAAG;AAErB;AAEA,SAAS,kBAAkB,QAAuC;CAChE,MAAM,OAA4B,CAAC;CACnC,IAAI,kBAAkB;CAEtB,KAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK,GAAG;EACzC,MAAM,QAAQ,OAAO;EAErB,IAAI,MAAM,WAAW,GAAG,GAAG;GACzB,IAAI,iBAAiB,IAAI,KAAK,GAC5B,KAAK;GAEP;EACF;EAEA,IAAI,CAAC,iBAAiB;GAGpB,IAAI,UAAU,OACZ,OAAO,CAAC;GAEV,kBAAkB;GAClB;EACF;EAEA,IAAI,cAAc,KAAK,GAAG;GACxB,OAAO,MAAM,EAAE,MAAM,MAAM,GAAG,2BAA2B;GACzD;EACF;EAEA,MAAM,MAAM,UAAU,KAAK;EAC3B,IAAI,KACF,KAAK,KAAK,GAAG;CAEjB;CAEA,OAAO;AACT;;;;;;;;;;;;;AAcA,SAAgB,eACd,aACA,YACqB;CACrB,OAAO,iBAAiB,aAAa,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,QACxD,iBACF;AACF"}
@@ -0,0 +1,108 @@
1
+ import { regEx } from "../../../util/regex.js";
2
+ import { logger } from "../../../logger/index.js";
3
+ import { api } from "../../versioning/deb/index.js";
4
+ import { DebDatasource } from "../../datasource/deb/index.js";
5
+ import { parseRunCommands } from "./run-command.js";
6
+ //#region lib/modules/manager/dockerfile/deb.ts
7
+ /**
8
+ * `apt` options which consume the following argument, so that the argument is
9
+ * not mistaken for a package name.
10
+ *
11
+ * Options given as `--opt=value` are a single token, so they need no entry here.
12
+ */
13
+ const optionsWithValue = /* @__PURE__ */ new Set([
14
+ "-o",
15
+ "--option",
16
+ "-c",
17
+ "--config-file",
18
+ "-t",
19
+ "--target-release",
20
+ "--default-release",
21
+ "-a",
22
+ "--host-architecture",
23
+ "--build-profiles"
24
+ ]);
25
+ /**
26
+ * An `apt` package specification, e.g. `curl`, `curl=8.5.0-2ubuntu10.6`,
27
+ * `curl:amd64=8.5.0-2ubuntu10.6` or `curl/bookworm-backports`.
28
+ *
29
+ * The `:arch` qualifier selects the architecture to install for, and the
30
+ * `/release` suffix picks the suite to install from - both are part of the
31
+ * spec rather than of the package name.
32
+ */
33
+ const debSpecRegex = regEx(/^(?<name>[a-z0-9][a-z0-9+.-]*)(?::(?<arch>[a-z0-9][a-z0-9-]*))?(?:=(?<version>.+)|\/(?<release>[a-z0-9][\w.-]*))?$/);
34
+ function parseSpec(spec) {
35
+ const groups = debSpecRegex.exec(spec)?.groups;
36
+ if (!groups) {
37
+ logger.trace({ spec }, "Skipping unparseable apt package spec");
38
+ return null;
39
+ }
40
+ const { name, version } = groups;
41
+ const dep = {
42
+ datasource: DebDatasource.id,
43
+ depName: name
44
+ };
45
+ if (!version) {
46
+ dep.skipReason = "unspecified-version";
47
+ return dep;
48
+ }
49
+ if (version.includes("$")) {
50
+ dep.skipReason = "contains-variable";
51
+ return dep;
52
+ }
53
+ if (!api.isSingleVersion(version)) {
54
+ dep.skipReason = "unsupported-version";
55
+ return dep;
56
+ }
57
+ dep.currentValue = version;
58
+ dep.replaceString = spec;
59
+ dep.autoReplaceStringTemplate = `${spec.slice(0, -version.length)}{{{newValue}}}`;
60
+ return dep;
61
+ }
62
+ /** Package specs which are not registry lookups, and so are silently ignored */
63
+ function isIgnoredSpec(spec) {
64
+ return spec.endsWith(".deb") || spec.startsWith(".") || spec.startsWith("/") || spec.endsWith("-") || spec.endsWith("+");
65
+ }
66
+ function extractAptInstallArgs(tokens) {
67
+ const deps = [];
68
+ let subCommandFound = false;
69
+ for (let i = 0; i < tokens.length; i += 1) {
70
+ const token = tokens[i];
71
+ if (token.startsWith("-")) {
72
+ if (optionsWithValue.has(token)) i += 1;
73
+ continue;
74
+ }
75
+ if (!subCommandFound) {
76
+ if (token !== "install") return [];
77
+ subCommandFound = true;
78
+ continue;
79
+ }
80
+ if (isIgnoredSpec(token)) {
81
+ logger.trace({ spec: token }, "Skipping apt package spec");
82
+ continue;
83
+ }
84
+ const dep = parseSpec(token);
85
+ if (dep) deps.push(dep);
86
+ }
87
+ return deps;
88
+ }
89
+ /**
90
+ * Extracts Debian packages pinned by `apt install` or `apt-get install` in a
91
+ * `RUN` instruction, e.g.
92
+ *
93
+ * ```dockerfile
94
+ * RUN apt-get update && apt-get install -y --no-install-recommends \
95
+ * curl=8.5.0-2ubuntu10.6 \
96
+ * git=1:2.43.0-1ubuntu7.3
97
+ * ```
98
+ *
99
+ * @param instruction the full `RUN` instruction, including any line continuations
100
+ * @param escapeChar the Dockerfile escape character, already regex-escaped
101
+ */
102
+ function extractDebDeps(instruction, escapeChar) {
103
+ return parseRunCommands(instruction, escapeChar, ["apt", "apt-get"]).flatMap(extractAptInstallArgs);
104
+ }
105
+ //#endregion
106
+ export { extractDebDeps };
107
+
108
+ //# sourceMappingURL=deb.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deb.js","names":["debVersioning"],"sources":["../../../../lib/modules/manager/dockerfile/deb.ts"],"sourcesContent":["import { logger } from '../../../logger/index.ts';\nimport { regEx } from '../../../util/regex.ts';\nimport { DebDatasource } from '../../datasource/deb/index.ts';\nimport { api as debVersioning } from '../../versioning/deb/index.ts';\nimport type { PackageDependency } from '../types.ts';\nimport { parseRunCommands } from './run-command.ts';\n\n/**\n * `apt` options which consume the following argument, so that the argument is\n * not mistaken for a package name.\n *\n * Options given as `--opt=value` are a single token, so they need no entry here.\n */\nconst optionsWithValue = new Set([\n '-o',\n '--option',\n '-c',\n '--config-file',\n '-t',\n '--target-release',\n '--default-release',\n '-a',\n '--host-architecture',\n '--build-profiles',\n]);\n\n/**\n * An `apt` package specification, e.g. `curl`, `curl=8.5.0-2ubuntu10.6`,\n * `curl:amd64=8.5.0-2ubuntu10.6` or `curl/bookworm-backports`.\n *\n * The `:arch` qualifier selects the architecture to install for, and the\n * `/release` suffix picks the suite to install from - both are part of the\n * spec rather than of the package name.\n */\nconst debSpecRegex = regEx(\n /^(?<name>[a-z0-9][a-z0-9+.-]*)(?::(?<arch>[a-z0-9][a-z0-9-]*))?(?:=(?<version>.+)|\\/(?<release>[a-z0-9][\\w.-]*))?$/,\n);\n\nfunction parseSpec(spec: string): PackageDependency | null {\n const groups = debSpecRegex.exec(spec)?.groups;\n if (!groups) {\n logger.trace({ spec }, 'Skipping unparseable apt package spec');\n return null;\n }\n\n const { name, version } = groups;\n const dep: PackageDependency = {\n datasource: DebDatasource.id,\n depName: name,\n };\n\n if (!version) {\n // a bare name, or one only pinned to a suite, leaves Renovate nothing to\n // update\n dep.skipReason = 'unspecified-version';\n return dep;\n }\n\n if (version.includes('$')) {\n dep.skipReason = 'contains-variable';\n return dep;\n }\n\n // `apt` also accepts a wildcard such as `curl=8.5.*`, which is not a version\n // we can compare against the releases we find\n if (!debVersioning.isSingleVersion(version)) {\n dep.skipReason = 'unsupported-version';\n return dep;\n }\n\n dep.currentValue = version;\n dep.replaceString = spec;\n // Only the version is templated, so an `:arch` in the spec is preserved\n dep.autoReplaceStringTemplate = `${spec.slice(0, -version.length)}{{{newValue}}}`;\n return dep;\n}\n\n/** Package specs which are not registry lookups, and so are silently ignored */\nfunction isIgnoredSpec(spec: string): boolean {\n return (\n // local or remote `.deb` files, e.g. `apt install ./curl_8.5.0-2_amd64.deb`\n spec.endsWith('.deb') ||\n spec.startsWith('.') ||\n spec.startsWith('/') ||\n // the `-` removes a package and the `+` installs one, e.g.\n // `apt install curl vim-`\n spec.endsWith('-') ||\n spec.endsWith('+')\n );\n}\n\nfunction extractAptInstallArgs(tokens: string[]): PackageDependency[] {\n const deps: PackageDependency[] = [];\n let subCommandFound = false;\n\n for (let i = 0; i < tokens.length; i += 1) {\n const token = tokens[i];\n\n if (token.startsWith('-')) {\n if (optionsWithValue.has(token)) {\n i += 1;\n }\n continue;\n }\n\n if (!subCommandFound) {\n // `apt` accepts global options before the sub-command, so the first\n // non-option token is the sub-command\n if (token !== 'install') {\n return [];\n }\n subCommandFound = true;\n continue;\n }\n\n if (isIgnoredSpec(token)) {\n logger.trace({ spec: token }, 'Skipping apt package spec');\n continue;\n }\n\n const dep = parseSpec(token);\n if (dep) {\n deps.push(dep);\n }\n }\n\n return deps;\n}\n\n/**\n * Extracts Debian packages pinned by `apt install` or `apt-get install` in a\n * `RUN` instruction, e.g.\n *\n * ```dockerfile\n * RUN apt-get update && apt-get install -y --no-install-recommends \\\n * curl=8.5.0-2ubuntu10.6 \\\n * git=1:2.43.0-1ubuntu7.3\n * ```\n *\n * @param instruction the full `RUN` instruction, including any line continuations\n * @param escapeChar the Dockerfile escape character, already regex-escaped\n */\nexport function extractDebDeps(\n instruction: string,\n escapeChar: string,\n): PackageDependency[] {\n return parseRunCommands(instruction, escapeChar, ['apt', 'apt-get']).flatMap(\n extractAptInstallArgs,\n );\n}\n"],"mappings":";;;;;;;;;;;;AAaA,MAAM,mCAAmB,IAAI,IAAI;CAC/B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;;;;;;;;;AAUD,MAAM,eAAe,MACnB,oHACF;AAEA,SAAS,UAAU,MAAwC;CACzD,MAAM,SAAS,aAAa,KAAK,IAAI,CAAC,EAAE;CACxC,IAAI,CAAC,QAAQ;EACX,OAAO,MAAM,EAAE,KAAK,GAAG,uCAAuC;EAC9D,OAAO;CACT;CAEA,MAAM,EAAE,MAAM,YAAY;CAC1B,MAAM,MAAyB;EAC7B,YAAY,cAAc;EAC1B,SAAS;CACX;CAEA,IAAI,CAAC,SAAS;EAGZ,IAAI,aAAa;EACjB,OAAO;CACT;CAEA,IAAI,QAAQ,SAAS,GAAG,GAAG;EACzB,IAAI,aAAa;EACjB,OAAO;CACT;CAIA,IAAI,CAACA,IAAc,gBAAgB,OAAO,GAAG;EAC3C,IAAI,aAAa;EACjB,OAAO;CACT;CAEA,IAAI,eAAe;CACnB,IAAI,gBAAgB;CAEpB,IAAI,4BAA4B,GAAG,KAAK,MAAM,GAAG,CAAC,QAAQ,MAAM,EAAE;CAClE,OAAO;AACT;;AAGA,SAAS,cAAc,MAAuB;CAC5C,OAEE,KAAK,SAAS,MAAM,KACpB,KAAK,WAAW,GAAG,KACnB,KAAK,WAAW,GAAG,KAGnB,KAAK,SAAS,GAAG,KACjB,KAAK,SAAS,GAAG;AAErB;AAEA,SAAS,sBAAsB,QAAuC;CACpE,MAAM,OAA4B,CAAC;CACnC,IAAI,kBAAkB;CAEtB,KAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK,GAAG;EACzC,MAAM,QAAQ,OAAO;EAErB,IAAI,MAAM,WAAW,GAAG,GAAG;GACzB,IAAI,iBAAiB,IAAI,KAAK,GAC5B,KAAK;GAEP;EACF;EAEA,IAAI,CAAC,iBAAiB;GAGpB,IAAI,UAAU,WACZ,OAAO,CAAC;GAEV,kBAAkB;GAClB;EACF;EAEA,IAAI,cAAc,KAAK,GAAG;GACxB,OAAO,MAAM,EAAE,MAAM,MAAM,GAAG,2BAA2B;GACzD;EACF;EAEA,MAAM,MAAM,UAAU,KAAK;EAC3B,IAAI,KACF,KAAK,KAAK,GAAG;CAEjB;CAEA,OAAO;AACT;;;;;;;;;;;;;;AAeA,SAAgB,eACd,aACA,YACqB;CACrB,OAAO,iBAAiB,aAAa,YAAY,CAAC,OAAO,SAAS,CAAC,CAAC,CAAC,QACnE,qBACF;AACF"}
@@ -6,6 +6,7 @@ import { api, id } from "../../versioning/debian/index.js";
6
6
  import { id as id$1 } from "../../versioning/ubuntu/index.js";
7
7
  import { DockerDatasource } from "../../datasource/docker/index.js";
8
8
  import { extractApkDeps } from "./apk.js";
9
+ import { extractDebDeps } from "./deb.js";
9
10
  import { isNonEmptyStringAndNotWhitespace, isNumericString, isString } from "@sindresorhus/is";
10
11
  //#region lib/modules/manager/dockerfile/extract.ts
11
12
  const variableMarker = "$";
@@ -239,7 +240,7 @@ function extractPackageFile(content, _packageFile, config) {
239
240
  deps.push(dep);
240
241
  }
241
242
  }
242
- for (const dep of extractApkDeps(instruction, escapeChar)) {
243
+ for (const dep of [...extractApkDeps(instruction, escapeChar), ...extractDebDeps(instruction, escapeChar)]) {
243
244
  dep.depType = "install";
244
245
  deps.push(dep);
245
246
  }