Documentation
¶
Overview ¶
Package gitserver 是 git push(SSH) 与 webhook 两条部署触发入口的 daemon 侧实现(T2.19):
- SSH 服务器(golang.org/x/crypto/ssh):认证 = 平台管理的 git 公钥 (state 层 git_keys 表,SHA256 指纹匹配);exec 白名单限定 git-receive-pack / git-upload-pack(防 shell 注入——git 直接 exec、 app 名严格校验);bare 仓库按 app 懒创建并生成 post-receive 钩子;
- webhook(gateway 原生 HTTP handler 消费本包 Handler):强制 HMAC- SHA256 验签 + delivery ID TTL 防重放 + (app, sha) 幂等去重 + 拉源;
- 两条入口汇入同一 DeployFromCommit:compose 真源在 git 对象库 (`git show <sha>:compose.{yaml,yml}`),不信任客户端传字节。
包纪律:不 import 任何框架类型(lynx.Service 壳在 cmd/fleetlyd);git 操作全部 exec 系统 git(宿主 git 为前置条件,不引入 go-git 等重框架)。
Index ¶
- Constants
- Variables
- func ValidAppName(name string) bool
- func ValidSHA(sha string) bool
- type Config
- type DeployInput
- type GitTriggers
- func (s *GitTriggers) CheckHealth() error
- func (s *GitTriggers) Config() Config
- func (s *GitTriggers) DeployFromCommit(ctx context.Context, in DeployInput) (state.DeployRecord, []compose.Warning, error)
- func (s *GitTriggers) DeployFromGitPush(ctx context.Context, app, sha, ref, actorTokenID string) (state.DeployRecord, []compose.Warning, error)
- func (s *GitTriggers) EnsureBareRepo(ctx context.Context, app string) (string, bool, error)
- func (s *GitTriggers) FetchRemote(ctx context.Context, app string) error
- func (s *GitTriggers) ListenAndServe(ctx context.Context, addr string) error
- func (s *GitTriggers) SecretValues(ctx context.Context, appID string) []string
- func (s *GitTriggers) StartWebhookWorker(ctx context.Context)
- func (s *GitTriggers) StopWebhookWorker(ctx context.Context) error
- type WebhookHandler
Constants ¶
const ( // DefaultAddr 是 SSH git 面的缺省监听地址。 DefaultAddr = "127.0.0.1:8424" // DefaultReplayTTL 是 webhook delivery ID 防重放缓存窗口(config // webhook.replay_ttl_seconds 缺省 900s = 15 分钟)。 DefaultReplayTTL = 15 * time.Minute // TimestampWindow 是自定义投递方时间戳头(X-Fleetly-Timestamp)的 // 接受窗口(±5 分钟)——绑定口径,不可配置。 TimestampWindow = 5 * time.Minute )
安全默认基线(architecture §4.2):SSH git 面默认只绑回环——VPS 上由 安装/文档指引改为对外;重放窗口默认 15 分钟。
Variables ¶
var ErrBranchNotTracked = errors.New("branch is not tracked")
ErrBranchNotTracked 是分支过滤哨兵(H2:daemon 侧权威过滤):push 的 ref 非该 app 配置分支(空回落缺省 main)时 DeployFromCommit 不建部署、 返回它;api 面据此映射 skipped 回执,与 webhook 入口的 ignored 语义 对齐(webhook 路径在上游已同词形预过滤,不会命中)。
var ErrFetchFailed = errors.New("git fetch failed")
ErrFetchFailed 是拉源失败的包内哨兵。
var ErrSourceNotConfigured = errors.New("app source not configured")
ErrSourceNotConfigured 表示 app 未配置拉源 source(webhook 拉源前置)。
var WebhookPathPattern = regexp.MustCompile(`^/v1/apps/([a-z0-9][a-z0-9-]{0,62})/webhooks/(github|gitea)$`)
WebhookPathPattern 是原生端点的精确路径形态(provider 白名单内联—— 未知 provider 一律 404,豁免面不放宽)。gateway 侧根 handler 用同一线性 词形做分派(豁免面 = 分派面,cmd/fleetlyd/gateway.go 例外清单)。
Functions ¶
Types ¶
type Config ¶
type Config struct {
// Enabled 报告是否启用 SSH git 面(git.enabled;缺省 true)。
Enabled bool
// Addr 是 SSH 监听地址(git.addr;缺省 127.0.0.1:8424)。
Addr string
// Root 是 bare 仓库根目录(git.root;缺省与 state 库同目录下 git/,
// 由装配方计算回落——本包不感知 db 路径)。
Root string
// HostKeyFile 是 SSH host key 文件路径(git.host_key_file;缺省
// <root>/host_ed25519。首启不存在自动生成 ed25519 并持久化,二次
// 启动复用——类比平台节点身份;私钥绝不打印)。
HostKeyFile string
// HookEndpoint 是 post-receive 钩子回调的控制面 HTTP 基址
// (如 http://127.0.0.1:8420;装配方由 addr 键推导)。
HookEndpoint string
// ReplayTTL 是 webhook delivery ID 防重放窗口
//(webhook.replay_ttl_seconds;缺省 15 分钟)。
ReplayTTL time.Duration
}
Config 是 git 触发入口配置(config 键 git.* 与 webhook.* 归并承载—— webhook.* 只有重放窗口一键,随本配置节装配)。
type DeployInput ¶
type DeployInput struct {
// App 是仓库对应的 app 名(compose name 与它不一致 → 拒绝——仓库
// 路径即应用身份,跨名部署会被静默错置)。
App string
// SHA 是 40 位 commit。
SHA string
// Ref 是来源引用(refs/heads/<branch>;非配置分支在入口被过滤——
// ErrBranchNotTracked,落库值恒为配置分支)。
Ref string
// AuditAction 是部署入队审计 action 词根。
AuditAction string
// ActorTokenID 是入队调用方 token(钩子 token id;webhook 进程内路径
// 为空)。
ActorTokenID string
// DedupeSHA 置位时入队事务内做 (app, sha) 幂等复查(M3-4):该 sha
// 已有 enqueued/active/succeeded 部署 → 整笔回滚返回
// state.ErrDuplicateGitDeployment。仅 webhook 入口置位(受理侧的
// check-then-insert 竞态在此闭合);SSH push 路径恒不置位——git push
// 是显式用户动作,每次调用都建部署(幂等口径绑定,见上)。
DedupeSHA bool
}
DeployFromCommit 是两条 git 触发入口的汇合点:compose 字节由服务端从 bare 仓库 `git show <sha>:compose.{yaml,yml}` 自取(真源在 git 对象库, 不信任客户端传字节),落临时文件走受控子集校验(与 API Deploy 同路径) 后入队部署,deployments.source_git_* 记录来源。
幂等口径(绑定):git push 是显式用户动作——每次调用都建部署记录(引擎 对同 spec 重放安全,Spike B2);(app, sha) 去重仅属 webhook 入口,受理侧 预查在 ServeHTTP、竞态兜底在本函数入队事务内(M3-4:DedupeSHA 置位时 COUNT 与 INSERT 同事务,命中返回 state.ErrDuplicateGitDeployment)。分支 过滤(push 到配置分支才触发部署)在 daemon 侧权威承载——见 checkBranchTracked。事件流复用既有 deployment.queued;审计 action 由入参区分(git.push_deploy / git.webhook_deploy),actor 恒 system (机器动作)。
type GitTriggers ¶
type GitTriggers struct {
// contains filtered or unexported fields
}
GitTriggers 是 git 触发入口的门面(命名口径 UBIQUITOUS_LANGUAGE §flagged-2: 承载触发面+拉源+部署入队,不只是一个「来源」,故弃 Source 旧名): bare 仓库管理与 post-receive 钩子、DeployFromCommit 入队、webhook 验签/防重放/去重/拉源、SSH 服务器。 它聚合 state 层(git keys/tokens/apps/deployments)与平台密钥盒 (webhook secret / 拉源认证材料的 envelope 加解密),对上暴露:
- SSH:ListenAndServe(lynx.Service 壳在 cmd/fleetlyd 驱动)
- HTTP:WebhookHandler(gateway 原生端点例外清单挂载)
- API:DeployFromGitPush(DeployFromGit RPC 的端口实现——端口接口 定义在 internal/api,方向纪律:api 不感知本包类型)
func NewGitTriggers ¶
New 构造 Source(cfg 先经 Normalize;启用态完整性 Validate 由装配点 在 Init 阶段 fail-fast)。
func (*GitTriggers) CheckHealth ¶
func (s *GitTriggers) CheckHealth() error
CheckHealth 报告 git 入口层健康:启用态配置完整性 + state 可达。SSH 监听失败属 Start 阶段显式失败,不在健康面二次上报(与 ingress 服务壳 同口径)。
func (*GitTriggers) DeployFromCommit ¶
func (s *GitTriggers) DeployFromCommit(ctx context.Context, in DeployInput) (state.DeployRecord, []compose.Warning, error)
DeployFromCommit 执行读源 → 校验 → 入队;返回 queued 部署记录与校验 期警告(非阻断标注,与 API Deploy 同面)。compose 违约(受控子集之外) 经 compose.Load 原样透传 E_COMPOSE_* 信封。
func (*GitTriggers) DeployFromGitPush ¶
func (s *GitTriggers) DeployFromGitPush(ctx context.Context, app, sha, ref, actorTokenID string) (state.DeployRecord, []compose.Warning, error)
DeployFromGitPush 是 DeployFromGit RPC 的端口实现(git push 路径;每次 push 都建部署记录)。actorTokenID 记录钩子回调 token(可追溯)。
func (*GitTriggers) EnsureBareRepo ¶
EnsureBareRepo 懒创建 app 的 bare 仓库并确保 post-receive 钩子在位:
- 仓库缺失 → git init --bare + 新钩子 token + 写钩子(0600 + POSIX 可执行位;明文 token 只写进钩子文件);
- 仓库在、钩子缺失(仓库重建/手工误删)→ 轮换钩子 token(按名吊销 旧 token)+ 重写钩子——daemon 重建仓库时轮换的绑定语义。
幂等;返回仓库路径与是否发生了创建。E7③(S19):全程持 per-app 互斥 (stat 判定与 init/钩子写入/token 轮换之间的 TOCTOU 收口——并发同 app 调用方 SSH push/webhook 拉源/管理面交错时不再产生失配 token)。
func (*GitTriggers) FetchRemote ¶
func (s *GitTriggers) FetchRemote(ctx context.Context, app string) error
FetchRemote 执行拉源:确保 bare 仓库在位 → 按计划 fetch。失败一律包装 ErrFetchFailed(调用方映射错误族信封;errors.Is 判定不受 B2 收口影响)。
B2(出站字节出口收口):全路径失败原文(git stderr、仓库路径、state/ 底层错误细节)只进 slog Warn;返回错误只保留哨兵 + 阶段词——调用方 (webhook 信封/审计)不再拼接原文,杜绝内部字节外泄。
func (*GitTriggers) ListenAndServe ¶
func (s *GitTriggers) ListenAndServe(ctx context.Context, addr string) error
ListenAndServe 监听 SSH git 面(阻塞至 ctx 取消或监听错误);启用态由 lynx.Service 壳(cmd/fleetlyd)驱动。
func (*GitTriggers) SecretValues ¶
func (s *GitTriggers) SecretValues(ctx context.Context, appID string) []string
SecretValues 实现 logs.SecretValuesSource(B3 脱敏值集扩面):钩子 token 的明文只落 post-receive 钩子文件(state 仅存哈希,无解密面)——从钩子 文件读取并入该 app 的日志脱敏值集。app 行/钩子文件缺失返回 nil(脱敏 面降级,不阻断采集)。
func (*GitTriggers) StartWebhookWorker ¶
func (s *GitTriggers) StartWebhookWorker(ctx context.Context)
StartWebhookWorker 启动 webhook 后台 worker(D1):随 git.ssh 服务壳的 lynx Start 启动(webhook 面是 gateway 原生端点、独立于 SSH enabled 开关, 服务壳恒承担 worker 生命周期);ctx 取消后 worker 排空队列退出(排空 预算尽时剩余项转披露丢弃,X-6——见 webhookAuditInterrupted), StopWebhookWorker 是排空完成等待点。
func (*GitTriggers) StopWebhookWorker ¶
func (s *GitTriggers) StopWebhookWorker(ctx context.Context) error
StopWebhookWorker 等待 worker 排空退出(D1);未启动为 no-op。
type WebhookHandler ¶
type WebhookHandler struct {
// contains filtered or unexported fields
}
WebhookHandler 是原生 webhook 端点处理器。
func NewWebhookHandler ¶
func NewWebhookHandler(src *GitTriggers) *WebhookHandler
NewWebhookHandler 构造(src 提供验签材料/防重放缓存/拉源/入队)。
func (*WebhookHandler) ServeHTTP ¶
func (h *WebhookHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
ServeHTTP 实现 http.Handler(路径精确匹配 WebhookPathPattern)。