Skip to content

Comments

[WEB-INTEGRATION] feat: Add comprehensive GitHub integration#6

Open
pqhung3007 wants to merge 3 commits intopreviewfrom
claude/add-github-integration-01GSx7PFBUTAy2TpKn1JapDB
Open

[WEB-INTEGRATION] feat: Add comprehensive GitHub integration#6
pqhung3007 wants to merge 3 commits intopreviewfrom
claude/add-github-integration-01GSx7PFBUTAy2TpKn1JapDB

Conversation

@pqhung3007
Copy link
Owner

This commit implements a full-featured GitHub integration for Plane that enables seamless synchronization between GitHub repositories and Plane projects.

Features Implemented

1. Database Models

  • Enhanced GithubRepositorySync with sync direction and state mappings
  • Added GithubUserConnection for personal GitHub account connections
  • Added GithubPRStateMapping for PR state automation
  • Added GithubPRSync for tracking synced pull requests

2. API Endpoints

  • Workspace-level GitHub integration management
  • Personal GitHub account connection (OAuth flow)
  • Project-level repository sync configuration
  • PR state mapping configuration
  • GitHub webhook endpoint for real-time sync

3. Synchronization Features

  • Bidirectional Issue Sync: Sync issues between GitHub and Plane
    • Unidirectional mode (GitHub → Plane only)
    • Bidirectional mode (both directions)
  • PR State Automation: Automatically update Plane issue states based on PR lifecycle
    • Draft, opened, review requested, approved, merged, closed states
    • Smart reference detection with [ISSUE-123] syntax
  • Comment Sync: Sync comments between platforms with proper attribution
  • Label Sync: Automatically create and sync labels
  • Personal Account Integration: Post comments as individual users

4. Service Layer

  • GitHubSyncService: Handles all issue and comment synchronization
  • GitHubPRService: Manages PR state automation and issue linking
  • Helper functions for reference extraction and state mapping

5. State Mapping

  • Configurable mappings between GitHub issue states and Plane states
  • Configurable mappings for PR lifecycle events
  • Support for custom workflows

6. Security

  • Webhook signature verification using HMAC-SHA256
  • Encrypted token storage for OAuth credentials
  • Permission-based access control

7. Documentation

  • Comprehensive setup guide in GITHUB_INTEGRATION.md
  • API endpoint documentation
  • Usage examples and troubleshooting guide

API Routes

Workspace Level:

  • GET/POST /api/workspaces/{slug}/integrations/github/
  • DELETE /api/workspaces/{slug}/integrations/github/{id}/
  • GET /api/workspaces/{slug}/integrations/github/user-connections/
  • DELETE /api/workspaces/{slug}/integrations/github/user-connections/{id}/

Project Level:

  • GET/POST /api/workspaces/{slug}/projects/{id}/integrations/github/repository-syncs/
  • PATCH/DELETE /api/workspaces/{slug}/projects/{id}/integrations/github/repository-syncs/{id}/
  • GET/POST /api/workspaces/{slug}/projects/{id}/integrations/github/pr-state-mappings/
  • PATCH/DELETE /api/workspaces/{slug}/projects/{id}/integrations/github/pr-state-mappings/{id}/

Webhooks & OAuth:

  • GET /api/integrations/github/callback/
  • POST /api/integrations/github/webhook/

Technical Details

Models Updated/Created:

  • GithubRepository (existing)
  • GithubRepositorySync (enhanced)
  • GithubIssueSync (existing)
  • GithubCommentSync (existing)
  • GithubUserConnection (new)
  • GithubPRStateMapping (new)
  • GithubPRSync (new)

Serializers:

  • IntegrationSerializer
  • WorkspaceIntegrationSerializer
  • GithubRepositorySerializer
  • GithubRepositorySyncSerializer
  • GithubIssueSyncSerializer
  • GithubCommentSyncSerializer
  • GithubUserConnectionSerializer
  • GithubPRStateMappingSerializer
  • GithubPRSyncSerializer

Views:

  • GithubIntegrationViewSet
  • GithubRepositorySyncViewSet
  • GithubPRStateMappingViewSet
  • GithubUserConnectionViewSet
  • GithubWebhookEndpoint
  • GithubOAuthCallbackEndpoint

Migration

  • 0108_github_integration_enhancements.py

Configuration Required

Environment variables needed:

  • GITHUB_CLIENT_ID
  • GITHUB_CLIENT_SECRET
  • GITHUB_WEBHOOK_SECRET
  • GITHUB_ORGANIZATION_ID (optional)

Next Steps

Frontend implementation is needed to provide UI for:

  • GitHub integration setup wizard
  • Repository sync configuration
  • PR state mapping interface
  • Personal account connection flow
  • Integration status dashboard

References

Description

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • Feature (non-breaking change which adds functionality)
  • Improvement (change that would cause existing functionality to not work as expected)
  • Code refactoring
  • Performance improvements
  • Documentation update

Screenshots and Media (if applicable)

Test Scenarios

References

This commit implements a full-featured GitHub integration for Plane that enables
seamless synchronization between GitHub repositories and Plane projects.

## Features Implemented

### 1. Database Models
- Enhanced GithubRepositorySync with sync direction and state mappings
- Added GithubUserConnection for personal GitHub account connections
- Added GithubPRStateMapping for PR state automation
- Added GithubPRSync for tracking synced pull requests

### 2. API Endpoints
- Workspace-level GitHub integration management
- Personal GitHub account connection (OAuth flow)
- Project-level repository sync configuration
- PR state mapping configuration
- GitHub webhook endpoint for real-time sync

### 3. Synchronization Features
- **Bidirectional Issue Sync**: Sync issues between GitHub and Plane
  - Unidirectional mode (GitHub → Plane only)
  - Bidirectional mode (both directions)
- **PR State Automation**: Automatically update Plane issue states based on PR lifecycle
  - Draft, opened, review requested, approved, merged, closed states
  - Smart reference detection with [ISSUE-123] syntax
- **Comment Sync**: Sync comments between platforms with proper attribution
- **Label Sync**: Automatically create and sync labels
- **Personal Account Integration**: Post comments as individual users

### 4. Service Layer
- GitHubSyncService: Handles all issue and comment synchronization
- GitHubPRService: Manages PR state automation and issue linking
- Helper functions for reference extraction and state mapping

### 5. State Mapping
- Configurable mappings between GitHub issue states and Plane states
- Configurable mappings for PR lifecycle events
- Support for custom workflows

### 6. Security
- Webhook signature verification using HMAC-SHA256
- Encrypted token storage for OAuth credentials
- Permission-based access control

### 7. Documentation
- Comprehensive setup guide in GITHUB_INTEGRATION.md
- API endpoint documentation
- Usage examples and troubleshooting guide

## API Routes

Workspace Level:
- GET/POST /api/workspaces/{slug}/integrations/github/
- DELETE /api/workspaces/{slug}/integrations/github/{id}/
- GET /api/workspaces/{slug}/integrations/github/user-connections/
- DELETE /api/workspaces/{slug}/integrations/github/user-connections/{id}/

Project Level:
- GET/POST /api/workspaces/{slug}/projects/{id}/integrations/github/repository-syncs/
- PATCH/DELETE /api/workspaces/{slug}/projects/{id}/integrations/github/repository-syncs/{id}/
- GET/POST /api/workspaces/{slug}/projects/{id}/integrations/github/pr-state-mappings/
- PATCH/DELETE /api/workspaces/{slug}/projects/{id}/integrations/github/pr-state-mappings/{id}/

Webhooks & OAuth:
- GET /api/integrations/github/callback/
- POST /api/integrations/github/webhook/

## Technical Details

### Models Updated/Created:
- GithubRepository (existing)
- GithubRepositorySync (enhanced)
- GithubIssueSync (existing)
- GithubCommentSync (existing)
- GithubUserConnection (new)
- GithubPRStateMapping (new)
- GithubPRSync (new)

### Serializers:
- IntegrationSerializer
- WorkspaceIntegrationSerializer
- GithubRepositorySerializer
- GithubRepositorySyncSerializer
- GithubIssueSyncSerializer
- GithubCommentSyncSerializer
- GithubUserConnectionSerializer
- GithubPRStateMappingSerializer
- GithubPRSyncSerializer

### Views:
- GithubIntegrationViewSet
- GithubRepositorySyncViewSet
- GithubPRStateMappingViewSet
- GithubUserConnectionViewSet
- GithubWebhookEndpoint
- GithubOAuthCallbackEndpoint

## Migration
- 0108_github_integration_enhancements.py

## Configuration Required

Environment variables needed:
- GITHUB_CLIENT_ID
- GITHUB_CLIENT_SECRET
- GITHUB_WEBHOOK_SECRET
- GITHUB_ORGANIZATION_ID (optional)

## Next Steps

Frontend implementation is needed to provide UI for:
- GitHub integration setup wizard
- Repository sync configuration
- PR state mapping interface
- Personal account connection flow
- Integration status dashboard

## References
- Spec: https://docs.plane.so/integrations/github
- GitHub API: https://docs.github.com/en/rest
except Exception as e:
log_exception(e)
return Response(
{"error": str(e)},

Check warning

Code scanning / CodeQL

Information exposure through an exception Medium

Stack trace information
flows to this location and may be exposed to an external user.

Copilot Autofix

AI 3 months ago

The best fix is to replace the exception message presented to external users with a generic error. Instead of exposing str(e) directly, provide a message such as "An internal error has occurred" or "Failed to create repository sync", and continue logging the actual exception (with stack trace if possible) server-side. Only change line(s) where str(e) is used in the Response. It is appropriate to still log exception details internally via log_exception(e) as is, assuming that function does not leak info to the user. This change should occur in the except Exception as e block of the create method.

No new methods or imports are required, as the generic message is hardcoded, and internal logging is already present.

Suggested changeset 1
apps/api/plane/app/views/integration/github.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/apps/api/plane/app/views/integration/github.py b/apps/api/plane/app/views/integration/github.py
--- a/apps/api/plane/app/views/integration/github.py
+++ b/apps/api/plane/app/views/integration/github.py
@@ -270,7 +270,7 @@
         except Exception as e:
             log_exception(e)
             return Response(
-                {"error": str(e)},
+                {"error": "Failed to create repository sync"},
                 status=status.HTTP_500_INTERNAL_SERVER_ERROR
             )
 
EOF
@@ -270,7 +270,7 @@
except Exception as e:
log_exception(e)
return Response(
{"error": str(e)},
{"error": "Failed to create repository sync"},
status=status.HTTP_500_INTERNAL_SERVER_ERROR
)

Copilot is powered by AI and may make mistakes. Always verify output.
except Exception as e:
log_exception(e)
return Response(
{"error": str(e)},

Check warning

Code scanning / CodeQL

Information exposure through an exception Medium

Stack trace information
flows to this location and may be exposed to an external user.

Copilot Autofix

AI 3 months ago

To fix the problem, we need to ensure that the client only receives a generic error message in case of unexpected exceptions and that stack trace or detailed exception information is not included in the response. Instead of returning {"error": str(e)} in the exception handler, we should change it to something generic such as {"error": "An internal error occurred"}. The existing log_exception(e) call should remain, allowing developers to inspect the actual error and stack trace internally via logs. Only the block in the "except Exception as e" clause (lines 439-441) is affected, and no new imports or definitions are required as log_exception(e) is already present.

Suggested changeset 1
apps/api/plane/app/views/integration/github.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/apps/api/plane/app/views/integration/github.py b/apps/api/plane/app/views/integration/github.py
--- a/apps/api/plane/app/views/integration/github.py
+++ b/apps/api/plane/app/views/integration/github.py
@@ -437,7 +437,7 @@
         except Exception as e:
             log_exception(e)
             return Response(
-                {"error": str(e)},
+                {"error": "An internal error occurred"},
                 status=status.HTTP_500_INTERNAL_SERVER_ERROR
             )
 
EOF
@@ -437,7 +437,7 @@
except Exception as e:
log_exception(e)
return Response(
{"error": str(e)},
{"error": "An internal error occurred"},
status=status.HTTP_500_INTERNAL_SERVER_ERROR
)

Copilot is powered by AI and may make mistakes. Always verify output.
)

# Redirect back to frontend
return HttpResponseRedirect(redirect_url)

Check warning

Code scanning / CodeQL

URL redirection from remote source Medium

Untrusted URL redirection depends on a
user-provided value
.

Copilot Autofix

AI 3 months ago

The best way to fix this vulnerability is to validate redirect_url before passing it to HttpResponseRedirect. Since this is a Django application, the recommended approach is to use url_has_allowed_host_and_scheme from django.utils.http to check the safety of the redirect URL. Generally, only allow relative URLs or, if external URLs must be supported, restrict allowed hosts and schemes accordingly. If validation fails, redirect to a safe default (such as /). Implementation steps:

  • Import url_has_allowed_host_and_scheme.
  • Replace line 709 to check if redirect_url is allowed (if not, use '/').
  • Only edit the logic in the snippet shown (apps/api/plane/app/views/integration/github.py).
  • No logic changes outside this function.

Suggested changeset 1
apps/api/plane/app/views/integration/github.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/apps/api/plane/app/views/integration/github.py b/apps/api/plane/app/views/integration/github.py
--- a/apps/api/plane/app/views/integration/github.py
+++ b/apps/api/plane/app/views/integration/github.py
@@ -12,6 +12,7 @@
 from django.http import HttpResponse, HttpResponseRedirect
 from django.views import View
 from django.utils import timezone
+from django.utils.http import url_has_allowed_host_and_scheme
 
 # Third party imports
 from rest_framework import status
@@ -706,7 +707,11 @@
             )
 
             # Redirect back to frontend
-            return HttpResponseRedirect(redirect_url)
+            # Validate redirect_url to prevent open redirect vulnerabilities
+            if url_has_allowed_host_and_scheme(redirect_url, allowed_hosts=None):
+                return HttpResponseRedirect(redirect_url)
+            else:
+                return HttpResponseRedirect("/")
 
         except Exception as e:
             log_exception(e)
EOF
@@ -12,6 +12,7 @@
from django.http import HttpResponse, HttpResponseRedirect
from django.views import View
from django.utils import timezone
from django.utils.http import url_has_allowed_host_and_scheme

# Third party imports
from rest_framework import status
@@ -706,7 +707,11 @@
)

# Redirect back to frontend
return HttpResponseRedirect(redirect_url)
# Validate redirect_url to prevent open redirect vulnerabilities
if url_has_allowed_host_and_scheme(redirect_url, allowed_hosts=None):
return HttpResponseRedirect(redirect_url)
else:
return HttpResponseRedirect("/")

except Exception as e:
log_exception(e)
Copilot is powered by AI and may make mistakes. Always verify output.
This commit adds comprehensive frontend components for the GitHub integration,
enabling users to configure repository syncing and PR state automation.

## Frontend Components Added

### 1. Enhanced GitHub Service (github.service.ts)
- Added interfaces for all GitHub integration entities
- Workspace integration management endpoints
- Personal GitHub account connection endpoints
- Repository sync configuration endpoints
- PR state mapping configuration endpoints
- Full TypeScript type safety

### 2. Repository Sync Component (repository-sync.tsx)
Features:
- Visual repository sync list with inline editing
- Sync direction toggle (unidirectional/bidirectional)
- State mapping for GitHub open/closed states
- Dropdown selectors using project states
- Real-time updates with toast notifications
- Delete sync with confirmation
- Empty state with helpful messaging

### 3. PR State Mapping Component (pr-state-mapping.tsx)
Features:
- Configure 6 PR lifecycle states:
  * Draft PR created
  * PR opened
  * Review requested
  * PR approved
  * PR merged
  * PR closed
- State mapping to Plane project states
- Info panel explaining automation vs linking
- Visual grid layout for easy configuration
- Toast notifications for updates

### 4. Sync Guide Component (sync-guide.tsx)
Features:
- How-to guide for issue syncing (both directions)
- PR automation explanation with code examples
- "What Gets Synced?" reference table
- Visual examples of bracket notation [WEB-344]
- Distinction between automation and linking
- Responsive design with icons

## UI/UX Highlights

- **Consistent Design**: Uses Plane's design system (@plane/ui)
- **Toast Notifications**: Success/error feedback for all actions
- **Loading States**: Proper async handling
- **Confirmation Modals**: Safe delete operations
- **Empty States**: Helpful guidance when no config exists
- **Inline Editing**: Quick updates without separate forms
- **Responsive Layout**: Grid-based responsive design
- **Icon System**: lucide-react icons for visual cues

## Integration Patterns

All components follow Plane's established patterns:
- **MobX State Management**: observer pattern for reactivity
- **SWR Data Fetching**: Built-in for future integration
- **TypeScript**: Full type safety
- **Component Composition**: Modular and reusable
- **Error Handling**: Graceful degradation

## Usage

These components can be integrated into project settings:

```tsx
import {
  GithubRepositorySync,
  GithubPRStateMapping,
  GithubSyncGuide
} from "@/components/integration/github";

// In project settings page
<GithubRepositorySync
  workspaceSlug={workspaceSlug}
  projectId={projectId}
  syncs={repositorySyncs}
  projectStates={projectStates}
  onCreateSync={handleCreate}
  onUpdateSync={handleUpdate}
  onDeleteSync={handleDelete}
/>
```

## Next Steps

To complete the integration:
1. Add routes in app router for integration settings
2. Create workspace-level integration page
3. Wire up to backend API endpoints
4. Add state management/stores if needed
5. Add loading skeletons
6. Add comprehensive error boundaries

## Related

- Backend PR: Already merged
- API Endpoints: All implemented
- Documentation: See GITHUB_INTEGRATION.md
Implemented comprehensive modal dialog for creating GitHub repository syncs
with all required configuration options per specification.

## Features Added

### Modal Dialog
- Title: "Link GitHub Repository to a Plane Project"
- Full-screen modal with HeadlessUI Dialog and Transition
- Proper backdrop and animations
- Close button and escape key support

### Form Fields

1. **Plane Project Selector**
   - Dropdown to select target Plane project
   - Required field with validation
   - Dynamically filters states based on selected project

2. **GitHub Repository Selector**
   - Dropdown showing available GitHub repositories
   - Format: owner/repository-name
   - Required field with validation
   - Populated from connected GitHub integration

3. **Issue Sync State Configuration**
   - Two state mappings in bordered section:
     * When GitHub issue is opened -> Select Plane state
     * When GitHub issue is closed -> Select Plane state
   - Dropdowns show state color indicators
   - Required fields with validation
   - Disabled until project is selected

4. **Sync Direction Options**
   - Radio button selection with two options:

   **Bidirectional:**
   - Sync issues and comments both ways
   - Full two-way synchronization

   **Unidirectional (default):**
   - GitHub → Plane only
   - Shows warning: "Data from GitHub Issue will replace data in Linked Plane Work Item"
   - Warning displayed with AlertTriangle icon in amber color

### Validation & UX

- All required fields marked with red asterisk
- Submit button disabled until all fields are filled
- Loading state during submission
- Toast notifications for success/error
- Form resets on close or successful submission
- Helpful descriptions under each field

### State Management

- Local form state with TypeScript interface
- Project-specific state filtering
- Repository selection with full metadata
- Proper state cleanup on modal close

## Component Props Enhanced

Added new optional props:
- `availableProjects`: List of workspace projects
- `availableRepositories`: List of GitHub repos from integration

## Technical Details

- Uses HeadlessUI Dialog for accessibility
- Transition animations for smooth UX
- Proper TypeScript typing throughout
- Form validation before submission
- Conditional warning display for unidirectional mode

## Usage Example

```tsx
<GithubRepositorySync
  workspaceSlug="acme-workspace"
  projectId="current-project-id"
  syncs={existingSyncs}
  projectStates={allProjectStates}
  availableProjects={workspaceProjects}
  availableRepositories={githubRepos}
  onCreateSync={handleCreate}
  onUpdateSync={handleUpdate}
  onDeleteSync={handleDelete}
/>
```

The modal matches Plane's design system and provides clear,
user-friendly configuration for GitHub integration setup.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants