Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion quartz/cli/plugin-git-handlers.js
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,16 @@ function linkPeerPlugins(pluginDir) {
}
}

/**
* Whether a plugin's previously-installed lockfile record no longer matches
* its current config source (e.g. a pinned ref was bumped), meaning the
* on-disk plugin directory is stale and must be reinstalled rather than
* reused as-is.
*/
export function pluginSourceChanged(priorEntry, currentSource) {
return priorEntry !== undefined && priorEntry.source !== currentSource
}

/**
* Search installed plugins for one whose package.json "name" matches the given
* npm package name (e.g. "@quartz-community/bases-page").
Expand Down Expand Up @@ -506,7 +516,13 @@ export async function handlePluginInstallUnified({
.filter((entry) => {
const name = extractPluginName(entry.source)
const pluginDir = path.join(PLUGINS_DIR, name)
if (lockfile.plugins[name] && fs.existsSync(pluginDir)) return false
const priorEntry = lockfile.plugins[name]
if (
!pluginSourceChanged(priorEntry, entry.source) &&
priorEntry &&
fs.existsSync(pluginDir)
)
return false
const src = getSourceUrl(entry.source)
return (
src.startsWith("github:") ||
Expand Down Expand Up @@ -575,6 +591,17 @@ export async function handlePluginInstallUnified({
try {
const { name, url, ref, local, subdir } = parseGitSource(entry.source)
const pluginDir = path.join(PLUGINS_DIR, name)
const priorEntry = lockfile.plugins[name]

if (fs.existsSync(pluginDir) && pluginSourceChanged(priorEntry, entry.source)) {
console.log(
styleText(
"yellow",
`⚠ ${name} source changed (${formatSource(priorEntry.source)} → ${formatSource(entry.source)}), reinstalling`,
),
)
fs.rmSync(pluginDir, { recursive: true, force: true })
}

if (fs.existsSync(pluginDir)) {
if (local) {
Expand Down
30 changes: 30 additions & 0 deletions quartz/cli/plugin-git-handlers.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import test, { describe } from "node:test"
import assert from "node:assert"
import { pluginSourceChanged } from "./plugin-git-handlers.js"

describe("pluginSourceChanged", () => {
test("is false when there is no prior lockfile record", () => {
// First-ever install: nothing to compare against, so this alone must not
// force a reinstall of an unrelated pre-existing directory.
assert.strictEqual(pluginSourceChanged(undefined, "github:owner/repo#main"), false)
})

test("is false when the source is unchanged", () => {
const priorEntry = { source: "github:owner/repo#main", resolved: "...", commit: "abc123" }
assert.strictEqual(pluginSourceChanged(priorEntry, "github:owner/repo#main"), false)
})

test("is true when the pinned ref changed", () => {
// This is the case `plugin install --from-config` used to miss entirely:
// the directory and lockfile entry both already exist, so the plugin was
// treated as "already installed" and never re-cloned even though its
// configured ref moved to a different branch.
const priorEntry = { source: "github:owner/repo#old-branch", resolved: "...", commit: "abc123" }
assert.strictEqual(pluginSourceChanged(priorEntry, "github:owner/repo#new-branch"), true)
})

test("is true when the repo itself changed", () => {
const priorEntry = { source: "github:owner/old-repo", resolved: "...", commit: "abc123" }
assert.strictEqual(pluginSourceChanged(priorEntry, "github:owner/new-repo"), true)
})
})
Loading