Files
Git-Skill/docs/架构.md
T
2026-08-20 23:42:02 +08:00

327 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# git-init 架构
本文说明组件如何分工、密钥放在哪、各条 Git 路径怎么走。使用步骤见 [使用文档.md](使用文档.md)。
## 设计目标
- **对话里只出现 Gitea 仓库 URL 和 SSH/GPG 选择**,不出现 Token
- **同一套本机档案**服务于初始化以及之后每一次提交、推送、克隆、拉取
- **默认远程为 Gitea**(自建域名与自定义端口);脚本按 URL 解析 host/port
- **Windows PowerShell** 可执行;不用 `eval "$(ssh-agent -s)"`
- **可复用**:源码在仓库 `.cursor/skills/git-init``install.ps1` 同步到个人 Skill 目录
## 逻辑架构
```mermaid
flowchart LR
user[UserCursorChat]
agent[AgentPlusSkill]
scripts[PowerShellScripts]
profile[ProfileJson]
tokenFile[TokenDpapi]
gcm[GitCredentialManager]
sshAgent[WindowsSshAgent]
gpg[GpgKeyring]
git[GitRepo]
remote[GiteaHost]
user --> agent
agent --> scripts
scripts --> profile
scripts --> tokenFile
tokenFile -->|"decrypt CurrentUser"| gcm
scripts --> sshAgent
scripts --> gpg
agent --> git
gcm --> remote
sshAgent --> remote
git --> remote
```
三层:
1. **Skill 指令**`SKILL.md`):路由到 Init / Commit / Push / Clone 等,并列出安全红线
2. **脚本**`scripts/`):状态检查、加密存取、注入凭据、SSH/GPGstdout 不含 Token
3. **本机秘密**`%USERPROFILE%\.git-skills\`):与任何项目仓库隔离
## 目录与职责
```mermaid
flowchart TB
subgraph repo [git-skillsRepo]
skillMd[SKILL.md]
workflows[workflows.md]
reference[reference.md]
examples[examples.md]
scriptsDir[scripts]
installPs[install.ps1]
docsDir[docs]
end
subgraph personal [UserHome]
personalSkill[".cursor/skills/git-init"]
secrets[".git-skills"]
end
installPs -->|"copy skill not secrets"| personalSkill
scriptsDir --> secrets
```
| 路径 | 职责 |
| --- | --- |
| `.cursor/skills/git-init/SKILL.md` | Agent 入口:路由、红线、Init/Commit/Push |
| `workflows.md` | Clone / Pull / Branch / Merge / Tag / Stash / Inspect |
| `reference.md` | 中文提交规范、Windows SSH/GPG、故障表 |
| `examples.md` | 触发例句 |
| `scripts/*.ps1` | 可执行步骤;失败用退出码和 `key=value` |
| `install.ps1` | 复制 Skill 到 `~/.cursor/skills/git-init` |
| `%USERPROFILE%\.git-skills\profile.json` | 姓名、邮箱、HTTPS 用户名、SSH 路径 |
| `%USERPROFILE%\.git-skills\token.dpapi` | 仅 TokenDPAPI `CurrentUser` |
Agent **可以**读 `profile.json`(经 `profile-status.ps1`)。Agent **不得**读 `token.dpapi`
## 信任边界
```mermaid
flowchart TB
subgraph chat [CursorChat]
urlIn[RemoteUrl]
talk[NoTokenInChat]
end
subgraph machine [ThisWindowsUser]
dpapi[DPAPI]
gcm[GCM]
sshKeys[SSHPrivateKey]
gpgKeys[GPGSecretKey]
end
subgraph project [GitWorkTree]
gitDir[.git]
ignore[.gitignore]
end
subgraph host [Gitea]
origin[origin]
end
urlIn --> gitDir
dpapi -->|"git credential approve stdin"| gcm
gcm --> origin
sshKeys --> origin
gitDir --> origin
ignore -->|"block secrets"| gitDir
```
- 解密只在当前 Windows 用户下有效;换用户或换机器无法读 Token
- 注入走 stdin 给 `git credential approve`,不写进命令行参数、不写进仓库
- 项目 `.gitignore` 忽略 `.git-skills/``*.dpapi`、私钥
## 脚本调用关系
```mermaid
flowchart TB
storeProfile[store-profile.ps1]
storeToken[store-token.ps1]
profileStatus[profile-status.ps1]
tokenStatus[token-status.ps1]
prepareCommit[prepare-commit.ps1]
prepareAuth[prepare-auth.ps1]
preparePush[prepare-push.ps1]
inject[inject-credential.ps1]
ensureSsh[ensure-ssh.ps1]
ensureGpg[ensure-gpg.ps1]
assertSecrets[assert-no-secrets.ps1]
repoStatus[repo-status.ps1]
common[common.ps1]
storeProfile --> common
storeToken --> common
profileStatus --> common
tokenStatus --> common
prepareCommit --> common
prepareAuth --> inject
prepareAuth --> ensureSsh
preparePush --> prepareAuth
inject --> common
ensureSsh --> common
ensureGpg --> common
assertSecrets --> common
repoStatus --> common
```
| 脚本 | 谁跑 | 作用 |
| --- | --- | --- |
| `store-profile.ps1` / `store-token.ps1` | 用户本机终端 | 交互写入档案 / 加密 Token |
| `profile-status.ps1` / `token-status.ps1` | Agent | `present` / `missing`,无密钥明文 |
| `prepare-commit.ps1` | Agent | 本地作者 + `commit.gpgsign` |
| `prepare-auth.ps1` | Agent | 按 URL 注入 GCM 或 ssh-add(克隆可用) |
| `prepare-push.ps1` | Agent | 读 `origin` 再调 `prepare-auth` |
| `ensure-ssh.ps1` / `ensure-gpg.ps1` | Agent | 复用或生成密钥;日常推送 `-NoGenerate -Quiet` |
| `assert-no-secrets.ps1` | Agent | 提交前拦截密钥路径 |
| `repo-status.ps1` | Agent | 分支、脏否、打码后的 origin |
## 主流程
### 总路由
```mermaid
flowchart TD
start[UserRequest]
start --> route{Intent}
route -->|init| initPath[Init]
route -->|commit| commitPath[Commit]
route -->|push| pushPath[Push]
route -->|commitAndPush| commitPath
commitPath -->|ifAskedPush| pushPath
route -->|clone| clonePath[Clone]
route -->|pullOrFetch| syncPath[Sync]
route -->|branchMergeTagStash| localPath[LocalGit]
route -->|statusLogDiff| inspectPath[Inspect]
route -->|commitButNotRepo| initPath
```
### 初始化
```mermaid
flowchart TD
detect[DetectRepoAndRemote]
detect --> profile{ProfileAndToken}
profile -->|missing| stopStore[StopTellUserRunStoreScripts]
profile -->|ok| askUrl[NeedRemoteUrl]
askUrl --> initGit[git init plus local author]
initGit --> addOrigin[remote add origin]
addOrigin --> inject[inject-credential]
inject --> ssh[ensure-ssh plus ssh test]
ssh --> gpg[ensure-gpg plus gpgsign]
gpg --> hygiene[gitignore and README]
hygiene --> firstCommit[Commit path]
firstCommit --> firstPush[Push main]
firstPush --> verify[status remote log signature]
```
### 每次提交
```mermaid
flowchart TD
prep[prepare-commit]
prep --> signing{signing present}
signing -->|missing| gpg[ensure-gpg]
gpg --> prep
signing -->|ok| inspect[status diff log]
inspect --> secrets[assert-no-secrets]
secrets -->|blocked| unstage[Unstage secret paths]
secrets -->|ok| stage[Stage related files]
stage --> msg[Chinese type colon description]
msg --> commit[git commit -S]
commit --> check[log show-signature]
check --> maybePush{User asked push}
maybePush -->|yes| pushPath[Push]
maybePush -->|no| done[Stop]
```
### 每次推送 / 克隆 / 拉取
```mermaid
flowchart TD
needAuth[NeedNetworkAuth]
needAuth --> hasRepo{Inside work tree}
hasRepo -->|yes| prepPush[prepare-push]
hasRepo -->|no clone| prepAuth[prepare-auth with URL]
prepPush --> proto{Protocol}
prepAuth --> proto
proto -->|https| gcm[GCM uses injected token]
proto -->|ssh| sshAdd[ssh-add Quiet]
gcm --> gitNet[git push or clone or pull]
sshAdd --> gitNet
```
HTTPSToken 解密后仅进入 `git credential approve` 的 stdin。SSH:用已有 `id_ed25519`,日常路径不生成新密钥、不打印公钥。
## Agent 控制流
```mermaid
sequenceDiagram
participant U as User
participant A as Agent
participant S as Scripts
participant G as Git
participant H as GitHost
U->>A: 提交并推送
A->>S: profile-status token-status repo-status
S-->>A: present plus branch info
A->>S: prepare-commit
A->>S: assert-no-secrets
A->>G: git commit -S
A->>S: prepare-push
S->>G: credential approve or ssh-add
A->>G: git push
G->>H: objects
A-->>U: 作者签名与推送结果无 Token
```
## 与原始任务的对应
[相关文档.md](../相关文档.md) 中的步骤映射:
| 文档章节 | 实现 |
| --- | --- |
| 一 init | `git init -b main` |
| 二 用户信息 | `profile.json` + 仓库 local `user.name` / `user.email` |
| 三 远程 | `git remote add origin`(已有则不改) |
| 四 Token | DPAPI + GCM,禁止写入代码 |
| 五 SSH | `ensure-ssh.ps1` + Windows `ssh-agent` |
| 六 GPG | `ensure-gpg.ps1` + `commit.gpgsign` |
| 七 / 十二 中文提交 | 每次 Commit 路径强制 `类型: 描述` |
| 八 README | 缺失才生成 |
| 九 / 十 首次提交推送 | Init 末尾走 Commit + Push |
| 十一 验收 | `status` / `remote -v` / `log --show-signature` |
## Gitea URL 与凭据
```mermaid
flowchart TD
url[GiteaRemoteUrl]
url --> parse[Parse-GitRemoteUrl]
parse --> httpsPath{Protocol}
httpsPath -->|https or http| credHost[CredentialHost may include port]
httpsPath -->|ssh| sshHost[Host plus optional Port]
credHost --> inject[inject-credential to GCM]
sshHost --> sshTest[test-ssh with ssh -p]
inject --> pushHttps[git push HTTPS]
sshTest --> pushSsh[git push SSH]
```
| URL 示例 | CredentialHost | SSH 测试 |
| --- | --- | --- |
| `https://git.example.com/a/b.git` | `git.example.com` | 不适用 |
| `https://git.example.com:3000/a/b.git` | `git.example.com:3000` | 不适用 |
| `git@git.example.com:a/b.git` | 主机名(供日后 HTTPS) | `ssh -T git@...` |
| `ssh://git@git.example.com:2222/a/b.git` | 主机名(不含 2222 | `ssh -p 2222 -T git@...` |
细节见 [gitea.md](../.cursor/skills/git-init/gitea.md)。
## Gitea 密钥验证(官方行为)
依据 [docs.gitea.com/administration/signing](https://docs.gitea.com/administration/signing/):灰色开锁 = 数据库中找不到可校验密钥。用户密钥须在 `/user/settings/keys` **验证**后,提交签名才能被识别。
```mermaid
flowchart TD
addKey[AddPubkeyOnGitea]
addKey --> verifyClick[ClickVerifyOnSettings]
verifyClick --> challenge[PageShowsToken]
challenge --> localSign[verify-gitea-keys with tokens from chat]
localSign --> paste[AgentShowsSignatureBlocks]
paste --> verified[KeyMarkedVerified]
verified --> newCommit[NewSignedCommitShowsVerified]
```
| 脚本 | 对齐官方 UI |
| --- | --- |
| `verify-gitea-keys.ps1` | 编排:可缺令牌时提示 `need_token`;有令牌则调下面两个脚本 |
| `verify-gpg-challenge.ps1` | `echo "TOKEN" \| gpg -a --default-key KEY --detach-sig`(有换行) |
| `verify-ssh-challenge.ps1` | `echo -n 'TOKEN' \| ssh-keygen -Y sign -n gitea -f KEY`(无换行) |
HTTPS **访问令牌**禁止进对话。页面 **验证挑战令牌**可由 Agent 主动询问(一次性,不落盘)。