Refuse to release from dependencies that don't match the lockfiles

This commit is contained in:
Jon Staab 2026-09-24 13:43:59 -07:00
parent 04bd38429e
commit 69cca87144
2 changed files with 35 additions and 3 deletions

View file

@ -1,5 +1,7 @@
import {existsSync, readFileSync} from "node:fs"
import {join} from "node:path"
import {parseArgs} from "node:util"
import {followUps, git, name, notes, repository, version} from "./context.mjs"
import {followUps, git, name, notes, repository, root, version} from "./context.mjs"
import {ask, bold, dim, fail, green, red, yellow} from "./shell.mjs"
export const release = async (command, steps) => {
@ -29,6 +31,19 @@ export const release = async (command, steps) => {
const problems = selected.map(step => ({step, missing: step.missing?.() ?? []}))
const warnings = []
// pnpm keeps a copy of the lockfile it last installed from, so any difference means a pull or
// checkout since then changed dependencies the build would silently go without
const installed = join(root, "node_modules/.pnpm/lock.yaml")
if (
!existsSync(installed) ||
readFileSync(installed, "utf-8") !== readFileSync(join(root, "pnpm-lock.yaml"), "utf-8")
) {
problems.push({
missing: ["node_modules doesn't match pnpm-lock.yaml: pnpm install --frozen-lockfile"],
})
}
if (!notes) {
problems.push({missing: [`CHANGELOG.md has no "# ${version}" section`]})
}

View file

@ -1,4 +1,4 @@
import {existsSync} from "node:fs"
import {existsSync, readFileSync} from "node:fs"
import {join, resolve} from "node:path"
import {followUps, missingEnv, root} from "../lib/context.mjs"
import {installed, run} from "../lib/shell.mjs"
@ -6,12 +6,29 @@ import {installed, run} from "../lib/shell.mjs"
const targets = {darwin: ["macos"], linux: ["linux", "windows"]}[process.platform] ?? []
const docker = process.env.DOCKER || "docker"
// Optional packages for other platforms are in the lockfile but never installed
const electronInstalled = () => {
const path = join(root, "electron/node_modules/.package-lock.json")
if (!existsSync(path)) {
return false
}
const {packages: wanted} = JSON.parse(readFileSync(join(root, "electron/package-lock.json")))
const {packages: installed} = JSON.parse(readFileSync(path))
return Object.entries(wanted).every(
([key, {version, optional}]) =>
!key || installed[key]?.version === version || (optional && !installed[key]),
)
}
export default {
name: "desktop",
title: "Package the desktop app",
missing: () => [
...(targets.length > 0 ? [] : [`desktop packaging on ${process.platform}`]),
...(existsSync(join(root, "electron/node_modules")) ? [] : ["electron dependencies"]),
...(electronInstalled() ? [] : ["electron dependencies that match electron/package-lock.json"]),
...(targets.includes("windows") && !installed(docker) ? [docker] : []),
...(targets.includes("macos")
? missingEnv("CSC_NAME", "ASC_KEY_ID", "ASC_ISSUER_ID", "ASC_KEY_PATH")