sotashimozono_obsidian-remo.../plugin/scripts/dev-install.mjs
Souta 79aace49c3 fix(daemon): sha-verify on the fast path + keep the dev-daemon marker in E2E
Addresses /review-pr findings on #447 and the E2E smoke failure they caused.

- F1 (code-reviewer + silent-failure): the connect fast-path trusted
  existsSync alone, re-opening the "deploy an unverified binary" gap one layer
  above DaemonDownloader's fixed sha-check. Now records `daemonBinarySha` and
  re-hashes the cached file on the fast path; a post-write-corrupted binary
  fails the check and is re-fetched (network-free). Version + sha must both
  match to reuse.
- F2 (silent-failure): a saveSettings() failure after a successful download
  was caught by the generic "download failed -> SFTP" handler and misreported.
  Marker persistence is now its own non-fatal try — the verified binary is
  still returned; a persist failure just re-verifies next connect.
- F3 (comment-analyzer): corrected the dev-install marker comment (a stale
  binary is refreshed only when its sha differs, not "always on a bump").
- F4/F5 (pr-test): new ensureDaemonBinary.test.ts covers the locateDaemonBinary
  .dev-daemon gate and the fast-path reuse/skip (sha match, sha mismatch,
  version mismatch).

E2E fix: the new locateDaemonBinary `.dev-daemon` gate broke the Obsidian smoke
run — E2E stages the daemon via `build:server` (no dev-install), so the staged
binary was unmarked -> gated out -> no RPC -> the live-reflect specs failed.
build-server.mjs now writes the `.dev-daemon` marker; the E2E vault-scaffold
copies server-bin/ verbatim so it rides along. (Release daemons are built via
the Makefile, never marked — correct: users' downloads stay subject to the sha
refresh.)

tsc/lint/vitest green (1193). Still beta.14 (same PR #447).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 19:07:38 +09:00

84 lines
3.3 KiB
JavaScript

#!/usr/bin/env node
/*
* dev-install — copy build artifacts (main.js, manifest.json, styles.css)
* into the dev vault's plugins folder.
*
* Usage:
* node scripts/dev-install.mjs
* node scripts/dev-install.mjs <vault-root>
*
* The default vault root is read from the `REMOTE_SSH_DEV_VAULT` env var
* or falls back to `../../dev-vault` relative to this script (i.e. a
* sibling of the repo root, since the plugin lives under
* `<repo>/plugin/`). Set the env var in your shell rc to point at
* whatever vault you actually use for development.
*/
import * as fs from 'node:fs';
import * as path from 'node:path';
import { fileURLToPath } from 'node:url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const pluginRoot = path.resolve(__dirname, '..'); // <repo>/plugin
const repoRoot = path.resolve(pluginRoot, '..'); // <repo>
const vaultRoot = process.argv[2]
?? process.env.REMOTE_SSH_DEV_VAULT
?? path.resolve(repoRoot, '..', 'dev-vault');
const manifestPath = path.join(pluginRoot, 'manifest.json');
if (!fs.existsSync(manifestPath)) {
console.error(`manifest.json not found at ${manifestPath}`);
process.exit(1);
}
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
const targetDir = path.join(vaultRoot, '.obsidian', 'plugins', manifest.id);
if (!fs.existsSync(path.join(vaultRoot, '.obsidian'))) {
console.error(`Vault root does not look like an Obsidian vault: ${vaultRoot}`);
console.error(`(no .obsidian directory found)`);
process.exit(1);
}
fs.mkdirSync(targetDir, { recursive: true });
const files = ['main.js', 'manifest.json', 'styles.css'];
for (const f of files) {
const src = path.join(pluginRoot, f);
if (!fs.existsSync(src)) {
console.error(`missing build artifact: ${src} (run npm run build first)`);
process.exit(1);
}
const dst = path.join(targetDir, f);
fs.copyFileSync(src, dst);
console.log(`copied ${f} -> ${dst}`);
}
// Stage the obsidian-remote-server binaries (if any) so the plugin
// can ship one to the remote at connect time. Built by
// `npm run build:server`; missing means "no auto-deploy this run",
// not an error.
const serverBinDir = path.join(pluginRoot, 'server-bin');
if (fs.existsSync(serverBinDir)) {
const binTarget = path.join(targetDir, 'server-bin');
fs.mkdirSync(binTarget, { recursive: true });
for (const f of fs.readdirSync(serverBinDir)) {
const src = path.join(serverBinDir, f);
if (!fs.statSync(src).isFile()) continue;
const dst = path.join(binTarget, f);
fs.copyFileSync(src, dst);
console.log(`copied server-bin/${f} -> ${dst}`);
}
// Mark this as a DEV-staged daemon so the plugin trusts it over a
// downloaded binary: locateDaemonBinary returns a staged binary only when
// this marker is present. Without it, a *downloaded* binary at the same
// filename would masquerade as a dev build and skip the version/sha
// re-check (so a changed daemon would never get refreshed).
fs.writeFileSync(path.join(binTarget, '.dev-daemon'), '');
console.log('wrote server-bin/.dev-daemon marker');
} else {
console.log('server-bin/ not present — skipping daemon staging (run `npm run build:server` to build it)');
}
console.log(`\nplugin '${manifest.id}' v${manifest.version} installed to ${targetDir}`);
console.log('Reload the plugin in Obsidian (Settings → Community plugins → toggle off/on).');