修: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,166 @@
|
||||
#!/usr/bin/env python3
|
||||
"""保存并读取 Oil Motion 的本地配置。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import getpass
|
||||
import json
|
||||
import os
|
||||
import stat
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
import secure_credentials
|
||||
|
||||
|
||||
CONFIG_FILE_ENV = "OIL_MOTION_CONFIG_FILE"
|
||||
API_KEY_ENV = "ZENMUX_API_KEY"
|
||||
CREDENTIAL_REF = 'oil-motion/zenmux/default'
|
||||
|
||||
|
||||
def config_path() -> Path:
|
||||
override = os.environ.get(CONFIG_FILE_ENV, "").strip()
|
||||
if override:
|
||||
return Path(override).expanduser().resolve()
|
||||
config_home = os.environ.get("XDG_CONFIG_HOME", "").strip()
|
||||
root = Path(config_home).expanduser() if config_home else Path.home() / ".config"
|
||||
return root / "oil-motion" / "config.json"
|
||||
|
||||
|
||||
def read_config(path: Path | None = None) -> dict[str, Any]:
|
||||
target = path or config_path()
|
||||
if not target.exists():
|
||||
return {}
|
||||
try:
|
||||
value = json.loads(target.read_text(encoding="utf-8"))
|
||||
except json.JSONDecodeError as error:
|
||||
raise RuntimeError(f"配置文件不是有效的 JSON:{target}") from error
|
||||
if not isinstance(value, dict):
|
||||
raise RuntimeError(f"配置文件的根节点必须是对象:{target}")
|
||||
return value
|
||||
|
||||
|
||||
def configured_api_key(path: Path | None = None) -> tuple[str, str]:
|
||||
environment_key = os.environ.get(API_KEY_ENV, "").strip()
|
||||
if environment_key:
|
||||
return environment_key, API_KEY_ENV
|
||||
config = read_config(path)
|
||||
zenmux = config.get("zenmux")
|
||||
if isinstance(zenmux, dict):
|
||||
if zenmux.get('credential_ref'):
|
||||
key = secure_credentials.read(zenmux['credential_ref'])
|
||||
return (key, '系统凭据库') if key else ('', '')
|
||||
stored_key = zenmux.get("api_key")
|
||||
if isinstance(stored_key, str) and stored_key.strip():
|
||||
return stored_key.strip(), str(path or config_path())
|
||||
return "", ""
|
||||
|
||||
|
||||
def require_api_key(path: Path | None = None) -> str:
|
||||
api_key, _ = configured_api_key(path)
|
||||
if api_key:
|
||||
return api_key
|
||||
raise RuntimeError(
|
||||
"读取不到 ZenMux API Key。先运行 `node scripts/credential-ui/src/profile.ts status default`;"
|
||||
"未配置时用 `setup default` 打开本机页面由用户填写,"
|
||||
"再通过 `profile.ts run default -- python3 <生成脚本>` 运行生成命令。"
|
||||
)
|
||||
|
||||
|
||||
def write_config(config: dict[str, Any], path: Path | None = None) -> Path:
|
||||
target = path or config_path()
|
||||
target.parent.mkdir(parents=True, exist_ok=True, mode=0o700)
|
||||
try:
|
||||
target.parent.chmod(0o700)
|
||||
except OSError:
|
||||
pass
|
||||
temporary = target.with_suffix(f"{target.suffix}.tmp")
|
||||
temporary.write_text(
|
||||
json.dumps(config, ensure_ascii=False, indent=2) + "\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
try:
|
||||
temporary.chmod(stat.S_IRUSR | stat.S_IWUSR)
|
||||
except OSError:
|
||||
pass
|
||||
temporary.replace(target)
|
||||
return target
|
||||
|
||||
|
||||
def set_api_key(path: Path | None = None) -> int:
|
||||
key = getpass.getpass("ZenMux API Key:").strip()
|
||||
if not key:
|
||||
raise RuntimeError("API Key 不能为空")
|
||||
config = read_config(path)
|
||||
zenmux = config.get("zenmux")
|
||||
if not isinstance(zenmux, dict):
|
||||
zenmux = {}
|
||||
secure_credentials.save(CREDENTIAL_REF, key)
|
||||
zenmux.pop('api_key', None)
|
||||
zenmux['credential_ref'] = CREDENTIAL_REF
|
||||
config["zenmux"] = zenmux
|
||||
target = write_config(config, path)
|
||||
print(f"已保存:{target}")
|
||||
return 0
|
||||
|
||||
|
||||
def clear_api_key(path: Path | None = None) -> int:
|
||||
target = path or config_path()
|
||||
config = read_config(target)
|
||||
zenmux = config.get("zenmux")
|
||||
if isinstance(zenmux, dict):
|
||||
reference = zenmux.get('credential_ref')
|
||||
if reference:
|
||||
secure_credentials.delete(reference)
|
||||
zenmux.pop('credential_ref', None)
|
||||
zenmux.pop("api_key", None)
|
||||
if zenmux:
|
||||
config["zenmux"] = zenmux
|
||||
else:
|
||||
config.pop("zenmux", None)
|
||||
if config:
|
||||
write_config(config, target)
|
||||
elif target.exists():
|
||||
target.unlink()
|
||||
print("已清除 ZenMux API Key")
|
||||
return 0
|
||||
|
||||
|
||||
def show_status(path: Path | None = None) -> int:
|
||||
_, source = configured_api_key(path)
|
||||
if source:
|
||||
print(f"ZenMux API Key 已配置(来源:{source})")
|
||||
return 0
|
||||
print("ZenMux API Key 尚未配置")
|
||||
return 1
|
||||
|
||||
|
||||
def parser() -> argparse.ArgumentParser:
|
||||
result = argparse.ArgumentParser(description="管理 Oil Motion 的本地配置")
|
||||
subparsers = result.add_subparsers(dest="command", required=True)
|
||||
subparsers.add_parser("set", help="在隐藏输入框中保存 ZenMux API Key")
|
||||
subparsers.add_parser("status", help="检查 API Key 是否已经配置")
|
||||
subparsers.add_parser("clear", help="清除已经保存的 API Key")
|
||||
subparsers.add_parser("path", help="显示配置文件路径")
|
||||
return result
|
||||
|
||||
|
||||
def main() -> int:
|
||||
args = parser().parse_args()
|
||||
if args.command == "set":
|
||||
return set_api_key()
|
||||
if args.command == "status":
|
||||
return show_status()
|
||||
if args.command == "clear":
|
||||
return clear_api_key()
|
||||
print(config_path())
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
raise SystemExit(main())
|
||||
except RuntimeError as error:
|
||||
print(f"错误:{error}", file=sys.stderr)
|
||||
raise SystemExit(1) from error
|
||||
Reference in new issue
Block a user