feat: Add GitLab backend for bidirectional issue sync#1290
Closed
markthebest12 wants to merge 15 commits intosteveyegge:mainfrom
Closed
feat: Add GitLab backend for bidirectional issue sync#1290markthebest12 wants to merge 15 commits intosteveyegge:mainfrom
markthebest12 wants to merge 15 commits intosteveyegge:mainfrom
Conversation
TDD implementation of internal/gitlab/types.go: - Issue, User, Label, Milestone, IssueLink, Project types - SyncStats, SyncResult, Conflict for sync operations - Label prefix parsing for priority/status/type mapping - IsValidState helper for GitLab state validation All 8 tests passing. Relates to: se-devsvc/roz#4 Co-Authored-By: Claude Opus 4.5 <[email protected]>
TDD implementation of GitLab client for beads integration: - NewClient constructor with builder pattern (WithHTTPClient) - FetchIssues with pagination support via X-Next-Page header - FetchIssuesSince for incremental sync with updated_after param - CreateIssue for POST /projects/:id/issues - UpdateIssue for PUT /projects/:id/issues/:iid - GetIssueLinks for GET /projects/:id/issues/:iid/links - Retry logic with exponential backoff for rate limiting (429) - Proper error handling with status code context All 11 new tests pass (19 total in gitlab package). Closes steveyegge#5 Co-Authored-By: Claude Opus 4.5 <[email protected]>
Add bidirectional mapping between GitLab issues and beads issues: - MappingConfig for configurable field mappings (priority, status, type, relations) - GitLabIssueToBeads for import/pull operations - BeadsIssueToGitLabFields for export/push operations - IssueLinksToDependencies for dependency conversion - Support for scoped labels (priority::*, status::*, type::*) - FilterNonScopedLabels to preserve custom labels All 11 mapping tests passing (30 total in gitlab package). Co-Authored-By: Claude Opus 4.5 <[email protected]>
Implements Issue steveyegge#7 from the beads GitLab backend epic. Features: - bd gitlab status: Show GitLab configuration and sync status - bd gitlab sync: Sync issues with GitLab (--dry-run, --pull-only, --push-only) - bd gitlab projects: List accessible GitLab projects Configuration via bd config or environment variables: - gitlab.url / GITLAB_URL - gitlab.token / GITLAB_TOKEN - gitlab.project_id / GITLAB_PROJECT_ID Tests: 6 passing tests for config, validation, and command registration. Co-Authored-By: Claude Opus 4.5 <[email protected]>
…IssueLink Code review identified these issues: - ProjectID now URL-encoded via projectPath() helper for path-based IDs - Added WithEndpoint() builder for custom API endpoints - Added FetchIssueByIID() for fetching single issues - Added CreateIssueLink() for creating issue dependencies All 15 client tests pass. Ready for sync implementation. Co-Authored-By: Claude Opus 4.5 <[email protected]>
…ction Added gitlab_sync.go with full sync implementation: - doPullFromGitLab: imports GitLab issues to beads with incremental sync - doPushToGitLab: creates/updates GitLab issues from local beads issues - detectGitLabConflicts: finds issues modified on both sides since last sync - resolveGitLabConflictsByTimestamp: resolves conflicts using newer-wins - parseGitLabSourceSystem: parses "gitlab:projectID:iid" format Updated gitlab.go: - runGitLabSync now uses sync functions with --pull-only/--push-only flags - Added proper error handling and progress output 8 new tests in gitlab_sync_test.go, all passing. Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Fix path-based project ID comparison in doPushToGitLab (skip numeric comparison when client.ProjectID contains "/" since we can't reliably compare "group/project" with numeric 789) - Add atomic counter to generateIssueID() to prevent collisions when generating multiple IDs rapidly - Add Warnings field to PullStats to track non-fatal issues like failed gitlab.last_sync saves - Add tests for conflict resolution (resolveGitLabConflictsByTimestamp) All 4 code review issues addressed with TDD. Co-Authored-By: Claude Opus 4.5 <[email protected]>
Add --prefer-local, --prefer-gitlab, and --prefer-newer flags to bd gitlab sync: - --prefer-local: Always keep local beads version on conflict - --prefer-gitlab: Always use GitLab version on conflict - --prefer-newer: Use most recently updated version (default) Features: - Unified resolveGitLabConflicts() function with strategy parameter - Flag validation (only one conflict strategy allowed) - Automatic conflict detection and resolution in bidirectional sync - 7 new tests for conflict resolution Co-Authored-By: Claude Opus 4.5 <[email protected]>
Improve test coverage from 82.3% to 92.2% for internal/gitlab package: - types_test.go: Add tests for GetPriorityFromLabel, GetStatusFromLabel, GetTypeFromLabel helper functions (0% → 100%) - mapping_test.go: Add tests for priorityToLabel (42.9% → 100%) and IssueLinksToDependencies edge cases (73.3% → 100%) - client_test.go: Add error handling tests for UpdateIssue, GetIssueLinks, FetchIssueByIID, CreateIssueLink, CreateIssue, FetchIssuesSince All 379 new lines of tests pass. Co-Authored-By: Claude Opus 4.5 <[email protected]>
Add gitlab_integration_test.go with end-to-end tests: - TestGitLabSyncRoundtrip: Pull from GitLab, create local, push back - TestGitLabConflictStrategiesIntegration: prefer-local/gitlab/newer flags - TestGitLabConflictDetectionIntegration: Detect conflicts between local/GitLab - TestIncrementalSync: Full sync then incremental sync with last_sync timestamp Uses httptest mock servers and in-memory SQLite stores to test complete sync flows without external dependencies. Run with: go test -tags=integration ./cmd/bd/... Co-Authored-By: Claude Opus 4.5 <[email protected]>
P0 steveyegge#1: Conflict detection now happens BEFORE push to prevent data loss. Previously: Pull -> Push -> Detect conflicts (wrong - conflicts overwritten) Now: Pull -> Detect conflicts -> Push (skip conflicting issues) P0 steveyegge#2: Added SyncContext struct for thread-safe sync operations. - SyncContext holds store, actor, dbPath, issueIDCounter - WithContext variants of all sync functions - globalContextIDCounter for cross-context uniqueness - Enables concurrent sync operations without race conditions Co-Authored-By: Claude Opus 4.5 <[email protected]>
Add safeguards to prevent infinite loops in FetchIssues and FetchIssuesSince: 1. MaxPages constant (1000) prevents runaway pagination from malformed X-Next-Page headers that never return empty 2. Context cancellation check at start of each pagination loop iteration allows graceful shutdown and returns partial results when cancelled between requests Fixes potential DoS from malformed GitLab responses and improves responsiveness to cancellation signals. Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Fix swallowed json.Marshal error during retry (client.go line 114)
Now logs the error and continues to next retry attempt instead of
silently ignoring with _.
- Add GetIssue() helper to IssueConversion for type-safe access
Returns *types.Issue from interface{} field, nil if wrong type.
Avoids import cycle by keeping interface{} but providing safe accessor.
- Eliminate duplicate mapping definitions between types.go and mapping.go
Export PriorityMapping, StatusMapping, TypeMapping from types.go as
single source of truth. DefaultMappingConfig now uses these exports.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
P2 Issue steveyegge#9: Remove deprecated resolveGitLabConflictsByTimestamp function - Delete unused function that was superseded by resolveGitLabConflicts - Remove associated tests that tested the deprecated function P3 Issue steveyegge#10: Document cross-project link limitation - Add doc comment to CreateIssueLink clarifying same-project constraint - Note that cross-project links will error at GitLab API level P3 Issue steveyegge#11: Fix ID collision on restart - Add 4 random bytes to generateIssueID to prevent collisions - Counter resets on process restart, random component ensures uniqueness - Add test to verify random component is present P3 Issue steveyegge#12: Document error handling philosophy - Add Error Handling Contract comment at top of gitlab_sync.go - Documents fatal vs non-fatal error handling approach - Clarifies that stats track error counts for reporting Co-Authored-By: Claude Opus 4.5 <[email protected]>
Changes based on PR steveyegge#1290 code review: 1. Wire up SyncContext in runGitLabSync - SyncContext was created but not actually used in the main entry point 2. Delete legacy non-WithContext functions - removes code duplication and eliminates dead code maintenance burden. All sync operations now use WithContext variants exclusively 3. Fix --prefer-local behavior - conflicting issues now go to forceUpdateIDs (to force push) instead of skipUpdateIDs. This ensures local changes actually win when that strategy is selected 4. Document conflict detection 1-second tolerance - explains the timestamp comparison window and its limitations for clock skew 5. Document UUID alternative for ID generation - notes that distributed deployments may need UUIDs instead of the current timestamp+counter+random approach Test updates: - All tests now use WithContext functions - Removed global store manipulation from tests - All 18 GitLab tests pass Co-Authored-By: Claude Opus 4.5 <[email protected]>
steveyegge
pushed a commit
that referenced
this pull request
Jan 31, 2026
Changes based on PR #1290 code review: 1. Wire up SyncContext in runGitLabSync - SyncContext was created but not actually used in the main entry point 2. Delete legacy non-WithContext functions - removes code duplication and eliminates dead code maintenance burden. All sync operations now use WithContext variants exclusively 3. Fix --prefer-local behavior - conflicting issues now go to forceUpdateIDs (to force push) instead of skipUpdateIDs. This ensures local changes actually win when that strategy is selected 4. Document conflict detection 1-second tolerance - explains the timestamp comparison window and its limitations for clock skew 5. Document UUID alternative for ID generation - notes that distributed deployments may need UUIDs instead of the current timestamp+counter+random approach Test updates: - All tests now use WithContext functions - Removed global store manipulation from tests - All 18 GitLab tests pass Co-Authored-By: Claude Opus 4.5 <[email protected]> Executed-By: beads/crew/emma Rig: beads Role: crew
steveyegge
added a commit
that referenced
this pull request
Jan 31, 2026
Adds bidirectional GitLab issue sync (pull/push/conflict detection) with SyncContext for thread-safe operations. Includes 51+ unit tests and 4 integration tests. Original author: Mark Ramos (markthebest12) Review fixes: SQL injection-safe queries, HTTPS validation, io.LimitReader, type safety (IssueConversion.Issue), and integration test compilation.
Owner
|
Merged to main with review fixes. Thanks for the contribution, @markthebest12! Changes made during merge review:
All existing unit tests pass. Two pre-existing context cancellation test failures in |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
internal/gitlab/client.go) for issue operationsinternal/gitlab/mapping.go)bd gitlabcommand with sync, status, and projects subcommands (cmd/bd/gitlab.go)--prefer-local,--prefer-gitlab,--prefer-newer(default)updated_afterparameterFeatures
Test Plan
🤖 Generated with Claude Code