UNPKG

@aaronshaf/ger

Version:

Gerrit CLI and SDK - A modern CLI tool and TypeScript SDK for Gerrit Code Review, built with Effect-TS

104 lines (75 loc) 2.5 kB
# ADR 0018: Auto-Install Gerrit Commit-msg Hook ## Status Accepted ## Context Gerrit requires a Change-Id footer in commit messages. This is typically added by a commit-msg hook that must be installed manually. ## Decision Auto-install the Gerrit commit-msg hook when pushing if not present. ## Rationale - **Convenience**: Users don't need manual setup - **Error prevention**: Avoid "missing Change-Id" rejections - **Standard practice**: Same hook Gerrit provides - **Idempotent**: Safe to run multiple times ## Consequences ### Positive - Just works out of the box - No manual hook installation - Consistent Change-Id format - Reduces onboarding friction ### Negative - Modifies user's git hooks - Hook download requires network - Must handle worktrees correctly ## Implementation ```typescript // src/services/commit-hook.ts export const ensureCommitHook = async (gerritHost: string): Promise<void> => { const hooksDir = await getHooksDir() const hookPath = path.join(hooksDir, 'commit-msg') // Check if hook exists and is executable try { const stat = await Bun.file(hookPath).stat() if (stat.mode & 0o111) { return // Already installed and executable } } catch { // Hook doesn't exist, continue to install } // Download from Gerrit const hookUrl = `${gerritHost}/tools/hooks/commit-msg` const response = await fetch(hookUrl) if (!response.ok) { throw new Error(`Failed to download commit-msg hook: ${response.status}`) } const hookContent = await response.text() // Write with executable permissions await Bun.write(hookPath, hookContent, { mode: 0o755 }) console.log(`Installed commit-msg hook to ${hookPath}`) } ``` ## Hook Behavior The Gerrit commit-msg hook: 1. Checks if Change-Id already exists in message 2. If not, generates a unique Change-Id 3. Appends `Change-Id: I...` to commit message 4. Change-Id is SHA-1 based on tree, parent, author, committer ## When to Install ```typescript // Install before push operations export const pushCommand = (options: PushOptions) => Effect.gen(function* () { const config = yield* ConfigService // Ensure hook is installed before push yield* Effect.tryPromise(() => ensureCommitHook(config.host)) // Proceed with push yield* performPush(options) }) ``` ## Manual Override Users can skip auto-install: ```bash # Environment variable GERRIT_SKIP_HOOK=1 ger push # Or install their own hook # (ger won't overwrite existing executable hooks) ```