修: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:
admin committed 2026-10-08 22:29:52 +08:00
1 parent e03465c398
commit 237a09a5b0
161 files changed
+19429 -2

No files matched your search

@@ -0,0 +1,60 @@
import assert from 'node:assert/strict';
import { test } from 'node:test';
import { mkdtemp, mkdir, writeFile, rm, readFile } from 'node:fs/promises';
import path from 'node:path';
import { tmpdir } from 'node:os';
import { fileURLToPath } from 'node:url';
import { loadProfile, prepareProfile, profileStatus } from '../src/profile.ts';
import { loadManifest, type CredentialBackend } from '../src/config.ts';
import { startServer } from '../src/server.ts';
test('真实分发配置:正式页面保存后可被对应业务环境读取,状态与参数不含值', async () => {
const root = fileURLToPath(new URL('../', import.meta.url));
const config = JSON.parse(await readFile(path.join(root, 'manifests/profiles.json'), 'utf8'));
for (const name of Object.keys(config.profiles)) {
const bindings = await loadProfile(name);
const manifests = await Promise.all(bindings.map(b => loadManifest(b.manifest)));
const values = new Map<string, string>();
const backendFactory = async (ref: string): Promise<CredentialBackend> => ({
name: 'fake', get: async () => values.get(ref),
set: async value => { values.set(ref, value); }, delete: async () => { values.delete(ref); },
});
const app = await startServer({ manifests, backends: await Promise.all(manifests.map(m => backendFactory(m.credential))) });
try {
const headers: Record<string, string> = { Origin: app.origin, Authorization: 'Bearer ' + app.bootstrap, 'X-Local-Request': '1', 'Content-Type': 'application/json' };
const session = await fetch(app.origin + '/api/session', { method: 'POST', headers });
headers.Cookie = session.headers.get('set-cookie')!.split(';')[0];
assert.equal((await fetch(app.origin)).status, 200);
const meta = await (await fetch(app.origin + '/api/meta', { headers })).json();
const saved = await fetch(app.origin + '/api/save', { method: 'POST', headers, body: JSON.stringify({ entries: meta.fields.map((f: { credential: string; revision: string }, i: number) => ({ credential: f.credential, revision: f.revision, value: 'TEST_ONLY_PROFILE_' + i })) }) });
assert.equal((await saved.json()).status, 'saved');
const status = await profileStatus(bindings, {}, async ref => values.get(ref));
assert.equal(status.configured, true);
assert.equal(JSON.stringify(status).includes('TEST_ONLY'), false);
const plan = await prepareProfile(bindings, ['business-program', '--input', 'a b'], {}, async ref => values.get(ref));
for (const b of bindings) assert.match(plan.env[b.env]!, /^TEST_ONLY_PROFILE_/);
assert.equal(JSON.stringify(plan.args).includes('TEST_ONLY'), false);
assert.deepEqual(plan.args, ['--input', 'a b']);
} finally { app.close(); }
}
});
test('已有环境凭据无需读取系统库;缺失或后端失败时不启动业务', async () => {
const bindings = await loadProfile('default');
const env = Object.fromEntries(bindings.map(b => [b.env, 'TEST_ONLY_ENV']));
const noRead = async () => { throw Error('TEST_ONLY_FAILURE'); };
assert.equal((await profileStatus(bindings, env, noRead)).configured, true);
assert.equal((await prepareProfile(bindings, ['business'], env, noRead)).env[bindings[0].env], 'TEST_ONLY_ENV');
await assert.rejects(prepareProfile(bindings, ['business'], {}, async () => undefined), /未配置/);
await assert.rejects(prepareProfile(bindings, ['business'], {}, noRead), error => error instanceof Error && !error.message.includes('TEST_ONLY'));
});
test('配置拒绝跨目录声明、危险变量与未知业务', async t => {
const dir = await mkdtemp(path.join(tmpdir(), 'credential-profile-')); t.after(() => rm(dir, { recursive: true, force: true }));
await mkdir(path.join(dir, 'manifests'));
for (const binding of [{ manifest: '../outside.json', env: 'API_KEY' }, { manifest: 'default.json', env: 'NODE_OPTIONS' }]) {
await writeFile(path.join(dir, 'manifests/profiles.json'), JSON.stringify({ version: 1, profiles: { default: [binding] } }));
await assert.rejects(loadProfile('default', dir));
}
await assert.rejects(loadProfile('unknown', dir));
});