@aaronshaf/ger 1.2.11 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.ast-grep/rules/no-as-casting.yml +13 -0
- package/.claude-plugin/plugin.json +22 -0
- package/.github/workflows/ci-simple.yml +53 -0
- package/.github/workflows/ci.yml +171 -0
- package/.github/workflows/claude-code-review.yml +83 -0
- package/.github/workflows/claude.yml +50 -0
- package/.github/workflows/dependency-update.yml +84 -0
- package/.github/workflows/release.yml +166 -0
- package/.github/workflows/security-scan.yml +113 -0
- package/.github/workflows/security.yml +96 -0
- package/.husky/pre-commit +16 -0
- package/.husky/pre-push +25 -0
- package/.lintstagedrc.json +6 -0
- package/.tool-versions +1 -0
- package/CLAUDE.md +105 -0
- package/DEVELOPMENT.md +361 -0
- package/EXAMPLES.md +457 -0
- package/README.md +831 -16
- package/bin/ger +3 -18
- package/biome.json +36 -0
- package/bun.lock +678 -0
- package/bunfig.toml +8 -0
- package/docs/adr/0001-use-effect-for-side-effects.md +65 -0
- package/docs/adr/0002-use-bun-runtime.md +64 -0
- package/docs/adr/0003-store-credentials-in-home-directory.md +75 -0
- package/docs/adr/0004-use-commander-for-cli.md +76 -0
- package/docs/adr/0005-use-effect-schema-for-validation.md +93 -0
- package/docs/adr/0006-use-msw-for-api-mocking.md +89 -0
- package/docs/adr/0007-git-hooks-for-quality.md +94 -0
- package/docs/adr/0008-no-as-typecasting.md +83 -0
- package/docs/adr/0009-file-size-limits.md +82 -0
- package/docs/adr/0010-llm-friendly-xml-output.md +93 -0
- package/docs/adr/0011-ai-tool-strategy-pattern.md +102 -0
- package/docs/adr/0012-build-status-message-parsing.md +94 -0
- package/docs/adr/0013-git-subprocess-integration.md +98 -0
- package/docs/adr/0014-group-management-support.md +95 -0
- package/docs/adr/0015-batch-comment-processing.md +111 -0
- package/docs/adr/0016-flexible-change-identifiers.md +94 -0
- package/docs/adr/0017-git-worktree-support.md +102 -0
- package/docs/adr/0018-auto-install-commit-hook.md +103 -0
- package/docs/adr/0019-sdk-package-exports.md +95 -0
- package/docs/adr/0020-code-coverage-enforcement.md +105 -0
- package/docs/adr/0021-typescript-isolated-declarations.md +83 -0
- package/docs/adr/0022-biome-oxlint-tooling.md +124 -0
- package/docs/adr/README.md +30 -0
- package/docs/prd/README.md +12 -0
- package/docs/prd/architecture.md +325 -0
- package/docs/prd/commands.md +425 -0
- package/docs/prd/data-model.md +349 -0
- package/docs/prd/overview.md +124 -0
- package/index.ts +219 -0
- package/oxlint.json +24 -0
- package/package.json +82 -15
- package/scripts/check-coverage.ts +69 -0
- package/scripts/check-file-size.ts +38 -0
- package/scripts/fix-test-mocks.ts +55 -0
- package/skills/gerrit-workflow/SKILL.md +247 -0
- package/skills/gerrit-workflow/examples.md +572 -0
- package/skills/gerrit-workflow/reference.md +728 -0
- package/src/api/gerrit.ts +696 -0
- package/src/cli/commands/abandon.ts +65 -0
- package/src/cli/commands/add-reviewer.ts +156 -0
- package/src/cli/commands/build-status.ts +282 -0
- package/src/cli/commands/checkout.ts +422 -0
- package/src/cli/commands/comment.ts +460 -0
- package/src/cli/commands/comments.ts +85 -0
- package/src/cli/commands/diff.ts +71 -0
- package/src/cli/commands/extract-url.ts +266 -0
- package/src/cli/commands/groups-members.ts +104 -0
- package/src/cli/commands/groups-show.ts +169 -0
- package/src/cli/commands/groups.ts +137 -0
- package/src/cli/commands/incoming.ts +226 -0
- package/src/cli/commands/init.ts +164 -0
- package/src/cli/commands/mine.ts +115 -0
- package/src/cli/commands/open.ts +57 -0
- package/src/cli/commands/projects.ts +68 -0
- package/src/cli/commands/push.ts +430 -0
- package/src/cli/commands/rebase.ts +52 -0
- package/src/cli/commands/remove-reviewer.ts +123 -0
- package/src/cli/commands/restore.ts +50 -0
- package/src/cli/commands/review.ts +486 -0
- package/src/cli/commands/search.ts +162 -0
- package/src/cli/commands/setup.ts +286 -0
- package/src/cli/commands/show.ts +491 -0
- package/src/cli/commands/status.ts +35 -0
- package/src/cli/commands/submit.ts +108 -0
- package/src/cli/commands/vote.ts +119 -0
- package/src/cli/commands/workspace.ts +200 -0
- package/src/cli/index.ts +53 -0
- package/src/cli/register-commands.ts +659 -0
- package/src/cli/register-group-commands.ts +88 -0
- package/src/cli/register-reviewer-commands.ts +97 -0
- package/src/prompts/default-review.md +86 -0
- package/src/prompts/system-inline-review.md +135 -0
- package/src/prompts/system-overall-review.md +206 -0
- package/src/schemas/config.test.ts +245 -0
- package/src/schemas/config.ts +84 -0
- package/src/schemas/gerrit.ts +681 -0
- package/src/services/commit-hook.ts +314 -0
- package/src/services/config.test.ts +150 -0
- package/src/services/config.ts +250 -0
- package/src/services/git-worktree.ts +342 -0
- package/src/services/review-strategy.ts +292 -0
- package/src/test-utils/mock-generator.ts +138 -0
- package/src/utils/change-id.test.ts +98 -0
- package/src/utils/change-id.ts +63 -0
- package/src/utils/comment-formatters.ts +153 -0
- package/src/utils/diff-context.ts +103 -0
- package/src/utils/diff-formatters.ts +141 -0
- package/src/utils/formatters.ts +85 -0
- package/src/utils/git-commit.test.ts +277 -0
- package/src/utils/git-commit.ts +122 -0
- package/src/utils/index.ts +55 -0
- package/src/utils/message-filters.ts +26 -0
- package/src/utils/review-formatters.ts +89 -0
- package/src/utils/review-prompt-builder.ts +110 -0
- package/src/utils/shell-safety.ts +117 -0
- package/src/utils/status-indicators.ts +100 -0
- package/src/utils/url-parser.test.ts +271 -0
- package/src/utils/url-parser.ts +118 -0
- package/tests/abandon.test.ts +230 -0
- package/tests/add-reviewer.test.ts +579 -0
- package/tests/build-status-watch.test.ts +344 -0
- package/tests/build-status.test.ts +789 -0
- package/tests/change-id-formats.test.ts +268 -0
- package/tests/checkout/integration.test.ts +653 -0
- package/tests/checkout/parse-input.test.ts +55 -0
- package/tests/checkout/validation.test.ts +178 -0
- package/tests/comment-batch-advanced.test.ts +431 -0
- package/tests/comment-gerrit-api-compliance.test.ts +414 -0
- package/tests/comment.test.ts +708 -0
- package/tests/comments.test.ts +323 -0
- package/tests/config-service-simple.test.ts +100 -0
- package/tests/diff.test.ts +419 -0
- package/tests/extract-url.test.ts +517 -0
- package/tests/groups-members.test.ts +256 -0
- package/tests/groups-show.test.ts +323 -0
- package/tests/groups.test.ts +334 -0
- package/tests/helpers/build-status-test-setup.ts +83 -0
- package/tests/helpers/config-mock.ts +27 -0
- package/tests/incoming.test.ts +357 -0
- package/tests/init.test.ts +70 -0
- package/tests/integration/commit-hook.test.ts +246 -0
- package/tests/interactive-incoming.test.ts +173 -0
- package/tests/mine.test.ts +285 -0
- package/tests/mocks/msw-handlers.ts +80 -0
- package/tests/open.test.ts +233 -0
- package/tests/projects.test.ts +259 -0
- package/tests/rebase.test.ts +271 -0
- package/tests/remove-reviewer.test.ts +357 -0
- package/tests/restore.test.ts +237 -0
- package/tests/review.test.ts +135 -0
- package/tests/search.test.ts +712 -0
- package/tests/setup.test.ts +63 -0
- package/tests/show-auto-detect.test.ts +324 -0
- package/tests/show.test.ts +813 -0
- package/tests/status.test.ts +145 -0
- package/tests/submit.test.ts +316 -0
- package/tests/unit/commands/push.test.ts +194 -0
- package/tests/unit/git-branch-detection.test.ts +82 -0
- package/tests/unit/git-worktree.test.ts +55 -0
- package/tests/unit/patterns/push-patterns.test.ts +148 -0
- package/tests/unit/schemas/gerrit.test.ts +85 -0
- package/tests/unit/services/commit-hook.test.ts +132 -0
- package/tests/unit/services/review-strategy.test.ts +349 -0
- package/tests/unit/test-utils/mock-generator.test.ts +154 -0
- package/tests/unit/utils/comment-formatters.test.ts +415 -0
- package/tests/unit/utils/diff-context.test.ts +171 -0
- package/tests/unit/utils/diff-formatters.test.ts +165 -0
- package/tests/unit/utils/formatters.test.ts +411 -0
- package/tests/unit/utils/message-filters.test.ts +227 -0
- package/tests/unit/utils/shell-safety.test.ts +230 -0
- package/tests/unit/utils/status-indicators.test.ts +137 -0
- package/tests/vote.test.ts +317 -0
- package/tests/workspace.test.ts +295 -0
- package/tsconfig.json +36 -5
- package/src/commands/branch.ts +0 -196
- package/src/ger.ts +0 -22
- package/src/types.d.ts +0 -35
- package/src/utils.ts +0 -130
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# ADR 0009: Enforce File Size Limits
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
|
|
5
|
+
Accepted
|
|
6
|
+
|
|
7
|
+
## Context
|
|
8
|
+
|
|
9
|
+
Large files are harder to maintain, test, and understand. We need limits to encourage modular code.
|
|
10
|
+
|
|
11
|
+
## Decision
|
|
12
|
+
|
|
13
|
+
Enforce file size limits in pre-commit hooks:
|
|
14
|
+
- **Warning** at 500 lines
|
|
15
|
+
- **Error** (block commit) at 700 lines
|
|
16
|
+
|
|
17
|
+
## Rationale
|
|
18
|
+
|
|
19
|
+
- **Maintainability**: Smaller files are easier to understand
|
|
20
|
+
- **Single responsibility**: Large files often do too much
|
|
21
|
+
- **Code review**: Easier to review smaller, focused files
|
|
22
|
+
- **Testing**: Smaller modules are easier to test in isolation
|
|
23
|
+
|
|
24
|
+
## Consequences
|
|
25
|
+
|
|
26
|
+
### Positive
|
|
27
|
+
- Encourages modular architecture
|
|
28
|
+
- Easier code reviews
|
|
29
|
+
- Better test coverage (smaller units)
|
|
30
|
+
- Faster navigation in IDE
|
|
31
|
+
|
|
32
|
+
### Negative
|
|
33
|
+
- May need refactoring for legitimate large files
|
|
34
|
+
- Arbitrary thresholds may not fit all cases
|
|
35
|
+
- Some complex modules genuinely need more code
|
|
36
|
+
|
|
37
|
+
## Exclusions
|
|
38
|
+
|
|
39
|
+
The following are excluded from size checks:
|
|
40
|
+
- `node_modules/`
|
|
41
|
+
- `tmp/`
|
|
42
|
+
- Generated files
|
|
43
|
+
- Test fixture files
|
|
44
|
+
|
|
45
|
+
## Implementation
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
// scripts/check-file-size.ts
|
|
49
|
+
import { glob } from 'glob'
|
|
50
|
+
|
|
51
|
+
const WARN_THRESHOLD = 500
|
|
52
|
+
const ERROR_THRESHOLD = 700
|
|
53
|
+
|
|
54
|
+
const files = await glob('src/**/*.ts')
|
|
55
|
+
|
|
56
|
+
for (const file of files) {
|
|
57
|
+
const content = await Bun.file(file).text()
|
|
58
|
+
const lines = content.split('\n').length
|
|
59
|
+
|
|
60
|
+
if (lines > ERROR_THRESHOLD) {
|
|
61
|
+
console.error(`ERROR: ${file} has ${lines} lines (max: ${ERROR_THRESHOLD})`)
|
|
62
|
+
process.exit(1)
|
|
63
|
+
} else if (lines > WARN_THRESHOLD) {
|
|
64
|
+
console.warn(`WARN: ${file} has ${lines} lines (consider refactoring)`)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Current Large Files
|
|
70
|
+
|
|
71
|
+
Files approaching limits (as of v0.3.5):
|
|
72
|
+
- `src/cli/commands/show.ts` (~400 lines) - display formatting
|
|
73
|
+
- `src/cli/commands/comment.ts` (~300 lines) - batch processing
|
|
74
|
+
- `src/cli/commands/checkout.ts` (~300 lines) - patchset handling
|
|
75
|
+
|
|
76
|
+
## Refactoring Strategies
|
|
77
|
+
|
|
78
|
+
When files approach limits:
|
|
79
|
+
1. Extract utility functions to `src/utils/`
|
|
80
|
+
2. Split formatters into separate modules
|
|
81
|
+
3. Move constants to dedicated files
|
|
82
|
+
4. Extract sub-commands to separate files
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# ADR 0010: LLM-Friendly XML Output
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
|
|
5
|
+
Accepted
|
|
6
|
+
|
|
7
|
+
## Context
|
|
8
|
+
|
|
9
|
+
We want to integrate with AI tools (Claude, GPT, etc.) for automated code review and analysis. LLMs work better with structured, parseable output.
|
|
10
|
+
|
|
11
|
+
## Decision
|
|
12
|
+
|
|
13
|
+
Add `--xml` flag to all major commands that outputs structured XML with CDATA wrapping for special characters.
|
|
14
|
+
|
|
15
|
+
## Rationale
|
|
16
|
+
|
|
17
|
+
- **LLM consumption**: XML is well-understood by language models
|
|
18
|
+
- **Structured data**: Clear field separation vs prose
|
|
19
|
+
- **CDATA safety**: Handles special characters without escaping issues
|
|
20
|
+
- **Composability**: Pipe output to AI tools directly
|
|
21
|
+
- **Human readable**: XML is also readable by humans when needed
|
|
22
|
+
|
|
23
|
+
## Consequences
|
|
24
|
+
|
|
25
|
+
### Positive
|
|
26
|
+
- AI tools can parse output reliably
|
|
27
|
+
- Pipe directly to `llm`, `claude`, etc.
|
|
28
|
+
- Clear data boundaries
|
|
29
|
+
- No escaping ambiguity with CDATA
|
|
30
|
+
|
|
31
|
+
### Negative
|
|
32
|
+
- Verbose output
|
|
33
|
+
- Two code paths (text and XML)
|
|
34
|
+
- CDATA has its own edge cases
|
|
35
|
+
|
|
36
|
+
## Implementation
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
// --xml flag on commands
|
|
40
|
+
program
|
|
41
|
+
.command('show [change-id]')
|
|
42
|
+
.option('--xml', 'Output as XML for LLM consumption')
|
|
43
|
+
.action((changeId, options) => {
|
|
44
|
+
if (options.xml) {
|
|
45
|
+
outputXml(change)
|
|
46
|
+
} else {
|
|
47
|
+
outputPretty(change)
|
|
48
|
+
}
|
|
49
|
+
})
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## CDATA Sanitization
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
// src/utils/shell-safety.ts
|
|
56
|
+
export function sanitizeCDATA(text: string): string {
|
|
57
|
+
// CDATA cannot contain "]]>" - split and rejoin
|
|
58
|
+
return text.replace(/]]>/g, ']]]]><![CDATA[>')
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function wrapCDATA(text: string): string {
|
|
62
|
+
return `<![CDATA[${sanitizeCDATA(text)}]]>`
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Example Output
|
|
67
|
+
|
|
68
|
+
```xml
|
|
69
|
+
<change>
|
|
70
|
+
<number>12345</number>
|
|
71
|
+
<project>canvas-lms</project>
|
|
72
|
+
<subject><![CDATA[Fix login bug with special chars <>&]]></subject>
|
|
73
|
+
<diff><![CDATA[
|
|
74
|
+
--- a/file.ts
|
|
75
|
+
+++ b/file.ts
|
|
76
|
+
@@ -1,3 +1,4 @@
|
|
77
|
+
+import { something } from 'somewhere'
|
|
78
|
+
]]></diff>
|
|
79
|
+
</change>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Integration Examples
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
# Pipe to Claude for review
|
|
86
|
+
ger show 12345 --xml | claude "Review this change"
|
|
87
|
+
|
|
88
|
+
# Pipe to llm tool
|
|
89
|
+
ger diff 12345 --xml | llm "Summarize changes"
|
|
90
|
+
|
|
91
|
+
# Batch review with comment posting
|
|
92
|
+
llm "Review this diff" < <(ger diff 12345 --xml) | ger comment 12345
|
|
93
|
+
```
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# ADR 0011: AI Tool Strategy Pattern
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
|
|
5
|
+
Accepted
|
|
6
|
+
|
|
7
|
+
## Context
|
|
8
|
+
|
|
9
|
+
We want to support AI-powered code review, but don't want to hard-code a single AI tool. Users may have different tools installed.
|
|
10
|
+
|
|
11
|
+
## Decision
|
|
12
|
+
|
|
13
|
+
Implement a strategy pattern for AI tools with auto-detection.
|
|
14
|
+
|
|
15
|
+
## Rationale
|
|
16
|
+
|
|
17
|
+
- **No vendor lock-in**: Support multiple AI tools
|
|
18
|
+
- **User choice**: Users can specify preferred tool
|
|
19
|
+
- **Future-proof**: Easy to add new tools
|
|
20
|
+
- **Graceful fallback**: Try tools in priority order
|
|
21
|
+
|
|
22
|
+
## Supported Tools
|
|
23
|
+
|
|
24
|
+
| Tool | Command | Priority |
|
|
25
|
+
|------|---------|----------|
|
|
26
|
+
| Claude CLI | `claude` | 1 (highest) |
|
|
27
|
+
| llm | `llm` | 2 |
|
|
28
|
+
| opencode | `opencode` | 3 |
|
|
29
|
+
| Gemini | `gemini` | 4 |
|
|
30
|
+
|
|
31
|
+
## Consequences
|
|
32
|
+
|
|
33
|
+
### Positive
|
|
34
|
+
- Works with whatever AI tool user has
|
|
35
|
+
- Easy to add new strategies
|
|
36
|
+
- Configurable default via config file
|
|
37
|
+
- Graceful degradation
|
|
38
|
+
|
|
39
|
+
### Negative
|
|
40
|
+
- Must maintain multiple integrations
|
|
41
|
+
- Tool-specific output parsing
|
|
42
|
+
- Version compatibility concerns
|
|
43
|
+
|
|
44
|
+
## Implementation
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
// src/services/review-strategy.ts
|
|
48
|
+
interface ReviewStrategy {
|
|
49
|
+
name: string
|
|
50
|
+
isAvailable(): Promise<boolean>
|
|
51
|
+
executeReview(prompt: string, diff: string): Promise<string>
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const strategies: ReviewStrategy[] = [
|
|
55
|
+
claudeStrategy,
|
|
56
|
+
llmStrategy,
|
|
57
|
+
opencodeStrategy,
|
|
58
|
+
geminiStrategy,
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
export const findAvailableStrategy = async (): Promise<ReviewStrategy | null> => {
|
|
62
|
+
for (const strategy of strategies) {
|
|
63
|
+
if (await strategy.isAvailable()) {
|
|
64
|
+
return strategy
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return null
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Configuration
|
|
72
|
+
|
|
73
|
+
```json
|
|
74
|
+
// ~/.ger/config.json
|
|
75
|
+
{
|
|
76
|
+
"host": "https://gerrit.example.com",
|
|
77
|
+
"username": "user",
|
|
78
|
+
"password": "token",
|
|
79
|
+
"aiTool": "claude", // explicit tool choice
|
|
80
|
+
"aiAutoDetect": true // or auto-detect
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Response Extraction
|
|
85
|
+
|
|
86
|
+
AI tools may wrap responses in tags:
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
const extractResponse = (output: string): string => {
|
|
90
|
+
// Try to extract from <response> tags
|
|
91
|
+
const match = output.match(/<response>([\s\S]*?)<\/response>/)
|
|
92
|
+
return match ? match[1].trim() : output.trim()
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Multi-Stage Review
|
|
97
|
+
|
|
98
|
+
The `review` command uses two stages:
|
|
99
|
+
1. **Inline comments**: Line-specific feedback
|
|
100
|
+
2. **Overall review**: High-level assessment
|
|
101
|
+
|
|
102
|
+
Each stage uses the same strategy but different prompts from `src/prompts/`.
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# ADR 0012: Build Status via Message Parsing
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
|
|
5
|
+
Accepted
|
|
6
|
+
|
|
7
|
+
## Context
|
|
8
|
+
|
|
9
|
+
We need to report CI/CD build status for changes. Gerrit doesn't have a standard build status API - different instances configure CI differently.
|
|
10
|
+
|
|
11
|
+
## Decision
|
|
12
|
+
|
|
13
|
+
Parse change messages for build status patterns rather than relying on specific labels or plugins.
|
|
14
|
+
|
|
15
|
+
## Rationale
|
|
16
|
+
|
|
17
|
+
- **Universal**: Works with any Gerrit instance
|
|
18
|
+
- **No plugin dependency**: Doesn't require specific CI integration
|
|
19
|
+
- **Flexible**: Patterns can be adjusted per instance
|
|
20
|
+
- **Observable**: Same info visible in Gerrit UI
|
|
21
|
+
|
|
22
|
+
## Detected States
|
|
23
|
+
|
|
24
|
+
| State | Detection Pattern |
|
|
25
|
+
|-------|-------------------|
|
|
26
|
+
| `pending` | No build-related messages yet |
|
|
27
|
+
| `running` | "Build Started" message found |
|
|
28
|
+
| `success` | "Verified +1" after build messages |
|
|
29
|
+
| `failure` | "Verified -1" after build messages |
|
|
30
|
+
| `not_found` | Change doesn't exist |
|
|
31
|
+
|
|
32
|
+
## Consequences
|
|
33
|
+
|
|
34
|
+
### Positive
|
|
35
|
+
- Works out of box with most Gerrit setups
|
|
36
|
+
- No additional configuration needed
|
|
37
|
+
- Same logic users apply mentally
|
|
38
|
+
|
|
39
|
+
### Negative
|
|
40
|
+
- Pattern matching can have false positives
|
|
41
|
+
- Doesn't work with non-standard CI messages
|
|
42
|
+
- Can't get detailed build logs
|
|
43
|
+
|
|
44
|
+
## Implementation
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
// src/cli/commands/build-status.ts
|
|
48
|
+
const detectBuildState = (messages: ChangeMessage[]): BuildState => {
|
|
49
|
+
const sorted = [...messages].sort((a, b) =>
|
|
50
|
+
new Date(a.date).getTime() - new Date(b.date).getTime()
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
let buildStarted = false
|
|
54
|
+
let lastVerified: number | null = null
|
|
55
|
+
|
|
56
|
+
for (const msg of sorted) {
|
|
57
|
+
if (msg.message.includes('Build Started')) {
|
|
58
|
+
buildStarted = true
|
|
59
|
+
}
|
|
60
|
+
const verifiedMatch = msg.message.match(/Verified([+-]\d)/)
|
|
61
|
+
if (verifiedMatch) {
|
|
62
|
+
lastVerified = parseInt(verifiedMatch[1])
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (lastVerified === 1) return 'success'
|
|
67
|
+
if (lastVerified === -1) return 'failure'
|
|
68
|
+
if (buildStarted) return 'running'
|
|
69
|
+
return 'pending'
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Watch Mode
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
# Poll until terminal state
|
|
77
|
+
ger build-status 12345 --watch --interval 30 --timeout 1800
|
|
78
|
+
|
|
79
|
+
# Exit codes for CI pipelines
|
|
80
|
+
# 0: completed (any state, like gh run watch)
|
|
81
|
+
# 1: failure (only with --exit-status)
|
|
82
|
+
# 2: timeout
|
|
83
|
+
# 3: API error
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## JSON Output
|
|
87
|
+
|
|
88
|
+
```json
|
|
89
|
+
{
|
|
90
|
+
"changeId": "12345",
|
|
91
|
+
"state": "running",
|
|
92
|
+
"lastMessage": "Build Started: https://jenkins.example.com/job/123"
|
|
93
|
+
}
|
|
94
|
+
```
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# ADR 0013: Git Subprocess Integration
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
|
|
5
|
+
Accepted
|
|
6
|
+
|
|
7
|
+
## Context
|
|
8
|
+
|
|
9
|
+
We need to interact with Git for checkout, push, commit-msg hooks, and detecting Change-IDs. Options considered:
|
|
10
|
+
|
|
11
|
+
1. **isomorphic-git** - Pure JS Git implementation
|
|
12
|
+
2. **simple-git** - Node.js Git wrapper
|
|
13
|
+
3. **nodegit** - libgit2 bindings
|
|
14
|
+
4. **Subprocess spawning** - Shell out to git
|
|
15
|
+
|
|
16
|
+
## Decision
|
|
17
|
+
|
|
18
|
+
Shell out to the `git` command via `Bun.spawn()` rather than using a library.
|
|
19
|
+
|
|
20
|
+
## Rationale
|
|
21
|
+
|
|
22
|
+
- **No dependency**: Git is already installed on dev machines
|
|
23
|
+
- **Full feature support**: All git features available
|
|
24
|
+
- **Worktree support**: Libraries often struggle with worktrees
|
|
25
|
+
- **Familiar output**: Same output as manual git commands
|
|
26
|
+
- **No native bindings**: Avoid node-gyp/native module issues
|
|
27
|
+
|
|
28
|
+
## Consequences
|
|
29
|
+
|
|
30
|
+
### Positive
|
|
31
|
+
- Zero additional dependencies for Git
|
|
32
|
+
- Works with any Git version
|
|
33
|
+
- Full worktree support out of box
|
|
34
|
+
- Same behavior as command line
|
|
35
|
+
|
|
36
|
+
### Negative
|
|
37
|
+
- Error handling is string parsing
|
|
38
|
+
- Platform-specific edge cases
|
|
39
|
+
- Subprocess overhead per call
|
|
40
|
+
- Security: must sanitize inputs
|
|
41
|
+
|
|
42
|
+
## Implementation
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
// src/utils/git-commit.ts
|
|
46
|
+
export const runGit = async (args: string[]): Promise<{ stdout: string; stderr: string }> => {
|
|
47
|
+
const proc = Bun.spawn(['git', ...args], {
|
|
48
|
+
stdout: 'pipe',
|
|
49
|
+
stderr: 'pipe',
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
const stdout = await new Response(proc.stdout).text()
|
|
53
|
+
const stderr = await new Response(proc.stderr).text()
|
|
54
|
+
const exitCode = await proc.exited
|
|
55
|
+
|
|
56
|
+
if (exitCode !== 0) {
|
|
57
|
+
throw new GitError({ message: stderr.trim(), exitCode })
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return { stdout: stdout.trim(), stderr: stderr.trim() }
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Security Considerations
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
// Validate ref names to prevent injection
|
|
68
|
+
const isValidRefName = (ref: string): boolean => {
|
|
69
|
+
// Git ref naming rules
|
|
70
|
+
return /^[a-zA-Z0-9_\-/.]+$/.test(ref) &&
|
|
71
|
+
!ref.includes('..') &&
|
|
72
|
+
!ref.startsWith('-')
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Use array args, not shell string
|
|
76
|
+
// GOOD: spawn(['git', 'checkout', branchName])
|
|
77
|
+
// BAD: spawn(`git checkout ${branchName}`, { shell: true })
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Worktree Detection
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
// Handles both regular repos and worktrees
|
|
84
|
+
export const getGitDir = async (): Promise<string> => {
|
|
85
|
+
const { stdout } = await runGit(['rev-parse', '--git-dir'])
|
|
86
|
+
return path.resolve(stdout)
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Change-ID Extraction
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
export const extractChangeIdFromHead = async (): Promise<string | null> => {
|
|
94
|
+
const { stdout } = await runGit(['log', '-1', '--format=%b'])
|
|
95
|
+
const match = stdout.match(/Change-Id: (I[0-9a-f]{40})/)
|
|
96
|
+
return match ? match[1] : null
|
|
97
|
+
}
|
|
98
|
+
```
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# ADR 0014: Group Management Support
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
|
|
5
|
+
Accepted
|
|
6
|
+
|
|
7
|
+
## Context
|
|
8
|
+
|
|
9
|
+
Gerrit uses groups for access control and reviewer assignment. Large teams need to manage groups and add them as reviewers efficiently.
|
|
10
|
+
|
|
11
|
+
## Decision
|
|
12
|
+
|
|
13
|
+
Implement full group management commands: `groups`, `groups-show`, `groups-members`.
|
|
14
|
+
|
|
15
|
+
## Rationale
|
|
16
|
+
|
|
17
|
+
- **Team workflows**: Add entire teams as reviewers at once
|
|
18
|
+
- **Discovery**: Find groups by name, owner, project
|
|
19
|
+
- **Visibility**: See group membership without Gerrit UI
|
|
20
|
+
- **Automation**: Script group-based reviewer assignment
|
|
21
|
+
|
|
22
|
+
## Commands
|
|
23
|
+
|
|
24
|
+
| Command | Purpose |
|
|
25
|
+
|---------|---------|
|
|
26
|
+
| `groups` | List groups with filtering |
|
|
27
|
+
| `groups-show <id>` | Detailed group information |
|
|
28
|
+
| `groups-members <id>` | List group members |
|
|
29
|
+
| `add-reviewer --group` | Add group as reviewer |
|
|
30
|
+
|
|
31
|
+
## Consequences
|
|
32
|
+
|
|
33
|
+
### Positive
|
|
34
|
+
- Efficient team reviewer management
|
|
35
|
+
- Discoverable group information
|
|
36
|
+
- Scriptable group operations
|
|
37
|
+
- Consistent with other ger commands
|
|
38
|
+
|
|
39
|
+
### Negative
|
|
40
|
+
- Additional API endpoints to maintain
|
|
41
|
+
- Group permissions can be complex
|
|
42
|
+
- LDAP groups may have sync delays
|
|
43
|
+
|
|
44
|
+
## Implementation
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
// List groups with filters
|
|
48
|
+
export const listGroups = (options: GroupListOptions) =>
|
|
49
|
+
Effect.gen(function* () {
|
|
50
|
+
const api = yield* GerritApiService
|
|
51
|
+
const params = new URLSearchParams()
|
|
52
|
+
|
|
53
|
+
if (options.pattern) params.set('m', options.pattern)
|
|
54
|
+
if (options.owned) params.set('owned', '')
|
|
55
|
+
if (options.project) params.set('p', options.project)
|
|
56
|
+
if (options.user) params.set('user', options.user)
|
|
57
|
+
|
|
58
|
+
return yield* api.listGroups(params)
|
|
59
|
+
})
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Filtering Options
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
# Filter by name pattern
|
|
66
|
+
ger groups --pattern "team-*"
|
|
67
|
+
|
|
68
|
+
# Groups I own
|
|
69
|
+
ger groups --owned
|
|
70
|
+
|
|
71
|
+
# Groups with access to project
|
|
72
|
+
ger groups --project canvas-lms
|
|
73
|
+
|
|
74
|
+
# Groups a user belongs to
|
|
75
|
+
ger groups --user john.doe
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Add Group as Reviewer
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
# Add group to change
|
|
82
|
+
ger add-reviewer 12345 --group frontend-team
|
|
83
|
+
|
|
84
|
+
# Add as CC instead of reviewer
|
|
85
|
+
ger add-reviewer 12345 --group frontend-team --cc
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## API Endpoints
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
GET /a/groups/ # List groups
|
|
92
|
+
GET /a/groups/{id} # Group info
|
|
93
|
+
GET /a/groups/{id}/detail # Detailed info with members
|
|
94
|
+
GET /a/groups/{id}/members # Member list
|
|
95
|
+
```
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# ADR 0015: Batch Comment Processing
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
|
|
5
|
+
Accepted
|
|
6
|
+
|
|
7
|
+
## Context
|
|
8
|
+
|
|
9
|
+
AI tools generate multiple inline comments at once. We need to post them efficiently rather than one-by-one.
|
|
10
|
+
|
|
11
|
+
## Decision
|
|
12
|
+
|
|
13
|
+
Accept JSON array input for bulk inline comments with schema validation.
|
|
14
|
+
|
|
15
|
+
## Rationale
|
|
16
|
+
|
|
17
|
+
- **AI integration**: AI tools output structured comment lists
|
|
18
|
+
- **Efficiency**: Single API call for multiple comments
|
|
19
|
+
- **Validation**: Schema ensures correct format before posting
|
|
20
|
+
- **Features**: Support ranges, sides, resolution state
|
|
21
|
+
|
|
22
|
+
## Comment Schema
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
interface InlineComment {
|
|
26
|
+
file: string // File path
|
|
27
|
+
line?: number // Single line
|
|
28
|
+
range?: { // Or line range
|
|
29
|
+
start_line: number
|
|
30
|
+
end_line: number
|
|
31
|
+
start_character?: number
|
|
32
|
+
end_character?: number
|
|
33
|
+
}
|
|
34
|
+
message: string // Comment text
|
|
35
|
+
side?: 'PARENT' | 'REVISION' // Which side of diff
|
|
36
|
+
unresolved?: boolean // Mark as unresolved
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Consequences
|
|
41
|
+
|
|
42
|
+
### Positive
|
|
43
|
+
- One API call for many comments
|
|
44
|
+
- Full Gerrit comment features
|
|
45
|
+
- Piped input from AI tools
|
|
46
|
+
- Schema validation catches errors early
|
|
47
|
+
|
|
48
|
+
### Negative
|
|
49
|
+
- JSON format is verbose
|
|
50
|
+
- Must handle malformed input gracefully
|
|
51
|
+
- Range calculations can be complex
|
|
52
|
+
|
|
53
|
+
## Implementation
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
// src/cli/commands/comment.ts
|
|
57
|
+
export const postBatchComments = (changeId: string, comments: InlineComment[]) =>
|
|
58
|
+
Effect.gen(function* () {
|
|
59
|
+
const api = yield* GerritApiService
|
|
60
|
+
|
|
61
|
+
// Group by file
|
|
62
|
+
const byFile: Record<string, CommentInput[]> = {}
|
|
63
|
+
for (const comment of comments) {
|
|
64
|
+
const input: CommentInput = {
|
|
65
|
+
message: comment.message,
|
|
66
|
+
line: comment.line,
|
|
67
|
+
range: comment.range,
|
|
68
|
+
side: comment.side,
|
|
69
|
+
unresolved: comment.unresolved ?? true,
|
|
70
|
+
}
|
|
71
|
+
byFile[comment.file] ??= []
|
|
72
|
+
byFile[comment.file].push(input)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Post all at once
|
|
76
|
+
yield* api.postReview(changeId, { comments: byFile })
|
|
77
|
+
})
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Usage Examples
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
# From file
|
|
84
|
+
cat comments.json | ger comment 12345
|
|
85
|
+
|
|
86
|
+
# From AI tool
|
|
87
|
+
llm "Review this diff, output JSON comments" < diff.txt | ger comment 12345
|
|
88
|
+
|
|
89
|
+
# Inline JSON
|
|
90
|
+
echo '[{"file":"src/index.ts","line":42,"message":"Consider null check"}]' | ger comment 12345
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Validation
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
const InlineCommentSchema = Schema.Struct({
|
|
97
|
+
file: Schema.String,
|
|
98
|
+
line: Schema.optional(Schema.Number),
|
|
99
|
+
range: Schema.optional(Schema.Struct({
|
|
100
|
+
start_line: Schema.Number,
|
|
101
|
+
end_line: Schema.Number,
|
|
102
|
+
start_character: Schema.optional(Schema.Number),
|
|
103
|
+
end_character: Schema.optional(Schema.Number),
|
|
104
|
+
})),
|
|
105
|
+
message: Schema.String,
|
|
106
|
+
side: Schema.optional(Schema.Literal('PARENT', 'REVISION')),
|
|
107
|
+
unresolved: Schema.optional(Schema.Boolean),
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
const CommentsArraySchema = Schema.Array(InlineCommentSchema)
|
|
111
|
+
```
|