UNPKG

renovate

Version:

Automated dependency updates. Flexible so you don't need to be.

172 lines (171 loc) 6.54 kB
import "../../../constants/error-messages.js"; import { regEx } from "../../../util/regex.js"; import { GlobalConfig } from "../../../config/global.js"; import { logger } from "../../../logger/index.js"; import { find } from "../../../util/host-rules.js"; import { readLocalFile } from "../../../util/fs/index.js"; import { api } from "../../versioning/semver/index.js"; import { exec } from "../../../util/exec/index.js"; import { findGithubToken } from "../../../util/check-token.js"; import { getConfigType, getLockFileName } from "./lockfile.js"; import { isNonEmptyStringAndNotWhitespace, isString } from "@sindresorhus/is"; import { quote } from "shlex"; import upath from "upath"; //#region lib/modules/manager/mise/artifacts.ts /** * First mise release that supports the features this manager relies on: * `MISE_SAFE=1` safe mode (a hard boundary against project config executing * code during `mise lock`) and `mise lock --bump` (advancing fuzzy selectors). * * Safe mode (jdx/mise#11146) and lockfile bumping (jdx/mise#11145) merged after * v2026.7.11 and first shipped in v2026.7.12. * * @see https://github.com/jdx/mise/pull/11146 * @see https://github.com/jdx/mise/pull/11145 * @see https://mise.jdx.dev/configuration/settings.html#safe */ const MISE_SAFE_MODE_MIN_VERSION = "2026.7.12"; /** * Detects the mise version that will actually run, by executing `mise version`. * `mise version` only prints the binary's version — it does not load or execute * project configuration — so it is safe to run even without allowlisting. * * Output looks like `2026.7.12 macos-arm64 (2026-07-21)`; we take the leading * `major.minor.patch`. Returns `null` if the probe fails or cannot be parsed, * in which case callers fall back to the conservative (pre-safe-mode) behavior. */ async function detectMiseVersion(execOptions) { try { const { stdout } = await exec("mise version", execOptions); const version = regEx(/\d+\.\d+\.\d+/).exec(stdout)?.[0]; if (version && api.isVersion(version)) return version; logger.debug({ stdout }, "Could not parse mise version output"); return null; } catch (err) { logger.debug({ err }, "Failed to determine mise version"); return null; } } /** True when `version` is at or above the safe-mode / `--bump` release. */ function versionSupportsSafeFeatures(version) { return !!version && (api.equals(version, MISE_SAFE_MODE_MIN_VERSION) || api.isGreaterThan(version, MISE_SAFE_MODE_MIN_VERSION)); } /** * Resolver tools that mise may invoke while running `mise lock`, for instance to convert an inexact version like `1` against the npm registry. * * NOTE: this will install all relevant tools, regardless of what the given mise configuration uses. * * In safe mode these are unnecessary: npm version listing uses mise's built-in * registry HTTP client and go runs with `GOTOOLCHAIN=local`, so `mise lock` * resolves versions over HTTP without shelling out to node/npm/ruby. * * @see https://mise.jdx.dev/dev-tools/backends/npm.html * @see https://mise.jdx.dev/dev-tools/backends/go.html * @see https://mise.jdx.dev/dev-tools/backends/gem.html */ function getMiseLockToolConstraints(constraints, safeMode = false) { const miseConstraint = { toolName: "mise", constraint: constraints?.mise }; if (safeMode) return [miseConstraint]; return [ miseConstraint, { toolName: "node", constraint: constraints?.node }, { toolName: "npm", constraint: constraints?.npm }, { toolName: "golang", constraint: constraints?.go }, { toolName: "ruby", constraint: constraints?.ruby } ]; } /** * Updates mise lock files when dependencies are updated. * Runs `mise lock` for lock file maintenance or `mise lock <tools>` for targeted updates. */ async function updateArtifacts({ packageFileName, updatedDeps, config }) { const lockFileName = getLockFileName(packageFileName); const existingLockFileContent = await readLocalFile(lockFileName, "utf8"); if (!existingLockFileContent) { logger.debug({ lockFileName }, "No mise lock file found"); return null; } const miseAllowlisted = GlobalConfig.get("allowedUnsafeExecutions").includes("mise"); let miseSupportsSafeFeatures = false; if (!miseAllowlisted || config.isLockFileMaintenance) miseSupportsSafeFeatures = versionSupportsSafeFeatures(await detectMiseVersion({ cwdFile: packageFileName, toolConstraints: [{ toolName: "mise", constraint: config.constraints?.mise }], docker: {} })); const safeMode = !miseAllowlisted && miseSupportsSafeFeatures; if (!miseAllowlisted && !safeMode) { logger.once.warn({ safeModeMinVersion: MISE_SAFE_MODE_MIN_VERSION }, "`mise lock` requires either `mise` in `allowedUnsafeExecutions`, or a mise version that supports safe mode"); return null; } const { isLocal, env } = getConfigType(packageFileName); const localFlag = isLocal ? " --local" : ""; let lockCmd; if (config.isLockFileMaintenance) lockCmd = `mise lock${localFlag}${miseSupportsSafeFeatures ? " --bump" : ""}`; else { const tools = updatedDeps.map(({ depName }) => depName).filter(isNonEmptyStringAndNotWhitespace).map(quote).join(" "); lockCmd = tools ? `mise lock${localFlag} ${tools}` : `mise lock${localFlag}`; } const extraEnv = {}; if (env) extraEnv.MISE_ENV = env; if (safeMode) extraEnv.MISE_SAFE = "1"; const token = findGithubToken(find({ hostType: "github", url: "https://api.github.com/" })); if (token) extraEnv.GITHUB_TOKEN = token; const execOptions = { cwdFile: packageFileName, extraEnv, toolConstraints: getMiseLockToolConstraints(config.constraints, safeMode), docker: {} }; const commands = safeMode ? [lockCmd] : [`mise trust ${quote(upath.basename(packageFileName))}`, lockCmd]; try { await exec(commands, execOptions); const newLockFileContent = await readLocalFile(lockFileName, "utf8"); if (!newLockFileContent || existingLockFileContent === newLockFileContent) return null; logger.debug({ lockFileName }, "Returning updated mise lock file"); return [{ file: { type: "addition", path: lockFileName, contents: newLockFileContent } }]; } catch (err) { // istanbul ignore if: not worth testing if (err.message === "temporary-error") throw err; const errorOutput = [ err.stdout, err.stderr, err.message ].filter(isString).join("\n"); logger.warn({ err, lockFileName }, "Error updating mise lock file"); return [{ artifactError: { fileName: lockFileName, stderr: errorOutput } }]; } } //#endregion export { updateArtifacts }; //# sourceMappingURL=artifacts.js.map