Connect your GitHub repositories to bridge the gap between project planning and code development. Our GitHub integration provides seamless synchronization of code context, issues, and development progress.
The GitHub integration enables:
Before setting up the GitHub integration, ensure you have:
Navigate to your team settings and select the Integrations section:
Complete the OAuth authorization flow:
Select and configure the repositories you want to integrate:
{
"repositories": [
{
"owner": "your-org",
"name": "frontend-app",
"sync_issues": true,
"create_issues_from_tasks": true,
"track_pull_requests": true
},
{
"owner": "your-org",
"name": "api-backend",
"sync_issues": true,
"create_issues_from_tasks": false,
"track_pull_requests": true
}
]
}
Link your platform projects to GitHub repositories:
Issue Synchronization
Content Mapping
const mappingConfig = {
// Map platform task status to GitHub issue state
statusMapping: {
'pending': 'open',
'in-progress': 'open',
'review': 'open',
'completed': 'closed',
'cancelled': 'closed'
},
// Map platform priority to GitHub labels
priorityLabels: {
'low': 'priority: low',
'medium': 'priority: medium',
'high': 'priority: high',
'critical': 'priority: critical'
},
// Custom field mapping
customFields: {
'estimated_hours': 'story-points',
'task_type': 'type'
}
};
Configure which GitHub events trigger notifications:
Set up branch tracking and deployment monitoring:
# .github/workflows/integration.yml
name: Platform Integration
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
notify-platform:
runs-on: ubuntu-latest
steps:
- name: Notify Platform
uses: your-org/platform-notify-action@v1
with:
api-key: ${{ secrets.PLATFORM_API_KEY }}
project-id: ${{ secrets.PLATFORM_PROJECT_ID }}
event-type: 'deployment'
Create GitHub issue templates that align with your platform tasks:
<!-- .github/ISSUE_TEMPLATE/task.md -->
---
name: Platform Task
about: Task created from platform
labels: platform-task
---
## Task Details
**Platform Task ID**: <!-- Will be filled automatically -->
**Priority**: <!-- High/Medium/Low -->
**Estimated Hours**: <!-- Hours -->
## Description
<!-- Task description from platform -->
## Acceptance Criteria
<!-- Detailed acceptance criteria -->
## Related Links
- Platform Task: [Link will be added automatically]
- Project Brief: [Link to related brief]
When you create a task in your platform project:
Platform Task
{
"id": "task_123",
"title": "Implement user authentication API",
"description": "Build JWT-based authentication with refresh tokens",
"status": "pending",
"priority": "high",
"assignee": "john.smith",
"estimated_hours": 8
}
Generated GitHub Issue
# Implement user authentication API
Build JWT-based authentication with refresh tokens
**Platform Details:**
- Task ID: task_123
- Priority: High
- Estimated Hours: 8
- Status: Open
**Acceptance Criteria:**
- [ ] JWT token generation and validation
- [ ] Refresh token mechanism
- [ ] Password hashing and verification
- [ ] API endpoint testing
---
*This issue was automatically created from Platform Task #123*
Link commits to platform tasks using commit messages:
# Link commit to platform task
git commit -m "Add JWT authentication endpoints
- Implement token generation
- Add password hashing
- Create refresh token logic
Refs: task_123"
# Close task with commit
git commit -m "Complete user authentication API
All endpoints tested and documented
Closes: task_123"
Pull requests automatically update related tasks:
<!-- Pull Request Description -->
## Changes
- Implemented JWT authentication
- Added password hashing
- Created refresh token mechanism
## Testing
- [x] Unit tests passing
- [x] Integration tests passing
- [x] Manual testing completed
**Related Platform Task**: #task_123
**Closes**: task_123
Track development progress through integrated analytics:
Commit Activity
Issue Management
Pull Request Analytics
Create custom dashboards combining platform and GitHub data:
const dashboardConfig = {
widgets: [
{
type: 'task-completion-rate',
timeframe: '30d',
repositories: ['frontend-app', 'api-backend']
},
{
type: 'commit-activity',
groupBy: 'author',
repositories: ['frontend-app']
},
{
type: 'pull-request-metrics',
metrics: ['merge-time', 'review-time'],
timeframe: '7d'
}
]
};
Set up custom webhooks for advanced integration scenarios:
Automatically create GitHub issues when specific platform events occur:
// Webhook handler for platform events
app.post('/webhooks/platform', async (req, res) => {
const { type, data } = req.body;
if (type === 'task.created' && data.task.create_github_issue) {
const issue = await createGitHubIssue({
repository: data.project.github_repository,
title: data.task.title,
body: formatTaskDescription(data.task),
labels: mapPriorityToLabels(data.task.priority),
assignee: data.task.assignee?.github_username
});
// Update task with GitHub issue link
await updateTask(data.task.id, {
github_issue_url: issue.html_url,
github_issue_number: issue.number
});
}
res.status(200).send('OK');
});
Sync GitHub events back to your platform:
// GitHub webhook handler
app.post('/webhooks/github', async (req, res) => {
const { action, issue, repository } = req.body;
if (action === 'closed' && issue.labels.includes('platform-task')) {
const taskId = extractTaskId(issue.body);
if (taskId) {
await updateTask(taskId, {
status: 'completed',
completion_notes: `Completed via GitHub issue #${issue.number}`,
github_closed_at: issue.closed_at
});
}
}
res.status(200).send('OK');
});
Automate platform updates using GitHub Actions:
# .github/workflows/platform-sync.yml
name: Platform Sync
on:
issues:
types: [opened, closed, reopened]
pull_request:
types: [opened, closed, merged]
jobs:
sync-to-platform:
runs-on: ubuntu-latest
steps:
- name: Sync Issue Status
if: github.event_name == 'issues'
uses: your-org/platform-sync-action@v1
with:
platform-api-key: ${{ secrets.PLATFORM_API_KEY }}
event-type: 'issue'
issue-number: ${{ github.event.issue.number }}
action: ${{ github.event.action }}
- name: Update Task Progress
if: github.event_name == 'pull_request'
uses: your-org/platform-sync-action@v1
with:
platform-api-key: ${{ secrets.PLATFORM_API_KEY }}
event-type: 'pull_request'
pr-number: ${{ github.event.pull_request.number }}
action: ${{ github.event.action }}
For advanced integrations, create a custom GitHub App:
{
"name": "Your Platform Integration",
"description": "Connect GitHub repositories with your platform",
"homepage_url": "https://your-platform.com",
"webhook_url": "https://your-platform.com/webhooks/github",
"permissions": {
"issues": "write",
"pull_requests": "read",
"repository_contents": "read",
"metadata": "read"
},
"events": [
"issues",
"pull_request",
"push",
"release"
]
}
const { App } = require('@octokit/app');
const app = new App({
appId: process.env.GITHUB_APP_ID,
privateKey: process.env.GITHUB_PRIVATE_KEY,
webhooks: {
secret: process.env.GITHUB_WEBHOOK_SECRET
}
});
// Handle installation events
app.webhooks.on('installation.created', async ({ octokit, payload }) => {
console.log(`App installed on account: ${payload.installation.account.login}`);
// Set up integration for new installation
await setupIntegrationForAccount(payload.installation);
});
Symptoms:
Solutions:
// Check OAuth token validity
async function validateGitHubToken(token) {
try {
const response = await fetch('https://api.github.com/user', {
headers: { 'Authorization': `token ${token}` }
});
if (response.status === 401) {
throw new Error('Token expired or invalid');
}
const user = await response.json();
console.log('Token valid for user:', user.login);
return true;
} catch (error) {
console.error('Token validation failed:', error.message);
return false;
}
}
// Handle token refresh
async function refreshGitHubToken(refreshToken) {
// Implementation depends on your OAuth setup
const response = await fetch('https://github.com/login/oauth/access_token', {
method: 'POST',
headers: { 'Accept': 'application/json' },
body: new URLSearchParams({
client_id: process.env.GITHUB_CLIENT_ID,
client_secret: process.env.GITHUB_CLIENT_SECRET,
refresh_token: refreshToken,
grant_type: 'refresh_token'
})
});
return response.json();
}
Issue Duplication:
// Prevent duplicate issue creation
async function createIssueIfNotExists(repository, taskId, issueData) {
const existingIssues = await octokit.issues.listForRepo({
owner: repository.owner,
repo: repository.name,
labels: `platform-task-${taskId}`
});
if (existingIssues.data.length > 0) {
console.log(`Issue already exists for task ${taskId}`);
return existingIssues.data[0];
}
return await octokit.issues.create({
owner: repository.owner,
repo: repository.name,
...issueData,
labels: [...(issueData.labels || []), `platform-task-${taskId}`]
});
}
Status Sync Conflicts:
// Handle conflicting status updates
async function resolveSyncConflict(taskId, platformStatus, githubStatus) {
const conflict = {
task_id: taskId,
platform_status: platformStatus,
github_status: githubStatus,
timestamp: new Date(),
resolution: 'manual_review_required'
};
// Log conflict for manual resolution
await logSyncConflict(conflict);
// Notify relevant team members
await notifyTeamOfConflict(conflict);
return conflict;
}
// Test GitHub API connectivity
async function testGitHubConnection(config) {
const tests = [
{
name: 'Authentication',
test: () => octokit.users.getAuthenticated()
},
{
name: 'Repository Access',
test: () => octokit.repos.get({
owner: config.repository.owner,
repo: config.repository.name
})
},
{
name: 'Issue Creation',
test: () => octokit.issues.create({
owner: config.repository.owner,
repo: config.repository.name,
title: 'Integration Test Issue',
body: 'This is a test issue created by the platform integration.'
})
}
];
const results = [];
for (const test of tests) {
try {
await test.test();
results.push({ name: test.name, status: 'passed' });
} catch (error) {
results.push({
name: test.name,
status: 'failed',
error: error.message
});
}
}
return results;
}
// Monitor synchronization health
async function getSyncHealthStatus(repositoryId) {
const stats = await db.query(`
SELECT
COUNT(*) as total_tasks,
COUNT(github_issue_id) as synced_tasks,
COUNT(CASE WHEN sync_status = 'failed' THEN 1 END) as failed_syncs,
MAX(last_sync_at) as last_successful_sync
FROM tasks
WHERE repository_id = ?
`, [repositoryId]);
const health = {
sync_percentage: (stats.synced_tasks / stats.total_tasks) * 100,
failed_syncs: stats.failed_syncs,
last_sync: stats.last_successful_sync,
status: stats.failed_syncs === 0 ? 'healthy' : 'needs_attention'
};
return health;
}
// Secure token storage and rotation
const tokenManager = {
async storeToken(userId, token, expiresAt) {
// Encrypt token before storage
const encryptedToken = encrypt(token, process.env.ENCRYPTION_KEY);
await db.tokens.upsert({
user_id: userId,
service: 'github',
encrypted_token: encryptedToken,
expires_at: expiresAt,
created_at: new Date()
});
},
async getToken(userId) {
const record = await db.tokens.findOne({
user_id: userId,
service: 'github'
});
if (!record || record.expires_at < new Date()) {
throw new Error('Token expired or not found');
}
return decrypt(record.encrypted_token, process.env.ENCRYPTION_KEY);
}
};
Now that you have GitHub integration set up:
Need help? Join our Slack community.