feat(desktop): embed BoosAPI Desktop client as a subproject
Vendor the rebranded BoosAPI Desktop client (Tauri + Vue) into metapi as a monorepo subdirectory under desktop/. Source only — node_modules, dist, Cargo target and the upstream .git history are excluded. desktop/ keeps its own independent Cargo workspace (desktop/src-tauri with crates/*); it is not part of the backend boosapi crate, so a `cargo check` at the metapi root still compiles only the backend. Build the desktop app from desktop/src-tauri per desktop/README.md. Note: app icons are still placeholder assets pending the final BoosAPI logo. The updater signing keys live outside the repo and are not included.
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
#!/usr/bin/env node
|
||||
import { readFileSync, writeFileSync } from 'fs';
|
||||
import { resolve, dirname } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { execFileSync } from 'child_process';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const root = resolve(__dirname, '..');
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
const flags = new Set(args.filter(a => a.startsWith('--')));
|
||||
const positional = args.filter(a => !a.startsWith('--'));
|
||||
const autoPush = flags.has('--push');
|
||||
|
||||
const version = positional[0];
|
||||
if (!version) {
|
||||
console.error('用法: pnpm bump [--push] <version>');
|
||||
console.error('示例: pnpm bump 0.0.11');
|
||||
console.error(' pnpm bump --push 0.0.11 (自动 push commit 和 tag)');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(version)) {
|
||||
console.error(`无效版本号: ${version}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const files = [
|
||||
'package.json',
|
||||
'src-tauri/tauri.conf.json',
|
||||
];
|
||||
|
||||
const tag = `v${version}`;
|
||||
|
||||
function git(args, options = {}) {
|
||||
return execFileSync('git', args, { cwd: root, stdio: 'inherit', ...options });
|
||||
}
|
||||
|
||||
function gitOutput(args) {
|
||||
return execFileSync('git', args, { cwd: root, encoding: 'utf-8' }).trim();
|
||||
}
|
||||
|
||||
const tagExists = (() => {
|
||||
try {
|
||||
gitOutput(['rev-parse', '--verify', '--quiet', `refs/tags/${tag}`]);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
})();
|
||||
|
||||
const currentVersions = files.map((rel) => {
|
||||
const filepath = resolve(root, rel);
|
||||
const json = JSON.parse(readFileSync(filepath, 'utf-8'));
|
||||
return { rel, filepath, json, old: json.version };
|
||||
});
|
||||
|
||||
if (tagExists && currentVersions.some(({ old }) => old !== version)) {
|
||||
console.error(`tag ${tag} 已存在,但版本文件尚未全部更新到 ${version},已停止以避免覆盖已有发布标签。`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let changed = false;
|
||||
|
||||
for (const { rel, filepath, json, old } of currentVersions) {
|
||||
if (old === version) {
|
||||
console.log(`⏭️ ${rel}: 已是 ${version}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
json.version = version;
|
||||
writeFileSync(filepath, JSON.stringify(json, null, 2) + '\n');
|
||||
console.log(`✅ ${rel}: ${old} → ${version}`);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
console.log(`\n版本检查完成: ${version}`);
|
||||
|
||||
if (changed) {
|
||||
git(['add', ...files]);
|
||||
|
||||
try {
|
||||
git(['diff', '--cached', '--quiet', '--', ...files], { stdio: 'ignore' });
|
||||
console.log('\n版本文件没有产生 Git diff,跳过 commit。');
|
||||
} catch {
|
||||
git(['commit', '-m', `chore(version): bump version to ${tag}`]);
|
||||
}
|
||||
} else {
|
||||
console.log('\n版本文件没有变化,跳过 commit。');
|
||||
}
|
||||
|
||||
if (tagExists) {
|
||||
console.log(`🏷️ tag 已存在,跳过创建: ${tag}`);
|
||||
} else {
|
||||
git(['tag', tag]);
|
||||
console.log(`🏷️ 已创建 tag: ${tag}`);
|
||||
}
|
||||
|
||||
if (autoPush) {
|
||||
git(['push']);
|
||||
git(['push', '--tags']);
|
||||
console.log(`\n🚀 已推送 commit 和 tag: ${tag}`);
|
||||
} else {
|
||||
console.log(`📌 执行 git push && git push --tags 即可触发 release`);
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
import { spawn } from "node:child_process";
|
||||
import { existsSync, readdirSync, rmSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const repoRoot = path.resolve(__dirname, "..");
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
function isBundleCommand() {
|
||||
return args[0] === "build" || args[0] === "bundle";
|
||||
}
|
||||
|
||||
function cleanStaleDmgTempFiles() {
|
||||
const macosBundleDir = path.join(repoRoot, "src-tauri", "target", "release", "bundle", "macos");
|
||||
if (!existsSync(macosBundleDir)) return;
|
||||
|
||||
for (const entry of readdirSync(macosBundleDir)) {
|
||||
if (/^rw\..+\.dmg$/.test(entry)) {
|
||||
rmSync(path.join(macosBundleDir, entry), { force: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function hasConfigOverride() {
|
||||
return args.some((arg) => arg === "--config" || arg === "-c" || arg.startsWith("--config="));
|
||||
}
|
||||
|
||||
const env = { ...process.env };
|
||||
|
||||
if (isBundleCommand()) {
|
||||
cleanStaleDmgTempFiles();
|
||||
if (!env.TAURI_BUNDLER_DMG_IGNORE_CI) {
|
||||
env.CI = "true";
|
||||
}
|
||||
if (!env.TAURI_SIGNING_PRIVATE_KEY && !hasConfigOverride()) {
|
||||
args.push("--config", JSON.stringify({ bundle: { createUpdaterArtifacts: false } }));
|
||||
}
|
||||
}
|
||||
|
||||
const tauriCliScript = path.join(
|
||||
repoRoot,
|
||||
"node_modules",
|
||||
"@tauri-apps",
|
||||
"cli",
|
||||
"tauri.js",
|
||||
);
|
||||
|
||||
const child = spawn(process.execPath, [tauriCliScript, ...args], {
|
||||
cwd: repoRoot,
|
||||
env,
|
||||
stdio: "inherit",
|
||||
});
|
||||
|
||||
child.on("error", (error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
child.on("exit", (code, signal) => {
|
||||
if (signal) {
|
||||
process.kill(process.pid, signal);
|
||||
return;
|
||||
}
|
||||
process.exit(code ?? 1);
|
||||
});
|
||||
Reference in New Issue
Block a user