本指南对应 ROADMAP Milestone 8。当前正式支持 Docker Compose、Linux systemd 服务和 Windows Service 三种部署方式,不支持 IIS in-process、IIS out-of-process 或旧 ASP.NET MVC5 的 IIS 发布流程。
推送 v* tag 后,GitHub Actions 创建 Release 并发布:
| 产物 | 用途 |
|---|---|
gitcandy-{version}-linux-x64.tar.gz |
Linux x64 自包含应用、systemd unit 和安装脚本 |
gitcandy-{version}-win-x64.zip |
Windows x64 自包含应用和 Windows Service 脚本 |
gitcandy-{version}-migration-sql.zip |
SQLite 建库 SQL 和 SQL Server idempotent migration SQL |
gitcandy-{version}-compose.zip |
docker-compose.yml 与镜像、宿主机端口变量样例 |
gitcandy-{version}-linux-amd64-image.tar.gz |
可离线下载并通过 docker load 导入的镜像 |
应用目录内的 wwwroot/help 是每种服务包和容器镜像的必需内容,至少包含 index.html、help-manifest.json、search-index.json 和本地主题资产。/help/ 允许匿名访问并与应用同版本发布;发布 smoke 会请求首页和 manifest,缺失文档会阻止发布。
在线镜像使用相同版本 tag:
docker pull ghcr.io/iotsharp/gitcandy:latest
docker pull iotsharp/gitcandy:latest离线镜像导入:
gzip -dc gitcandy-1.0.0-linux-amd64-image.tar.gz | docker loadDocker Hub 发布需要仓库 secrets DOCKERHUB_USERNAME 和 DOCKERHUB_TOKEN,该账户必须有 iotsharp/gitcandy push 权限。GHCR 使用 GitHub Actions 的 GITHUB_TOKEN 发布到 ghcr.io/iotsharp/gitcandy。首次正式发布前必须把两个 container repository 都设为 public;workflow 在 push 后注销凭据并实际执行两个匿名 pull,私有或不可拉取会让发布失败。
- 解压 Release 中的 Compose 包,或使用仓库根目录的文件。
- 将
.env.example复制为.env,把GITCANDY_IMAGE固定到明确版本,生产环境不要长期使用latest。 - 启动服务并检查 readiness。
docker compose pull
docker compose up -d
docker compose ps
curl --fail http://127.0.0.1:8080/health/readyGitCandy 主程序会在 Web、SSH、Quartz 和其他 hosted service 启动前检查 EF Core pending migrations;有待应用版本时自动迁移,没有时直接继续。迁移失败会让进程以失败状态退出,不会在旧 schema 上开始监听。GitCandy --migrate 仍可用于只执行迁移后退出,但 Compose 不再需要单独的迁移服务。
帮助站点只在构建/发布阶段由固定 JekyllNet local tool 从 docs/help Markdown 生成。生产启动和请求不会解析 Markdown,也不需要 JekyllNet、Node.js、网络或 SonnetDB。不要在服务器上手工覆盖 wwwroot/help;升级和回滚时应把它与同一应用产物整体替换。
源码仓库使用标准 docker-compose.override.yml 保存 build.context 和 build.dockerfile,执行 docker compose up --build -d 时会自动加载。Release Compose 包不包含该重载文件;Release 部署先执行 docker compose pull,随后使用基础 docker-compose.yml 启动预构建镜像。
默认端口为 HTTP 8080、SSH 2222,持久状态位于 gitcandy-data volume。可在 .env 修改宿主机映射端口,不要修改容器内 SSH 端口而不同步 GitCandy__Application__SshPort。
容器内端口、SQLite、repository、cache、SSH host key 和 Data Protection key ring 的生产默认值统一定义在主程序的 appsettings.json,docker-compose.yml 不再重复列出这些应用配置。需要修改时可通过 Compose override 文件把自定义 appsettings.Production.json 只读挂载到 /app/appsettings.Production.json,也可继续使用 ASP.NET Core 环境变量;环境变量把 : 替换为 __,并覆盖 JSON 配置。不要把密码、token 或私钥写入 Compose 文件或提交到仓库。
Web 登录 cookie 始终带 Secure。公网部署必须在 8080 前放置支持长请求和流式响应的 TLS reverse proxy,并正确转发 scheme、host 和客户端地址。Git Smart HTTP 的 request body 上限和 timeout 也要在代理层设置为不低于 GitCandy 的 GitCandy:GitHttp 配置。不要对 pack 响应启用代理缓冲。
仓库提供 Caddy 快速 TLS overlay。域名必须已解析到主机,80/443 必须可达:
export GITCANDY_DOMAIN=git.example.com
docker compose -f docker-compose.yml -f docker-compose.tls.yml config --quiet
docker compose -f docker-compose.yml -f docker-compose.tls.yml up -d
curl --fail https://git.example.com/health/readyGitCandy:Proxy 默认关闭。overlay 只信任固定容器地址 172.16.0.2,ForwardLimit=1;自行更换网络时必须同步修改 KnownProxies,禁止清空 known proxies/networks 后信任任意来源。Forwarded Headers 在 HTTPS redirect、认证和链接生成之前执行,因此 clone URL 与 OIDC callback 使用外部 https scheme/host。
deploy/sonnet-vip 提供 gitcandy.com 的服务器专用 profile:复用该主机已有 Caddy 和内部 SonnetDB,不启动第二个 Caddy,也不公开数据库端口。该 profile、DNS、固定代理地址、SSH 2222、secret 注入和验证步骤见 sonnet.vip 部署说明。
密码恢复使用 GitCandy:Identity:AccountRecovery。启用时配置 SMTP Host、Port、EnableSsl、UserName、Password 和 FromAddress;密码应通过 secret store 或未提交的环境配置提供。默认 token 有效一小时,同一 IP/邮箱分区 15 分钟最多请求五次。未配置或投递失败不会在响应中泄漏账号是否存在,也不会记录 token 或邮箱。
目标主机需要 Linux x64、systemd 和 Git CLI;自包含包不要求预装 .NET Runtime。解压后执行:
sudo ./install.sh
curl --fail http://127.0.0.1:8080/health/ready
sudo systemctl status gitcandy
sudo journalctl -u gitcandy -n 200 --no-pager安装脚本执行以下受控操作:
- 创建无登录 shell 的
gitcandy系统账户。 - 安装程序到
/opt/gitcandy,数据放到/var/lib/gitcandy。 - 仅在配置文件不存在时安装
appsettings.Production.json,升级不会覆盖现有配置。 - 安装并启动
gitcandy.service;服务进程在开始监听前自动应用 pending migrations。
systemd unit 使用只读系统目录、独立临时目录和 NoNewPrivileges,仅允许写 /var/lib/gitcandy。默认 SSH 端口 2222 不需要授予低端口 capability;若对外使用 22,优先通过防火墙/NAT 转发 22 到 2222。
目标主机需要 Windows x64 和 Git for Windows;自包含包不要求预装 .NET Runtime。以管理员 PowerShell 解压并执行:
Set-ExecutionPolicy -Scope Process Bypass
.\Install-GitCandyService.ps1
Get-Service GitCandy
Invoke-WebRequest http://127.0.0.1:8080/health/ready默认程序目录为 %ProgramFiles%\GitCandy,数据目录为 %ProgramData%\GitCandy。安装脚本先停止旧服务、保留现有配置和数据、复制新程序,然后以 NT SERVICE\GitCandy 虚拟账户启动服务并授予数据目录修改权限;服务进程在开始监听前自动应用 pending migrations。
移除服务但保留程序和数据:
.\Uninstall-GitCandyService.ps1主程序 appsettings.json 提供 Linux/容器生产默认值;本地 Development 环境由 appsettings.Development.json 覆盖为 content root 下的 App_Data。生产修改优先使用 appsettings.Production.json 或环境变量。环境变量把 : 替换为 __,例如 GitCandy:Application:RepositoryPath 对应 GitCandy__Application__RepositoryPath;ASPNETCORE_HTTP_PORTS 可覆盖默认 HTTP 8080 监听端口。
| 旧 MVC5 配置 | ASP.NET Core 配置 | 说明 |
|---|---|---|
connectionStrings/GitCandyContext |
ConnectionStrings:GitCandy |
SQLite 不再使用 BinaryGUID=True;Identity 使用新 schema |
| provider name / 旧 EF6 provider | GitCandy:Database:Provider |
当前主 host 发布路径为 SQLite-first |
appSettings/LogPathFormat |
不迁移 | 日志输出由 ASP.NET Core logging provider 和部署宿主管理 |
appSettings/UserConfiguration |
GitCandy:Application:UserConfigurationPath |
只用于保留配置和 RSA host key 导入 |
config.xml/RepositoryPath |
GitCandy:Application:RepositoryPath |
相对路径基于 content root;外部目录使用绝对路径 |
config.xml/CachePath |
GitCandy:Application:CachePath |
cache 可重建,不应与 repository 混用 |
config.xml/GitCorePath |
GitCandy:Application:GitCorePath |
留空时从 PATH 查找 git;配置时必须指向包含 Git executable 的目录 |
config.xml/EnableSsh |
GitCandy:Application:EnableSsh |
内置 SSH 随主进程启停 |
config.xml/SshPort |
GitCandy:Application:SshPort |
容器和服务模板默认 2222 |
config.xml/HostKeys |
GitCandy:Application:SshHostKeyPath |
首次可导入旧 RSA key;新文件包含私钥 |
config.xml/IsPublicServer 等页面设置 |
GitCandy:Application:* 同名属性 |
设置页当前只读,生产配置由文件/环境变量管理 |
IIS requestLimits/timeout |
GitCandy:GitHttp:MaxRequestBodySize、RequestTimeout |
reverse proxy 必须配置匹配的上限和流式行为 |
代码浏览的显示与计算边界位于 GitCandy:RepositoryBrowser:MaxDisplayedBlobBytes、MaxDiffCharacters、MaxDiffFiles、MaxArchiveBytes、MaxArchiveEntries 和 OperationTimeout。这些限制只影响 Web 展示/归档,不改变 Git Smart HTTP/SSH pack 传输。
Git LFS v2 由 GitCandy:Lfs 配置:Enabled、MaxObjectBytes、RepositoryQuotaBytes、StreamBufferSize 和 OperationTimeout。RepositoryQuotaBytes=0 表示不设置额外的仓库总量上限。reverse proxy 需要允许 /{namespace}/{repository}.git/info/lfs/* 的流式 PUT/GET 和 range response。
新增的 GitCandy:Application:DataProtectionKeysPath 保存 Identity cookie 加密 key ring。它必须位于持久化、仅应用账户可写的目录;丢失后已有 cookie 全部失效。
Pull Request review 策略也位于 GitCandy:Application:
| 配置键 | 默认值 | 行为 |
|---|---|---|
AllowAuthorApproval |
false |
是否允许 PR 作者提交对本人 PR 的 approve;comment 和 request changes 不受此项影响 |
DismissStalePullRequestApprovals |
true |
source head 更新后,旧 head 上的 approve 是否不再作为有效批准;历史 review 始终保留 |
RequiredPullRequestApprovals |
1 |
merge/squash 前所需的当前有效 approve 数;设为 0 可关闭该基础门禁,M13 branch policy 会在此基础上扩展 |
Webhook delivery 由 GitCandy:Webhooks 配置。生产默认只允许公网 HTTPS target,AllowHttpTargets=false、AllowPrivateNetworkTargets=false;DNS/IP 策略在 subscription 保存和实际 socket 连接时都会执行,且不会跟随 redirect。RequestTimeout、ConnectTimeout、MaxAttempts、DeliveryBatchSize、MaxResponseBytes 和 MaxSubscriptionsPerRepository 控制有界资源使用。AllowHttpTargets 与 AllowPrivateNetworkTargets 只应用于受控本地 fixture;生产启用会扩大 SSRF 和内网访问面,必须另行评审并限制应用出站网络。
Webhook signing secret 由 Data Protection key ring 加密保存且只在创建页显示一次,因此 DataProtectionKeysPath 与数据库必须一致备份/恢复。丢失 key ring 后旧 subscription 无法继续签名,应暂停并重新创建,而不是在日志或配置中回显 secret。
远程账号连接和受控 Git 同步后端由 GitCandy:Remotes 配置。GitHub、GitLab、Gitee 各自提供 Enabled、ServerUrl 和 ApiBaseUrl;正式环境只允许固定 HTTPS origin,loopback HTTP 仅供受控 fixture。RequestTimeout 默认 20 秒,只控制 Provider API;OperationTimeout 默认 30 分钟,控制 Git fetch/push;StreamBufferSize 默认 81920 字节,MaxDiagnosticCharacters 默认 8192。用户不能覆盖 endpoint,authenticated request 不跟随 redirect,token 不进入 URL、参数、环境变量、临时文件或日志。Git credential helper 通过一次性同用户命名管道取得 token。连接 token 由 Data Protection 加密保存在 DataProtectionKeysPath/remote-credentials,因此该子目录必须与 key ring 和数据库一致备份;丢失 key ring 时应在远端撤销 token 并重新连接。
单向 Pull/Push mirror 已使用同一受控 backend。Pull mirror 在 refs/gitcandy/mirrors/ 隔离区比较远端 refs,正式 Git transport 会隐藏该 namespace,操作结束后清理;启用期间所有本地写入口都拒绝写入。Push mirror 的 post-receive 子进程只把每个 mirror/ref 的最新 generation 保存到 RemoteMirrorRefUpdates,durable worker 再异步执行远端 fetch/compare/push,因此远端失败不回滚本地 push。初始、schedule、webhook、manual、post-receive 和 recovery 触发合并到每个 mirror 唯一的 RemoteMirrorJobs 记录;optimistic concurrency lease 防止重复执行,到期 lease 自动恢复,可重试故障使用指数退避和 jitter。仓库 owner 通过 /{namespace}/{repository}/settings/mirrors 暂停、恢复、立即同步、取消、重试和删除。
GitCandy:Remotes:Jobs 默认 MaxConcurrentJobs=2、DispatchBatchSize=10、MaxAttempts=5、LeaseDuration=00:35:00、InitialRetryDelay=00:00:15、MaximumRetryDelay=00:15:00、RetryJitterRatio=0.2。LeaseDuration 必须大于远程 Git OperationTimeout;提高并发前评估 Git HTTP/SSH 延迟、磁盘、CPU 和出站带宽。部署仍建议先保持单实例,当前 lease/恢复不能替代完整的多区域协调和灾难恢复方案。
Provider callback 为 /remote-events/{connectionId}/{provider}。连接只保存 env: 或 config: webhook secret reference;GitHub 使用 X-Hub-Signature-256,GitLab 使用 X-Gitlab-Token,Gitee 使用 X-Gitee-Token。生产反向代理应允许最多 1 MiB request body 且不得记录这些 header 或 body。每个真实 provider/tenant 上线前必须 smoke 仓库发现、token scope/expiry/rotation、callback 注册与签名、rename、delete、rate-limit 以及 Pull/Push;fixture 测试不能验证上游应用授权。GitHub App 当前绑定已获取且带到期时间的 installation token,不从 private key 自动换票;GitLab/Gitee OAuth 当前绑定已有 access token,不发起交互式 consent。
数据库与仓库必须一致备份:只恢复数据库可能重放已不存在的本地 ref,只恢复仓库会丢失尚未发送的 pending event。二进制回滚前停止 scheduler 和 push 写入,并恢复升级前同一时间点的数据库、repositories、Data Protection key ring 与 secret 配置;只执行 down migration 会删除 pending/retry 和 provider event receipt 状态。
用户通知的 webhook target 复用相同 SSRF、连接和签名边界;通知邮件复用 GitCandy:Identity:AccountRecovery:Smtp。通知偏好不会关闭站内 inbox,只决定额外邮件/webhook 投递。后台投递前会重新检查 repository/team 权限,撤权后的 pending delivery 以 permission_revoked 失败结束,不再发送资源标题或 URL。
Release 附件由 GitCandy:Releases 控制:MaxAssetBytes 默认 100 MiB,MaxTotalAssetBytes 默认每个 Release 1 GiB,MaxAssetsPerRelease 默认 20,OrphanRetention 默认 24 小时。附件流式写入临时文件、计算 SHA-256 后原子提交,物理键只使用 repository/release/asset ID,不使用上传文件名。CachePath/release-assets 与 CachePath/lfs 一样是不可重建的持久内容,必须与数据库、repository、LFS 和 Data Protection keys 一致备份/恢复;普通 cache 仍可重建。
CachePath/lfs 与 CachePath/release-assets 虽位于 cache 根,但保存不可重建内容,必须纳入持久卷、备份/恢复和容量告警;其他普通 cache 仍可重建。
新密码默认至少 12 位,并要求至少 4 个不同字符以及大写字母、小写字母、数字和非字母数字字符。可通过 GitCandy:Identity:Password:RequiredLength、RequiredUniqueChars、RequireDigit、RequireLowercase、RequireUppercase、RequireNonAlphanumeric 调整;降低策略前应完成安全评审。策略只影响新密码和密码变更,不会使已有密码立即失效。
通用 OpenID Connect 登录默认关闭。启用时在未提交到仓库的生产配置中设置:
{
"GitCandy": {
"Identity": {
"OpenIdConnect": {
"Enabled": true,
"DisplayName": "Company ID",
"Authority": "https://identity.example.com",
"ClientId": "gitcandy",
"ClientSecret": "从生产 secret store 注入",
"CallbackPath": "/signin-oidc",
"RequireHttpsMetadata": true
}
}
}
}优先通过 secret store 或进程凭据提供 GitCandy__Identity__OpenIdConnect__ClientSecret,不要把真实 secret 写入镜像、Compose、systemd unit 或仓库。Identity provider 中登记的 redirect URI 是 GitCandy 公网 HTTPS origin 加 CallbackPath。GitCandy 不持久化上游 access/refresh token;本地退出不会结束 provider 的单点登录会话。关闭 Enabled 即可回滚外部登录入口,已绑定记录保留在标准 AspNetUserLogins 表中,不需要 schema 回滚。
TOTP 和恢复码只用于交互式 Web/外部登录的第二阶段认证。Git Smart HTTP Basic Auth 与 SSH public key 仍是独立认证方案,不能弹出 MFA challenge,本项不改变其现有协议行为或凭据要求。
不迁移旧 _gc_auth cookie、密码 hash、Users、AuthorizationLog 或 PasswordVersion。用户必须在新 Identity schema 中重新创建。旧 repository/team/role 元数据只能由后续独立导入工具导入;当前发布不包含自动导入器,也不会在启动时读取并改写旧数据库。物理 bare repositories 可备份后复制到新 repository 根目录,再在新系统重建对应 metadata 和权限。
TeamOwner 或系统管理员可从团队详情进入 Enterprise connections。连接只持久化公开配置和 secret reference;支持以下两种运行时引用:
env:GITCANDY_ENTRA_CLIENT_SECRET
config:GitCandy:EnterpriseSecrets:ContosoEntra
env: 后只允许 ASCII 字母、数字和下划线;config: 后是 ASP.NET Core configuration key。真实值应由进程环境、容器 secret、systemd credential 或未提交的生产配置注入。管理 UI 和诊断只显示引用及脱敏状态,不返回 secret。provider webhook 使用独立的 Webhook secret reference,不要与 OAuth client secret 共用。
| Provider | 公开连接字段 | secret 内容 | 说明 |
|---|---|---|---|
| Microsoft Entra ID | tenant ID、HTTPS authority、client ID | client secret | authority 通常为 tenant 专用 v2.0 authority;redirect URI 是公网 HTTPS origin 加 /EnterpriseLogin/Callback。JIT 还要求应用允许注册且非敏感 JSON 为 {"allowJit":true} |
| SCIM | 稳定组织 ID | secret reference 仍为必填占位引用 | 从管理页轮换独立 SCIM bearer;base URL 为公网 HTTPS origin 加 /scim/v2/{connectionId},明文 bearer 只显示一次 |
| 企业微信 | CorpId、可选 API base URL | CorpSecret | 登录还需非敏感 JSON {"agentId":"100001"};callback 同为 /EnterpriseLogin/Callback |
| 飞书 | tenant/组织稳定 ID、App ID、可选 API base URL | App Secret | event URL 为 /enterprise-events/{connectionId}/Feishu,签名 secret 单独配置 |
| 钉钉 | CorpId、AppKey、可选 API base URL | App Secret | event URL 为 /enterprise-events/{connectionId}/DingTalk,签名 secret 单独配置 |
先创建但保持 connection、login 和 provisioning disabled,注入 secret 后执行 Test connection,再在上游登记 callback、SCIM 或 event URL。上游 scope 只授予实际启用的登录、用户和部门读取能力。Microsoft Entra ID 连接测试使用 client credential 请求 Graph .default scope;生产 consent 必须与此一致。
SCIM 使用独立 Bearer authentication scheme,不接受浏览器 cookie、PAT 或 Git Basic credential。轮换 bearer 会立即使旧值失效;通过 Authorization: Bearer <token> 调用 Users/Groups create、query、PATCH 和分页接口。active=false 或完整目录对账发现用户缺失时,GitCandy 会锁定 Identity、刷新 security stamp、移除团队成员关系、撤销 PAT 并删除 SSH key。系统始终保护最后 TeamOwner 和至少一位本地、非企业托管的 break-glass TeamOwner。
企业微信、飞书和钉钉的主动目录同步由同进程 Quartz job 每 15 分钟执行。单个连接的失败不会阻止其他连接;游标会持久化以便重启恢复。只有完整 fresh scan 才停用缺失用户。飞书和钉钉 event endpoint 只在 1 MiB 限制、写入 rate limit、签名和 5 分钟时间窗通过后记录去重收据,不在 HTTP 请求内执行目录同步。
运行验收至少包括:连接测试、企业登录、同一 stable external ID 重复登录、SCIM create/query/PATCH、目录分页恢复、停用后的 Web/PAT/SSH 拒绝,以及 break-glass owner 仍可登录和修复配置。真实 Entra、企业微信、飞书和钉钉 tenant 的 consent、scope、回调域名、事件订阅和网络策略必须在部署环境验证;仓库测试 fixture 不包含生产 credential。
紧急功能回滚时先禁用对应 connection 或分别关闭 login/provisioning,并在 provider 侧撤销 client secret、SCIM bearer 或 event subscription。该方式保留 external identity mapping、游标和审计,便于修复后恢复。二进制回滚若跨越 M14 migrations,必须停止服务并恢复升级前数据库快照;不要让旧二进制连接含 M14 schema 的数据库。Data Protection keys 必须与数据库保持同一备份时间点,否则未完成的 enterprise login state 和现有 Identity cookie 会失效。
| 数据 | Compose | Linux | Windows | 备份要求 |
|---|---|---|---|---|
| SQLite database | /var/lib/gitcandy/GitCandy.db |
/var/lib/gitcandy/GitCandy.db |
%ProgramData%\GitCandy\GitCandy.db |
必须 |
| repositories | /var/lib/gitcandy/repositories |
/var/lib/gitcandy/repositories |
%ProgramData%\GitCandy\repositories |
必须 |
| cache | /var/lib/gitcandy/cache |
/var/lib/gitcandy/cache |
%ProgramData%\GitCandy\cache |
可选,可重建 |
| SSH host key | /var/lib/gitcandy/ssh-host-key.xml |
同左 | %ProgramData%\GitCandy\ssh-host-key.xml |
必须且按私钥保护 |
| Data Protection keys | /var/lib/gitcandy/data-protection-keys |
同左 | %ProgramData%\GitCandy\data-protection-keys |
必须 |
repository、cache、archive 和 delete 的子路径必须继续经过根目录边界检查。不要把 repository、cache、host key 或 key ring 放进程序安装目录;升级程序时只替换二进制。
GitCandy 不创建或轮转独立日志文件。运行时代码统一使用 ILogger<T>:容器从标准输出收集日志,systemd 使用 journald,Windows Service 使用已配置的 ASP.NET Core logging provider。日志级别由 Logging 配置节控制,保留与轮转策略由部署环境负责。
GitCandy:Observability:Enabled 默认开启 OpenTelemetry provider,但 OTLP 和诊断 Console exporter 默认关闭,因此未配置 collector 时不会发起 telemetry 网络连接,也不会重复打印 Console 日志。当前信号包括:
- tracing:ASP.NET Core 请求(排除高频
/health/live)、Git transport 和 Quartz job; - metrics:ASP.NET Core、.NET runtime、Git transport 操作数/活跃数/耗时,以及 Quartz 执行数/活跃数/耗时;
- logging:现有结构化
ILogger<T>日志,并携带可用的 trace/span correlation。
Git 和 scheduler 自定义标签只使用 service、result 等低基数字段,不加入 repository 名称、actor、物理路径、authorization header、token 或 key。ASP.NET Core 和现有应用日志仍可能包含普通请求路由或业务标识;collector 的访问控制、保留期和二次脱敏属于部署者责任,禁止把 OTLP headers 或其他 collector 凭据写入仓库配置。
推荐通过环境变量连接 OTLP collector:
GitCandy__Observability__Otlp__Enabled=true
OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317
OTEL_EXPORTER_OTLP_PROTOCOL=grpc
也可设置 GitCandy:Observability:Otlp:Endpoint 为绝对 HTTP/HTTPS 地址;留空时由 OpenTelemetry SDK 的标准环境变量或默认值决定。TraceSamplingRatio 接受 0 到 1,只影响 tracing;生产环境应根据流量降低采样率。ConsoleExporterEnabled=true 会把三类信号写到标准输出,仅用于本地诊断,不建议与生产日志长期同时启用。
GitCandy 不内置 collector、存储或 dashboard,也不暴露 Prometheus scrape endpoint。OTLP exporter 使用批量后台导出,collector 故障不会改变 Web/Git/SSH 协议响应。紧急回滚时先设置 GitCandy__Observability__Otlp__Enabled=false;若还需移除本地 provider,再设置 GitCandy__Observability__Enabled=false 并重启,不涉及数据库或文件系统迁移。
/health/live只确认 ASP.NET Core 进程可以处理请求,不访问依赖。/health/ready验证数据库连接、repository 可写、cache 可写、git --version和启用后的 SSH listener。
readiness 失败时返回 HTTP 503,默认响应不会暴露数据库连接串、物理路径或密钥内容。先查看应用日志,再检查目录 ACL、数据库文件、Git PATH 和 SSH 端口占用。
Release workflow 用固定在 .config/dotnet-tools.json 的 dotnet-ef 生成:
sqlite.sql:从空数据库创建当前 SQLite schema。sqlserver-idempotent.sql:用于 SQL Server schema 审阅和后续独立部署验证;当前主 host 仍是 SQLite-first。
生产升级前先审阅对应版本 SQL 和 migration 差异,并完成备份。普通 GitCandy 启动会检测并自动应用 pending migrations;GitCandy --migrate 保留为只迁移后退出的运维入口。自动迁移只处理当前 EF Core schema,不导入或改写旧 MVC5 用户数据库。
一致性备份必须阻止新 Web/SSH push,并停止 GitCandy 进程。SQLite 的 database、-wal、-shm 文件不能在写入期间随意分别复制。
- 停止
gitcandy容器或系统服务。 - 备份数据库文件、完整 repositories、SSH host key、Data Protection keys 和生产配置。
- cache 可跳过;恢复后由应用重建。
- 记录备份对应的 GitCandy image/version 和 migration 版本。
- 重新启动并验证 readiness、登录、clone/fetch/push 和 SSH host key 指纹。
发布门禁使用 tools/operations/Invoke-RecoveryRehearsal.ps1 对 GitCandy.db、repositories、LFS 和 Data Protection keys 建立同一 SHA-256 清单并恢复到隔离目录。生产演练仍必须先停止写入;脚本不会替代 SQLite 在线备份协议,也不会覆盖活动数据目录。版本回滚只能回到兼容当前 schema 的版本;否则先恢复与旧版本同时取得的完整一致性备份。
Compose 可在停止主服务后复制持久目录:
docker compose stop gitcandy
docker compose cp gitcandy:/var/lib/gitcandy ./backup/gitcandy
docker compose start gitcandy恢复时先停止服务,把当前损坏状态另存为诊断副本,再将同一备份集整体恢复到原路径并修复 owner/ACL。不要把旧数据库与较新的 repository/配置状态随意混合。
升级:
- 固定当前运行版本并完成一致性备份。
- 下载新版本产物,审阅 CHANGES 和 migration SQL。
- 停止旧服务并启动新版本;新进程会在开放 Web/SSH 端口前自动应用 pending migrations。
- 验证
/health/ready、登录、公开/私有权限、HTTP clone/fetch/push 和 SSH clone/fetch/push。
回滚:
- 立即停止新版本,避免继续写入。
- 恢复升级前的程序或 image tag。
- 如果新版本执行过 database migration,必须恢复升级前数据库快照;不要让旧二进制连接新 schema。
- 只有 repositories 在升级后发生写入或损坏时才恢复对应备份,并确认数据库 metadata 与物理 refs 一致。
- 恢复原配置、SSH host key 和 Data Protection keys,启动旧版本并重复健康检查和 Git 协议 smoke tests。
Compose 回滚应把 .env 中 GITCANDY_IMAGE 改回明确旧 tag 后执行 docker compose up -d。不要使用浮动 latest 作为可审计回滚点。