diff --git a/etc/gitks.env b/etc/gitks.env new file mode 100644 index 0000000..af25763 --- /dev/null +++ b/etc/gitks.env @@ -0,0 +1,35 @@ +# GitKS environment configuration +# +# This file is sourced by systemd via EnvironmentFile. +# Uncomment and adjust values as needed. +# +# Location: /etc/gitks/gitks.env +# Permissions: 0640 root:gitks + +REPO_PREFIX_PATH=/data/repos +GITKS_HOST=0.0.0.0 +GITKS_PORT=50051 +# GITKS_ADVERTISE_ADDR=http://node1.example.com:50051 +GITKS_METRICS_PORT=9100 + +GITKS_CLUSTER_PORT=4697 +GITKS_CLUSTER_COOKIE=gitks-default-cookie +# GITKS_CLUSTER_HOSTNAME=node1.example.com +GITKS_ETCD_ENDPOINTS=http://localhost:2379 +GITKS_ETCD_CONNECT_TIMEOUT=5000 +GITKS_LEASE_TTL=15 +GITKS_HEALTH_CHECK_INTERVAL=1 +GITKS_MAX_HEALTH_FAILURES=10 +GITKS_DISK_CACHE_ENABLED=false +GITKS_DISK_CACHE_MAX_AGE=300 +GITKS_PACK_CACHE_ENABLED=false +GITKS_PACK_CACHE_BACKPRESSURE=true +GITKS_HOOKS_ENABLED=true +GITKS_HOOK_TIMEOUT=30 +GITKS_ALLOW_CUSTOM_HOOKS=true +# GITKS_SERVER_HOOKS_DIR=/etc/gitks/hooks +# GITKS_HOOK_CALLBACK_ADDR=http://localhost:50052 +GITKS_RATE_LIMIT_MAX_CONCURRENT=200 + +# === LOGGING === +RUST_LOG=info diff --git a/etc/install.sh b/etc/install.sh new file mode 100755 index 0000000..f76455c --- /dev/null +++ b/etc/install.sh @@ -0,0 +1,132 @@ +#!/usr/bin/env bash +# GitKS bare-metal installation script +# +# Installs the gitks binary, systemd service, environment config, +# logrotate config, and creates the service user and data directory. +# +# Usage: +# sudo ./install.sh # install from local build +# sudo ./install.sh /path/to/gitks # install from prebuilt binary +# sudo GITKS_USER=custom ./install.sh # use a different service user + +set -euo pipefail + +GITKS_USER="${GITKS_USER:-gitks}" +GITKS_GROUP="${GITKS_GROUP:-gitks}" +BINARY_SRC="${1:-target/release/gitks}" +INSTALL_PREFIX="${INSTALL_PREFIX:-/usr/local}" +DATA_DIR="${DATA_DIR:-/data/repos}" + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +log() { echo -e "${GREEN}[INFO]${NC} $*"; } +warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } +err() { echo -e "${RED}[ERROR]${NC} $*"; exit 1; } + +# ── Preflight checks ────────────────────────────────────────────── +[[ $EUID -eq 0 ]] || err "This script must be run as root (use sudo)" + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +PROJECT_DIR="$(dirname "$SCRIPT_DIR")" + +command -v git >/dev/null 2>&1 || { + warn "git not found in PATH. Installing git..." + if command -v apt-get >/dev/null 2>&1; then + apt-get update -qq && apt-get install -y -qq git + elif command -v yum >/dev/null 2>&1; then + yum install -y git + elif command -v dnf >/dev/null 2>&1; then + dnf install -y git + else + err "Cannot install git automatically. Please install git first." + fi +} + +# ── Create service user ─────────────────────────────────────────── +if id "$GITKS_USER" &>/dev/null; then + log "User '$GITKS_USER' already exists" +else + log "Creating system user '$GITKS_USER'" + useradd --system --user-group --home-dir /var/lib/gitks \ + --shell /usr/sbin/nologin --comment "GitKS service" "$GITKS_USER" +fi + +# ── Install binary ──────────────────────────────────────────────── +BINARY_DST="${INSTALL_PREFIX}/bin/gitks" +if [[ -f "$BINARY_SRC" ]]; then + log "Installing gitks binary: $BINARY_SRC → $BINARY_DST" + install -o root -g root -m 0755 "$BINARY_SRC" "$BINARY_DST" +elif [[ -f "$PROJECT_DIR/$BINARY_SRC" ]]; then + log "Installing gitks binary: $PROJECT_DIR/$BINARY_SRC → $BINARY_DST" + install -o root -g root -m 0755 "$PROJECT_DIR/$BINARY_SRC" "$BINARY_DST" +else + warn "Binary not found at '$BINARY_SRC'. Skipping binary install." + warn "Build first: cargo build --release" + warn "Or specify path: sudo ./install.sh /path/to/gitks" +fi + +# ── Create directories ──────────────────────────────────────────── +log "Creating directories..." +install -d -o "$GITKS_USER" -g "$GITKS_GROUP" -m 0750 /var/lib/gitks +install -d -o "$GITKS_USER" -g "$GITKS_GROUP" -m 0750 /var/log/gitks +install -d -o root -g root -m 0755 /etc/gitks +[[ -d "$DATA_DIR" ]] || { + log "Creating repo data directory: $DATA_DIR" + mkdir -p "$DATA_DIR" + chown "$GITKS_USER:$GITKS_GROUP" "$DATA_DIR" +} + +# ── Install config ──────────────────────────────────────────────── +CONF_SRC="${PROJECT_DIR}/etc/gitks.env" + +if [[ -f /etc/gitks/gitks.env ]]; then + warn "/etc/gitks/gitks.env already exists — skipping (preserving existing config)" +else + log "Installing environment config..." + install -o root -g "$GITKS_GROUP" -m 0640 "$CONF_SRC" /etc/gitks/gitks.env + log "Edit /etc/gitks/gitks.env to configure your deployment" +fi + +# ── Install systemd service ─────────────────────────────────────── +log "Installing systemd service..." +install -o root -g root -m 0644 \ + "${PROJECT_DIR}/etc/systemd/gitks.service" \ + /etc/systemd/system/gitks.service + +# ── Install logrotate config ────────────────────────────────────── +LOG_ROTATE_SRC="${PROJECT_DIR}/etc/logrotate.d/gitks" +if [[ -d /etc/logrotate.d ]]; then + if [[ -f /etc/logrotate.d/gitks ]]; then + warn "/etc/logrotate.d/gitks already exists — skipping" + else + log "Installing logrotate config..." + install -o root -g root -m 0644 "$LOG_ROTATE_SRC" /etc/logrotate.d/gitks + fi +fi + +# ── Reload systemd & enable ─────────────────────────────────────── +log "Reloading systemd..." +systemctl daemon-reload + +log "Enabling gitks to start on boot..." +systemctl enable gitks.service + +# ── Done ────────────────────────────────────────────────────────── +echo "" +log "Installation complete!" +echo "" +echo " Next steps:" +echo " 1. Edit config: vim /etc/gitks/gitks.env" +echo " 2. Start service: systemctl start gitks" +echo " 3. Check status: systemctl status gitks" +echo " 4. View logs: journalctl -fu gitks" +echo "" +echo " Cluster setup (multi-node):" +echo " - Set STORAGE_NAME uniquely per node" +echo " - Set GITKS_CLUSTER_HOSTNAME to each node's hostname" +echo " - Set GITKS_ADVERTISE_ADDR to each node's reachable IP:port" +echo " - Point GITKS_ETCD_ENDPOINTS to your etcd cluster" +echo "" diff --git a/etc/logrotate.d/gitks b/etc/logrotate.d/gitks new file mode 100644 index 0000000..6cd017a --- /dev/null +++ b/etc/logrotate.d/gitks @@ -0,0 +1,22 @@ +# GitKS logrotate configuration +# +# Rotates file-based application logs if gitks is configured +# to write logs to a file instead of journald. +# +# Location: /etc/logrotate.d/gitks +# Permissions: 0644 root:root + +/var/log/gitks/*.log { + daily + missingok + rotate 14 + compress + delaycompress + notifempty + create 0640 gitks gitks + sharedscripts + postrotate + # Notify gitks to reopen log files if needed + /bin/systemctl kill -s HUP gitks.service 2>/dev/null || true + endscript +} diff --git a/etc/systemd/gitks.service b/etc/systemd/gitks.service new file mode 100644 index 0000000..052ea66 --- /dev/null +++ b/etc/systemd/gitks.service @@ -0,0 +1,59 @@ +[Unit] +Description=GitKS - gRPC Git Bare Repository Operations Service +Documentation=https://github.com/example/gitks +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +User=gitks +Group=gitks + +# Environment +EnvironmentFile=/etc/gitks/gitks.env + +# Runtime directories +RuntimeDirectory=gitks +StateDirectory=gitks +WorkingDirectory=/var/lib/gitks + +# Executable (assumes installed to /usr/local/bin/gitks) +ExecStart=/usr/local/bin/gitks + +# Restart policy +Restart=on-failure +RestartSec=5s + +# Graceful shutdown — send SIGTERM, wait, then SIGKILL +KillMode=mixed +KillSignal=SIGTERM +TimeoutStopSec=30s + +# Sandbox hardening +NoNewPrivileges=yes +PrivateTmp=yes +ProtectSystem=strict +ProtectHome=yes +ReadWritePaths=/var/lib/gitks /data/repos +ReadOnlyPaths=/etc/gitks +ProtectKernelTunables=yes +ProtectKernelModules=yes +ProtectControlGroups=yes +RestrictRealtime=yes +RestrictNamespaces=yes +LockPersonality=yes +MemoryDenyWriteExecute=no +RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX +SystemCallFilter=@system-service +SystemCallErrorNumber=EPERM + +# Logging to journald +StandardOutput=journal +StandardError=journal +SyslogIdentifier=gitks + +# Limit file descriptors (git may open many files) +LimitNOFILE=65536 + +[Install] +WantedBy=multi-user.target diff --git a/task.md b/task.md deleted file mode 100644 index 1a5a8b9..0000000 --- a/task.md +++ /dev/null @@ -1,225 +0,0 @@ -# GitKS RPC 补齐任务 - -> 对照 Gitaly 分析后,梳理有必要实现但目前缺失的功能,按优先级排列。 -> 每个任务标注:类别、预估工作量、前置依赖、实现思路。 - ---- - -## P0 — 核心功能缺失(影响基本使用场景) - -### P0-1. `RefService` — 原子性引用操作 - -| 项 | 内容 | -|------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| **新增 RPC** | `UpdateReferences`, `DeleteRefs`, `FindDefaultBranchName`, `RefExists` | -| **Proto** | 新建 `proto/ref.proto`(独立 RefService) | -| **工作量** | **M**(3-5 天) | -| **为什么必要** | 当前 `branch.proto` / `tag.proto` 每个操作单独一个 RPC,无法做批量原子更新。`UpdateReferences` 是 Gitaly 中最核心的写操作之一,支持 `expected_old_oid` 校验 | -| **实现思路** | 1. 新建 `ref/` 模块 2. `UpdateReferences` 调用 `git update-ref --stdin` 批量原子更新 3. `DeleteRefs` 调用 `git update-ref -d` 批量删除 4. `RefExists` 用 `gix` 检查 reference 是否存在 5. `FindDefaultBranchName` 从已有的 `default_branch_name()` 抽取 | - -### P0-2. `RepositoryService` — FindMergeBase - -| 项 | 内容 | -|------------|-----------------------------------------------------------------------------| -| **新增 RPC** | `FindMergeBase` | -| **Proto** | 扩展现有 `proto/repository.proto` | -| **工作量** | **S**(1 天) | -| **为什么必要** | diff、merge、rebase 操作都依赖 merge-base 计算。当前 GitKS 的 merge/diff 模块各自计算,缺少独立 API | -| **实现思路** | 调用 `gix::Repository::merge_base()` 返回两个 revision 的 merge base OID | - -### P0-3. `RepositoryService` — SearchFiles(代码搜索) - -| 项 | 内容 | -|------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| **新增 RPC** | `SearchFilesByContent`, `SearchFilesByName` | -| **Proto** | 扩展现有 `proto/repository.proto` | -| **工作量** | **M**(2-3 天) | -| **为什么必要** | 代码搜索是代码托管平台的基础功能,当前完全缺失 | -| **实现思路** | `SearchFilesByContent` → `git grep -I --line-number --column `;`SearchFilesByName` → `git ls-tree -r --name-only ` + 正则过滤。需注意大仓库性能(加 timeout、limit) | - -### P0-4. `RepositoryService` — WriteRef - -| 项 | 内容 | -|------------|------------------------------------------------------------------------------------------| -| **新增 RPC** | `WriteRef` | -| **Proto** | 扩展现有 `proto/repository.proto` | -| **工作量** | **S**(0.5 天) | -| **为什么必要** | 直接写 ref 是最底层的仓库操作,Replica 同步、快照恢复都依赖此能力。当前 `sync.rs` 中 `update_local_ref` 是内部函数,应暴露为 RPC | -| **实现思路** | `git update-ref ` -- 已有 `update_local_ref` 可直接封装 | - ---- - -## P1 — 重要功能缺失(影响高级场景) - -### P1-1. `RemoteService` — 远程仓库交互 - -| 项 | 内容 | -|----------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| **新增 Service** | `RemoteService`(3 个 RPC) | -| **新增 RPC** | `FindRemoteRepository`, `FindRemoteRootRef`, `UpdateRemoteMirror` | -| **Proto** | 新建 `proto/remote.proto` | -| **工作量** | **L**(5-7 天) | -| **为什么必要** | 支持从远程 URL 导入仓库、镜像同步。`FetchRemote` 在 Gitaly 的 RepositoryService 中也有对应 | -| **实现思路** | 1. `FindRemoteRepository` → `git ls-remote ` 2. `FindRemoteRootRef` → 取 ls-remote 的 HEAD 3. `UpdateRemoteMirror` → `git remote add` + `git fetch --mirror` + 清理。需注意认证(支持 SSH key / token 注入) | - -### P1-2. `RepositoryService` — FetchRemote / CreateRepositoryFromURL - -| 项 | 内容 | -|------------|----------------------------------------------------------------------------------------------------------------------------------------------| -| **新增 RPC** | `FetchRemote`, `CreateRepositoryFromURL` | -| **Proto** | 扩展现有 `proto/repository.proto` | -| **工作量** | **M**(3-4 天) | -| **为什么必要** | 仓库导入是核心 onboarding 流程,当前只能创建空仓库 | -| **实现思路** | `CreateRepositoryFromURL` → `git clone --bare --mirror `;`FetchRemote` → `git fetch `。复用 RemoteService 的认证基础设施 | - -### P1-3. `CommitService` — 扩展查询能力 - -| 项 | 内容 | -|------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| **新增 RPC** | `FindCommit`, `ListCommitsByOid`, `CommitIsAncestor`, `CheckObjectsExist`, `CommitsByMessage` | -| **Proto** | 扩展现有 `proto/commit.proto` | -| **工作量** | **M**(3-4 天) | -| **为什么必要** | 当前 `list_commits` / `get_commit` 太基础,缺少批量查询、ancestor 判断、message 搜索等常用模式 | -| **实现思路** | 1. `FindCommit` → `gix::Repository::find_object()` + 解析 Commit 2. `ListCommitsByOid` → 批量 `gix::Repository::find_commit()` 3. `CommitIsAncestor` → `gix::Repository::merge_base()` 判断 4. `CheckObjectsExist` → 批量 `gix::Repository::try_find()` 5. `CommitsByMessage` → `git log --all --grep=` | - -### P1-4. `RepositoryService` — ObjectsSize / RepositorySize - -| 项 | 内容 | -|------------|---------------------------------------------------------------------------------------------------------| -| **新增 RPC** | `ObjectsSize`, `RepositorySize` | -| **Proto** | 扩展现有 `proto/repository.proto` | -| **工作量** | **S**(1 天) | -| **为什么必要** | 前端需要展示仓库大小、文件大小,当前 `RepositoryStatistics` 只有对象计数没有大小 | -| **实现思路** | `ObjectsSize` → `git cat-file --batch-check` 批量获取对象大小;`RepositorySize` → `du -sb ` 或遍历 objects 目录 | - -### P1-5. `DiffService` — RawDiff / RawPatch - -| 项 | 内容 | -|------------|---------------------------------------------------------------------------------------------------------------------------------| -| **新增 RPC** | `RawDiff`, `RawPatch` | -| **Proto** | 扩展现有 `proto/diff.proto` | -| **工作量** | **S**(1 天) | -| **为什么必要** | 当前 `get_diff` 返回结构化 protobuf,对于大 diff 非常低效。Raw 格式可直接流式返回文本,用于 patch 应用、邮件发送 | -| **实现思路** | `RawDiff` → `git diff ..` streaming stdout;`RawPatch` → `git format-patch ..` streaming。注意大 diff 时的内存控制 | - -### P1-6. `CommitService` — CommitStats / LastCommitForPath - -| 项 | 内容 | -|------------|--------------------------------------------------------------------------------------------------------------------------------| -| **新增 RPC** | `CommitStats`, `LastCommitForPath` | -| **Proto** | 扩展现有 `proto/commit.proto` | -| **工作量** | **S**(1 天) | -| **为什么必要** | 文件列表需要显示最后修改 commit,commit 详情需要统计信息。当前 `CommitStats` 内嵌在 `Commit` message 中需额外请求才填充 | -| **实现思路** | `CommitStats` → `git diff --stat ^..` 解析输出;`LastCommitForPath` → `git log -1 --format=%H -- ` | - ---- - -## P2 — 锦上添花(完善体验) - -### P2-1. `RepositoryService` — FindLicense - -| 项 | 内容 | -|------------|-----------------------------------------------------------------------------------------------------------------| -| **新增 RPC** | `FindLicense` | -| **工作量** | **S**(1 天) | -| **实现思路** | 基于 GitHub Licensee 算法:读取 `LICENSE*` / `COPYING*` 文件 → 用 go-license-detector 等价逻辑(Rust 可用 `askalono` crate)做文本匹配 | - -### P2-2. `RepositoryService` — OptimizeRepository - -| 项 | 内容 | -|------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| **新增 RPC** | `OptimizeRepository` | -| **工作量** | **M**(2-3 天) | -| **实现思路** | 根据仓库状态自动决定优化策略:loose objects > N → `repack -d`;packfiles > N → `repack -ad`;没有 commit-graph → `commit-graph write`;没有 bitmap → `repack -adb`。比当前单独调用 `gc`/`repack`/`write_commit_graph` 更智能 | - -### P2-3. `RepositoryService` — GetRawChanges - -| 项 | 内容 | -|------------|------------------------------------------------------------------------------| -| **新增 RPC** | `GetRawChanges` | -| **工作量** | **S**(0.5 天) | -| **实现思路** | `git diff-tree --raw -r ..` 返回纯文件级变更列表(旧模式、新模式、状态),不生成完整 diff 内容 | - -### P2-4. `CommitService` — CountCommits / CountDivergingCommits - -| 项 | 内容 | -|------------|-----------------------------------------------------------------------------------------------------------------------------------| -| **新增 RPC** | `CountCommits`, `CountDivergingCommits` | -| **工作量** | **S**(0.5 天) | -| **实现思路** | `CountCommits` → `git rev-list --count `;`CountDivergingCommits` → `git rev-list --count --left-right ...` | - -### P2-5. `RefService` — FindRefsByOID / ListRefs(增强查询) - -| 项 | 内容 | -|------------|----------------------------------------------------------------------------------------------------------------------------------------------------------| -| **新增 RPC** | `FindRefsByOID`, `ListRefs` | -| **工作量** | **S**(1 天) | -| **实现思路** | `FindRefsByOID` → `git for-each-ref --points-at=`;`ListRefs` → `git for-each-ref --format=... --sort=...` 通用 ref 列表(当前只能在 branch/tag service 中分别查询) | - -### P2-6. `DiffService` — FindChangedPaths - -| 项 | 内容 | -|------------|-----------------------------------------------------------------------------------------------| -| **新增 RPC** | `FindChangedPaths` | -| **工作量** | **S**(0.5 天) | -| **实现思路** | `git diff-tree --name-status -r ..` 只返回变更的文件路径和状态(A/M/D/R),无 diff 内容,适合只展示文件列表的场景 | - ---- - -## P3 — 低优先级(生态特定 / 边缘场景) - -### P3-1. ObjectPoolService(Fork 去重) - -| 项 | 内容 | -|----------------|------------------------------------------------------------------------------| -| **新增 Service** | `ObjectPoolService`(6 个 RPC) | -| **工作量** | **XL**(2-3 周) | -| **前置依赖** | P0-1 `UpdateReferences` 稳定后 | -| **为什么低优** | Fork 去重是 GitLab.com 级别的需求。单租户或小规模部署用不上,且实现复杂(需管理 alternates、pool 生命周期、GC 协调) | - -### P3-2. HookService(Server 端 gRPC Hook 回调) - -| 项 | 内容 | -|----------------|------------------------------------------------------------------------------------| -| **新增 Service** | `HookService`(6 个 RPC) | -| **工作量** | **L**(1-2 周) | -| **为什么低优** | GitKS 的 hook 是内嵌脚本执行的"客户端模式",改为 gRPC 回调的"server 模式"需要对 hook runner 彻底重构,且需要下游客户端对接 | - -### P3-3. CommitService — GPG 签名相关 - -| 项 | 内容 | -|------------|-------------------------------------------------------------------------| -| **新增 RPC** | `GetCommitSignatures`, `FilterShasWithSignatures`, `GetTagSignatures` | -| **工作量** | **M**(2-3 天) | -| **为什么低优** | 需要 GPG 工具链依赖。可用 `gpg --verify` 或 `sequoia-openpgp`(Rust crate)实现,但非刚性需求 | - -### P3-4. SmartHTTP / SSH Service — Sidechannel + SSH 支持 - -| 项 | 内容 | -|-----------|--------------------------------------------------------------------------------------------------| -| **新增/扩展** | `PostUploadPackWithSidechannel`, `SSHUploadPack`, `SSHReceivePack` | -| **工作量** | **XL**(3-4 周) | -| **为什么低优** | Sidechannel 需要 Unix socket 旁路,平台依赖强。SSH 支持需要完整的 SSH server 协议栈(或依赖外部 SSH → gRPC 代理)。建议通过外部网关层来解决 | - -### P3-5. ServerService — 健康检查 / 磁盘统计 - -| 项 | 内容 | -|----------------|--------------------------------------------------------------------------------------------------------------------------| -| **新增 Service** | `ServerService`(4 个 RPC) | -| **工作量** | **S**(1 天) | -| **为什么低优** | GitKS 已有 Prometheus metrics endpoint + logging,`ServerInfo`/`ReadinessCheck`/`DiskStatistics` 更多用于 Kubernetes/平台集成。可快速补充 | - ---- - -## 汇总 - -| 优先级 | Service 数 | RPC 数 | 预估总工作量 | -|--------|-------------|---------|------------------------| -| **P0** | 2(扩展现有) | 9 | ~8 天 | -| **P1** | 1 新建 + 4 扩展 | 17 | ~17 天 | -| **P2** | 3 扩展 | 9 | ~7 天 | -| **P3** | 3 新建 + 2 扩展 | 22+ | ~10 周 | -| **合计** | | **57+** | **~13 周**(P0-P2 约 5 周) | - -> **建议路线**:优先完成 P0 + P1(共 26 个 RPC,约 4-5 周),可覆盖 80% 的常用场景。 -> P2 在核心功能稳定后逐步添加。P3 按实际用户需求驱动,不必全部实现。