修:draw-ui / oil-motion 原被当子模块指针收录 ⇒ 改为正常文件入库(两份内容原先对别人是空的)
一、问题(本轮实测)
`draw-ui` 与 `oil-motion` 目录里**各自带一个内嵌 `.git`** ⇒ 上一次提交把它们记成了 **gitlink(子模块指针)**
⇒ 仓库里只存了一个不属于任何远端的 commit id,**别人克隆下来这两份是空的** ✗(`git status` 显示 ` m draw-ui` / ` m oil-motion` = 子模块内容有改动)。
二、处置(可回退)
· 把两处的 `.git` **挪走**(⛔ 不是删除)⇒ `归档/内嵌git-20261008/{draw-ui,oil-motion}.git`;
· `git rm --cached` 掉那两个 gitlink,再 `git add` 两个目录 ⇒ **按正常文件入库**(内容才真的进仓库)。
三、副作用(如实记)
挪走 `.git` 后,这两个技能**不能再原地 `git pull` 取上游更新**(要更新得重新拉一份覆盖);
如需恢复其本地仓库,把 `归档/内嵌git-20261008/` 里的 `.git` 挪回原处即可。
This commit is contained in:
1 parent
e03465c398
commit
237a09a5b0
161 files changed
+19429
-2
No files matched your search
@@ -0,0 +1,96 @@
|
||||
import { randomBytes } from 'node:crypto';
|
||||
import { lstat, mkdir, open, readFile, rename, unlink } from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url';
|
||||
import { loadManifest, nativeBackend, PublicError, validateManifest, type Manifest } from './config.ts';
|
||||
import { loadPage, validateFields, validatePageUI } from './page.ts';
|
||||
|
||||
async function writeConfiguration(file: string, configuration: object) {
|
||||
await mkdir(path.dirname(file), { recursive: true });
|
||||
const temporary = file + '.' + randomBytes(8).toString('hex') + '.tmp';
|
||||
try {
|
||||
const handle = await open(temporary, 'wx', 0o600);
|
||||
try { await handle.writeFile(JSON.stringify(configuration, null, 2) + '\n', 'utf8'); await handle.sync(); }
|
||||
finally { await handle.close(); }
|
||||
await rename(temporary, file);
|
||||
} finally { await unlink(temporary).catch(() => {}); }
|
||||
}
|
||||
export async function configurePage(file: string, manifests: string[], options: Record<string, string>, dryRun = false) {
|
||||
if (Object.keys(options).some(key => !['title', 'label', 'save-label'].includes(key))) throw new PublicError('存在不支持的页面配置项。');
|
||||
let existing: { manifests: string[]; ui?: Record<string, string> } | undefined;
|
||||
try {
|
||||
const stat = await lstat(file);
|
||||
if (!stat.isFile() || stat.isSymbolicLink() || stat.nlink > 1) throw new PublicError('页面配置必须是独立的普通文件。');
|
||||
await loadPage(file);
|
||||
existing = JSON.parse(await readFile(file, 'utf8'));
|
||||
} catch (error) { if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error; }
|
||||
// 命令参数按当前目录解析;写入页面文件后统一保存为相对该文件的路径。
|
||||
const paths = manifests.length ? manifests.map(item => path.relative(path.dirname(file), path.resolve(item)).split(path.sep).join('/')) : existing?.manifests ?? [];
|
||||
validateFields(await Promise.all(paths.map(item => loadManifest(path.resolve(path.dirname(file), item)))));
|
||||
const ui = { ...existing?.ui };
|
||||
for (const [flag, key] of [['title', 'title'], ['label', 'label'], ['save-label', 'saveLabel']])
|
||||
if (options[flag] !== undefined) ui[key] = options[flag];
|
||||
const configuration = { version: 1, manifests: paths, ui: validatePageUI(ui) };
|
||||
if (!dryRun) await writeConfiguration(file, configuration);
|
||||
return { status: dryRun ? 'preview' : 'configured', page: file, configuration };
|
||||
}
|
||||
|
||||
export async function configureManifest(file: string, options: Record<string, string>, dryRun = false) {
|
||||
const allowed = ['id', 'label', 'credential', 'title', 'placeholder', 'save-label'];
|
||||
if (Object.keys(options).some(k => !allowed.includes(k))) throw new PublicError('存在不支持的配置项。');
|
||||
let existing: Manifest | undefined;
|
||||
try {
|
||||
const stat = await lstat(file);
|
||||
if (!stat.isFile() || stat.isSymbolicLink() || stat.nlink > 1) throw new PublicError('声明必须是独立的普通文件。');
|
||||
existing = await loadManifest(file);
|
||||
} catch (error) { if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error; }
|
||||
if (existing && ((options.id && options.id !== existing.id) || (options.credential && options.credential !== existing.credential)))
|
||||
throw new PublicError('已有声明的身份和凭据引用不可改写。请为新凭据指定新的 --manifest 文件。');
|
||||
const draft: Record<string, unknown> = { version: 1, ...existing };
|
||||
for (const key of ['id', 'label', 'credential']) if (options[key] !== undefined) draft[key] = options[key];
|
||||
const ui = { ...existing?.ui };
|
||||
for (const [flag, key] of [['title', 'title'], ['placeholder', 'placeholder'], ['save-label', 'saveLabel']] as const)
|
||||
if (options[flag] !== undefined) ui[key] = options[flag];
|
||||
if (Object.keys(ui).length) draft.ui = ui;
|
||||
const manifest = validateManifest(draft);
|
||||
if (!dryRun) await writeConfiguration(file, manifest);
|
||||
return { status: dryRun ? 'preview' : 'configured', manifest: file, configuration: manifest };
|
||||
}
|
||||
async function main() {
|
||||
const [command, ...args] = process.argv.slice(2);
|
||||
if (!command || command === '--help') {
|
||||
process.stdout.write('configure [--manifest 文件] --id 标识 --label 用途 --credential 引用 [--title 标题] [--placeholder 占位文字] [--save-label 按钮文字] [--dry-run]\nconfigure-page --page 文件 [--manifest 声明(可重复,替换整组)] [--title 标题] [--label 用途] [--save-label 按钮文字] [--dry-run]\nstatus [--manifest 文件]\n');
|
||||
return;
|
||||
}
|
||||
const flags: Record<string, string> = {};
|
||||
const manifests: string[] = [];
|
||||
let dryRun = false;
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
if (args[i] === '--dry-run' && ['configure', 'configure-page'].includes(command)) { dryRun = true; continue; }
|
||||
if (command === 'configure-page' && args[i] === '--manifest' && args[i + 1] && !args[i + 1].startsWith('--')) { manifests.push(args[++i]); continue; }
|
||||
const name = args[i].replace(/^--/, '');
|
||||
if (!args[i].startsWith('--') || !args[i + 1] || args[i + 1].startsWith('--') || Object.hasOwn(flags, name))
|
||||
throw new PublicError('命令参数不正确。');
|
||||
flags[name] = args[++i];
|
||||
}
|
||||
if (command === 'configure-page') {
|
||||
if (!flags.page) throw new PublicError('请指定 --page 配置文件。');
|
||||
const file = path.resolve(flags.page); delete flags.page;
|
||||
process.stdout.write(JSON.stringify(await configurePage(file, manifests, flags, dryRun)) + '\n'); return;
|
||||
}
|
||||
const file = path.resolve(flags.manifest ?? fileURLToPath(new URL('../manifests/default.json', import.meta.url)));
|
||||
delete flags.manifest;
|
||||
if (command === 'configure') process.stdout.write(JSON.stringify(await configureManifest(file, flags, dryRun)) + '\n');
|
||||
else if (command === 'status' && !Object.keys(flags).length) {
|
||||
const m = await loadManifest(file);
|
||||
const backend = await nativeBackend(m.credential);
|
||||
const configured = (await backend.get()) !== undefined;
|
||||
process.stdout.write(JSON.stringify({ id: m.id, credential: m.credential, configured, storage: backend.name }) + '\n');
|
||||
if (!configured) process.exitCode = 2;
|
||||
} else throw new PublicError('命令不支持,请使用 --help。');
|
||||
}
|
||||
if (process.argv[1] && pathToFileURL(path.resolve(process.argv[1])).href === import.meta.url)
|
||||
main().catch(error => {
|
||||
process.stderr.write((error instanceof PublicError ? error.message : '配置操作未完成,请检查文件位置与权限。') + '\n');
|
||||
process.exitCode = 1;
|
||||
});
|
||||
Reference in new issue
Block a user