From a01cc80202aba8852e9e9d6005db015b1fc4f89b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E6=97=A5=E5=A4=A9?= Date: Thu, 2 Apr 2026 10:12:20 +0800 Subject: [PATCH] Sync Cargo.lock in version updates --- .githooks/sync-versions.ts | 126 ++++++++++++++++++++++++++ Cargo.lock | 12 +-- Cargo.toml | 2 +- cli/npm/darwin-arm64/package.json | 2 +- cli/npm/darwin-x64/package.json | 2 +- cli/npm/linux-arm64-gnu/package.json | 2 +- cli/npm/linux-x64-gnu/package.json | 2 +- cli/npm/win32-x64-msvc/package.json | 2 +- cli/package.json | 2 +- doc/package.json | 2 +- gui/package.json | 2 +- gui/src-tauri/Cargo.toml | 2 +- gui/src-tauri/tauri.conf.json | 2 +- libraries/logger/package.json | 2 +- libraries/logger/src/lib.rs | 42 +++++++-- libraries/md-compiler/package.json | 2 +- libraries/script-runtime/package.json | 2 +- mcp/package.json | 2 +- package.json | 5 +- scripts/install-rust-deps.ts | 60 ++++++++++++ sdk/package.json | 2 +- 21 files changed, 247 insertions(+), 30 deletions(-) create mode 100644 scripts/install-rust-deps.ts diff --git a/.githooks/sync-versions.ts b/.githooks/sync-versions.ts index 256f4058..b515d46b 100644 --- a/.githooks/sync-versions.ts +++ b/.githooks/sync-versions.ts @@ -114,6 +114,85 @@ function updateVersionLineInSection( return content } +function extractTomlSectionValue(content: string, sectionName: string, key: string): string | undefined { + const lines = content.split(/\r?\n/) + let inTargetSection = false + + for (const line of lines) { + const trimmed = line.trim() + + if (/^\[.*\]$/.test(trimmed)) { + inTargetSection = trimmed === `[${sectionName}]` + continue + } + + if (!inTargetSection) { + continue + } + + const match = trimmed.match(new RegExp(`^${key}\\s*=\\s*"([^"]+)"$`, 'u')) + if (match != null) { + return match[1] + } + } + + return undefined +} + +function updateCargoLockPackageVersions( + content: string, + packageNames: ReadonlySet, + targetVersion: string, +): string { + const lines = content.split(/\r?\n/) + let currentPackageName: string | undefined + let inPackageBlock = false + + for (let index = 0; index < lines.length; index += 1) { + const line = lines[index] + const trimmed = line.trim() + + if (trimmed === '[[package]]') { + inPackageBlock = true + currentPackageName = undefined + continue + } + + if (/^\[\[.+\]\]$|^\[.+\]$/u.test(trimmed)) { + inPackageBlock = false + currentPackageName = undefined + continue + } + + if (!inPackageBlock) { + continue + } + + const nameMatch = trimmed.match(/^name\s*=\s*"([^"]+)"$/u) + if (nameMatch != null) { + currentPackageName = nameMatch[1] + continue + } + + if (currentPackageName == null || !packageNames.has(currentPackageName)) { + continue + } + + const versionMatch = line.match(/^(\s*version\s*=\s*")([^"]+)(".*)$/u) + if (versionMatch == null) { + continue + } + + if (versionMatch[2] === targetVersion) { + continue + } + + lines[index] = `${versionMatch[1]}${targetVersion}${versionMatch[3]}` + } + + return lines.join('\n') +} + function runGit(rootDir: string, args: readonly string[]): string { return execFileSync('git', args, { cwd: rootDir, @@ -168,6 +247,49 @@ function syncCargoVersion( } } +function collectCargoPackageNames(cargoTomlPaths: readonly string[]): Set { + const packageNames = new Set() + + for (const filePath of cargoTomlPaths) { + try { + const content = readFileSync(filePath, 'utf-8') + const packageName = extractTomlSectionValue(content, 'package', 'name') + if (packageName != null && packageName !== '') { + packageNames.add(packageName) + } + } catch { + console.log(`⚠️ ${filePath} not found or invalid, skipping`) + } + } + + return packageNames +} + +function syncCargoLockVersion( + filePath: string, + packageNames: ReadonlySet, + targetVersion: string, + changedPaths: Set, +): void { + if (packageNames.size === 0) { + return + } + + try { + const originalContent = readFileSync(filePath, 'utf-8') + const updatedContent = updateCargoLockPackageVersions(originalContent, packageNames, targetVersion) + + if (updatedContent === originalContent) { + return + } + + writeFileSync(filePath, updatedContent, 'utf-8') + changedPaths.add(filePath) + } catch { + console.log(`⚠️ ${filePath} not found or invalid, skipping`) + } +} + function getStagedPackageVersionCandidates(rootDir: string, rootVersion: string): Map { const stagedFiles = runGit(rootDir, ['diff', '--cached', '--name-only', '--diff-filter=ACMR']) .split(/\r?\n/) @@ -253,6 +375,7 @@ export function runSyncVersions(options: SyncVersionsOptions = {}): SyncVersions const rootDir = resolve(options.rootDir ?? '.') const rootPackagePath = resolve(rootDir, 'package.json') const rootCargoPath = resolve(rootDir, 'Cargo.toml') + const cargoLockPath = resolve(rootDir, 'Cargo.lock') const rootPkg = readJsonFile(rootPackagePath) const currentRootVersion = typeof rootPkg.version === 'string' ? rootPkg.version.trim() : '' @@ -284,11 +407,14 @@ export function runSyncVersions(options: SyncVersionsOptions = {}): SyncVersions const cargoTomlPaths = discoverFilesByName(rootDir, 'Cargo.toml') .filter(filePath => resolve(filePath) !== rootCargoPath) .sort() + const cargoPackageNames = collectCargoPackageNames([rootCargoPath, ...cargoTomlPaths]) for (const filePath of cargoTomlPaths) { syncCargoVersion(filePath, 'package', target.version, changedPaths) } + syncCargoLockVersion(cargoLockPath, cargoPackageNames, target.version, changedPaths) + for (const filePath of discoverFilesByName(rootDir, 'tauri.conf.json').sort()) { syncJsonVersion(filePath, target.version, changedPaths) } diff --git a/Cargo.lock b/Cargo.lock index 7df64df3..e7445a37 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2147,7 +2147,7 @@ dependencies = [ [[package]] name = "memory-sync-gui" -version = "2026.10402.103" +version = "2026.10402.110" dependencies = [ "dirs", "proptest", @@ -4436,7 +4436,7 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tnmsc" -version = "2026.10402.103" +version = "2026.10402.110" dependencies = [ "clap", "dirs", @@ -4458,7 +4458,7 @@ dependencies = [ [[package]] name = "tnmsc-cli-shell" -version = "2026.10402.103" +version = "2026.10402.110" dependencies = [ "clap", "serde_json", @@ -4468,7 +4468,7 @@ dependencies = [ [[package]] name = "tnmsc-logger" -version = "2026.10402.103" +version = "2026.10402.110" dependencies = [ "napi", "napi-build", @@ -4479,7 +4479,7 @@ dependencies = [ [[package]] name = "tnmsc-md-compiler" -version = "2026.10402.103" +version = "2026.10402.110" dependencies = [ "markdown", "napi", @@ -4494,7 +4494,7 @@ dependencies = [ [[package]] name = "tnmsc-script-runtime" -version = "2026.10402.103" +version = "2026.10402.110" dependencies = [ "napi", "napi-build", diff --git a/Cargo.toml b/Cargo.toml index 92ea6e74..9069b5fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ members = [ ] [workspace.package] -version = "2026.10402.109" +version = "2026.10402.110" edition = "2024" rust-version = "1.88" license = "AGPL-3.0-only" diff --git a/cli/npm/darwin-arm64/package.json b/cli/npm/darwin-arm64/package.json index b00c876a..0381c4ed 100644 --- a/cli/npm/darwin-arm64/package.json +++ b/cli/npm/darwin-arm64/package.json @@ -1,6 +1,6 @@ { "name": "@truenine/memory-sync-cli-darwin-arm64", - "version": "2026.10402.109", + "version": "2026.10402.110", "os": [ "darwin" ], diff --git a/cli/npm/darwin-x64/package.json b/cli/npm/darwin-x64/package.json index 1fa29888..d37d6c1d 100644 --- a/cli/npm/darwin-x64/package.json +++ b/cli/npm/darwin-x64/package.json @@ -1,6 +1,6 @@ { "name": "@truenine/memory-sync-cli-darwin-x64", - "version": "2026.10402.109", + "version": "2026.10402.110", "os": [ "darwin" ], diff --git a/cli/npm/linux-arm64-gnu/package.json b/cli/npm/linux-arm64-gnu/package.json index eab4b734..cea698d9 100644 --- a/cli/npm/linux-arm64-gnu/package.json +++ b/cli/npm/linux-arm64-gnu/package.json @@ -1,6 +1,6 @@ { "name": "@truenine/memory-sync-cli-linux-arm64-gnu", - "version": "2026.10402.109", + "version": "2026.10402.110", "os": [ "linux" ], diff --git a/cli/npm/linux-x64-gnu/package.json b/cli/npm/linux-x64-gnu/package.json index 6b3d07ec..ec2798af 100644 --- a/cli/npm/linux-x64-gnu/package.json +++ b/cli/npm/linux-x64-gnu/package.json @@ -1,6 +1,6 @@ { "name": "@truenine/memory-sync-cli-linux-x64-gnu", - "version": "2026.10402.109", + "version": "2026.10402.110", "os": [ "linux" ], diff --git a/cli/npm/win32-x64-msvc/package.json b/cli/npm/win32-x64-msvc/package.json index 4607f49d..30435815 100644 --- a/cli/npm/win32-x64-msvc/package.json +++ b/cli/npm/win32-x64-msvc/package.json @@ -1,6 +1,6 @@ { "name": "@truenine/memory-sync-cli-win32-x64-msvc", - "version": "2026.10402.109", + "version": "2026.10402.110", "os": [ "win32" ], diff --git a/cli/package.json b/cli/package.json index 8874d3e1..e3721f61 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,7 +1,7 @@ { "name": "@truenine/memory-sync-cli", "type": "module", - "version": "2026.10402.109", + "version": "2026.10402.110", "description": "TrueNine Memory Synchronization CLI shell", "author": "TrueNine", "license": "AGPL-3.0-only", diff --git a/doc/package.json b/doc/package.json index 2c262cc2..cd487cdf 100644 --- a/doc/package.json +++ b/doc/package.json @@ -1,6 +1,6 @@ { "name": "@truenine/memory-sync-docs", - "version": "2026.10402.109", + "version": "2026.10402.110", "private": true, "description": "Chinese-first manifesto-led documentation site for @truenine/memory-sync.", "engines": { diff --git a/gui/package.json b/gui/package.json index 2506eadf..62cf1f13 100644 --- a/gui/package.json +++ b/gui/package.json @@ -1,6 +1,6 @@ { "name": "@truenine/memory-sync-gui", - "version": "2026.10402.109", + "version": "2026.10402.110", "private": true, "engines": { "node": ">=25.2.1", diff --git a/gui/src-tauri/Cargo.toml b/gui/src-tauri/Cargo.toml index 0abd2cd8..28d37dc2 100644 --- a/gui/src-tauri/Cargo.toml +++ b/gui/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "memory-sync-gui" -version = "2026.10402.109" +version = "2026.10402.110" description = "Memory Sync desktop GUI application" authors.workspace = true edition.workspace = true diff --git a/gui/src-tauri/tauri.conf.json b/gui/src-tauri/tauri.conf.json index 1a2c668a..89e5b11e 100644 --- a/gui/src-tauri/tauri.conf.json +++ b/gui/src-tauri/tauri.conf.json @@ -1,6 +1,6 @@ { "$schema": "https://schema.tauri.app/config/2", - "version": "2026.10402.109", + "version": "2026.10402.110", "productName": "Memory Sync", "identifier": "org.truenine.memory-sync", "build": { diff --git a/libraries/logger/package.json b/libraries/logger/package.json index 5ef000c0..fa14ed21 100644 --- a/libraries/logger/package.json +++ b/libraries/logger/package.json @@ -1,7 +1,7 @@ { "name": "@truenine/logger", "type": "module", - "version": "2026.10402.109", + "version": "2026.10402.110", "private": true, "description": "Rust-powered AI-friendly Markdown logger for Node.js via N-API", "license": "AGPL-3.0-only", diff --git a/libraries/logger/src/lib.rs b/libraries/logger/src/lib.rs index 14f5e82d..f4e87f23 100644 --- a/libraries/logger/src/lib.rs +++ b/libraries/logger/src/lib.rs @@ -692,13 +692,9 @@ fn output_worker(receiver: Receiver) { match command { OutputCommand::Write { use_stderr, output } => { if use_stderr { - if stderr_writer.write_all(output.as_bytes()).is_ok() { - let _ = stderr_writer.write_all(b"\n"); - } + let _ = write_output_line(&mut stderr_writer, &output); } else { - if stdout_writer.write_all(output.as_bytes()).is_ok() { - let _ = stdout_writer.write_all(b"\n"); - } + let _ = write_output_line(&mut stdout_writer, &output); } } OutputCommand::Flush { ack } => { @@ -713,6 +709,12 @@ fn output_worker(receiver: Receiver) { let _ = stderr_writer.flush(); } +fn write_output_line(writer: &mut impl Write, output: &str) -> std::io::Result<()> { + writer.write_all(output.as_bytes())?; + writer.write_all(b"\n")?; + writer.flush() +} + fn print_output_direct(use_stderr: bool, output: &str) { if use_stderr { let mut stderr = std::io::stderr().lock(); @@ -1222,4 +1224,32 @@ mod tests { assert_eq!(drained.len(), 1); assert_eq!(drained[0].code, "BUFFERED_WARN"); } + + #[derive(Default)] + struct FlushTrackingWriter { + writes: Vec, + flush_count: usize, + } + + impl Write for FlushTrackingWriter { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.writes.extend_from_slice(buf); + Ok(buf.len()) + } + + fn flush(&mut self) -> std::io::Result<()> { + self.flush_count += 1; + Ok(()) + } + } + + #[test] + fn test_write_output_line_flushes_each_message() { + let mut writer = FlushTrackingWriter::default(); + + write_output_line(&mut writer, "**INFO** `logger-test` hello").unwrap(); + + assert_eq!(String::from_utf8(writer.writes).unwrap(), "**INFO** `logger-test` hello\n"); + assert_eq!(writer.flush_count, 1); + } } diff --git a/libraries/md-compiler/package.json b/libraries/md-compiler/package.json index 895ae624..fd8a5f69 100644 --- a/libraries/md-compiler/package.json +++ b/libraries/md-compiler/package.json @@ -1,7 +1,7 @@ { "name": "@truenine/md-compiler", "type": "module", - "version": "2026.10402.109", + "version": "2026.10402.110", "private": true, "description": "Rust-powered MDX→Markdown compiler for Node.js with pure-TS fallback", "license": "AGPL-3.0-only", diff --git a/libraries/script-runtime/package.json b/libraries/script-runtime/package.json index cb368ff1..fc463844 100644 --- a/libraries/script-runtime/package.json +++ b/libraries/script-runtime/package.json @@ -1,7 +1,7 @@ { "name": "@truenine/script-runtime", "type": "module", - "version": "2026.10402.109", + "version": "2026.10402.110", "private": true, "description": "Rust-backed TypeScript proxy runtime for tnmsc", "license": "AGPL-3.0-only", diff --git a/mcp/package.json b/mcp/package.json index 72d5e020..c63ddc6a 100644 --- a/mcp/package.json +++ b/mcp/package.json @@ -1,7 +1,7 @@ { "name": "@truenine/memory-sync-mcp", "type": "module", - "version": "2026.10402.109", + "version": "2026.10402.110", "description": "MCP stdio server for managing memory-sync prompt sources and translation artifacts", "author": "TrueNine", "license": "AGPL-3.0-only", diff --git a/package.json b/package.json index b2bd5055..0fdc56f3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@truenine/memory-sync", - "version": "2026.10402.109", + "version": "2026.10402.110", "description": "Cross-AI-tool prompt synchronisation toolkit (CLI + Tauri desktop GUI) — one ruleset, multi-target adaptation. Monorepo powered by pnpm + Turbo.", "license": "AGPL-3.0-only", "keywords": [ @@ -37,9 +37,10 @@ "dev:doc": "pnpm -C doc dev", "build:doc": "pnpm -C doc build", "sync-versions": "tsx .githooks/sync-versions.ts", + "install:rust-deps": "tsx scripts/install-rust-deps.ts", "build:native": "tsx scripts/build-native.ts", "build:native:copy": "tsx scripts/copy-napi.ts", - "postinstall": "simple-git-hooks && pnpm run build:native" + "postinstall": "simple-git-hooks && pnpm run install:rust-deps && pnpm run build:native" }, "author": { "email": "truenine304520@gmail.com", diff --git a/scripts/install-rust-deps.ts b/scripts/install-rust-deps.ts new file mode 100644 index 00000000..e4c0993b --- /dev/null +++ b/scripts/install-rust-deps.ts @@ -0,0 +1,60 @@ +#!/usr/bin/env tsx +import {execFileSync} from 'node:child_process' +import {existsSync} from 'node:fs' +import {homedir} from 'node:os' +import {dirname, join, resolve} from 'node:path' +import process from 'node:process' +import {fileURLToPath} from 'node:url' + +const __dirname = import.meta.dirname ?? dirname(fileURLToPath(import.meta.url)) +const root = resolve(__dirname, '..') + +function findCargo(): string | null { + const candidates: string[] = [ + process.env['CARGO'] ?? '', + join(homedir(), '.cargo', 'bin', 'cargo'), + join(homedir(), '.cargo', 'bin', 'cargo.exe'), + 'cargo', + ].filter(Boolean) + + for (const candidate of candidates) { + try { + if (candidate === 'cargo') { + execFileSync(candidate, ['--version'], {stdio: 'ignore'}) + return candidate + } + + if (existsSync(candidate)) return candidate + } catch {} + } + + return null +} + +const cargo = findCargo() +if (cargo == null) { + console.warn('[install-rust-deps] cargo not found, skipping Rust dependency fetch') + console.warn('[install-rust-deps] Install Rust: https://rustup.rs') + process.exit(0) +} + +const cargoDir = dirname(cargo) +const envWithCargo = { + ...process.env, + CARGO: cargo, + PATH: `${cargoDir}${process.platform === 'win32' ? ';' : ':'}${process.env['PATH'] ?? ''}`, +} + +console.log(`[install-rust-deps] Using cargo: ${cargo}`) + +try { + execFileSync(cargo, ['fetch', '--locked'], { + cwd: root, + env: envWithCargo, + stdio: 'inherit', + }) +} catch { + console.warn('[install-rust-deps] cargo fetch failed, continuing without prefetch') + console.warn('[install-rust-deps] Ensure Rust toolchain + network access are available, then rerun: cargo fetch --locked') + process.exit(0) +} diff --git a/sdk/package.json b/sdk/package.json index d9958753..68034fa6 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,7 +1,7 @@ { "name": "@truenine/memory-sync-sdk", "type": "module", - "version": "2026.10402.109", + "version": "2026.10402.110", "private": true, "description": "TrueNine Memory Synchronization SDK", "author": "TrueNine",