2cddf06b6c
Co-authored-by: 旅行呀~ <travelxiao@qq.com>
10 KiB
10 KiB
git-init 架构
本文说明组件如何分工、密钥放在哪、各条 Git 路径怎么走。使用步骤见 使用文档.md。
设计目标
- 对话里只出现 Gitea 仓库 URL 和 SSH/GPG 选择,不出现 Token
- 同一套本机档案服务于初始化以及之后每一次提交、推送、克隆、拉取
- 默认远程为 Gitea(自建域名与自定义端口);脚本按 URL 解析 host/port
- Windows PowerShell 可执行;不用
eval "$(ssh-agent -s)" - 可复用:源码在仓库
.cursor/skills/git-init,install.ps1同步到个人 Skill 目录
逻辑架构
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
三层:
- Skill 指令(
SKILL.md):路由到 Init / Commit / Push / Clone 等,并列出安全红线 - 脚本(
scripts/):状态检查、加密存取、注入凭据、SSH/GPG;stdout 不含 Token - 本机秘密(
%USERPROFILE%\.git-skills\):与任何项目仓库隔离
目录与职责
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 |
仅 Token,DPAPI CurrentUser |
Agent 可以读 profile.json(经 profile-status.ps1)。Agent 不得读 token.dpapi。
信任边界
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、私钥
脚本调用关系
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 |
主流程
总路由
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
初始化
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]
每次提交
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]
每次推送 / 克隆 / 拉取
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
HTTPS:Token 解密后仅进入 git credential approve 的 stdin。SSH:用已有 id_ed25519,日常路径不生成新密钥、不打印公钥。
Agent 控制流
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 中的步骤映射:
| 文档章节 | 实现 |
|---|---|
| 一 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 与凭据
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。
Gitea 密钥验证(官方行为)
依据 docs.gitea.com/administration/signing:灰色开锁 = 数据库中找不到可校验密钥。用户密钥须在 /user/settings/keys 验证后,提交签名才能被识别。
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 主动询问(一次性,不落盘)。