如何实现GitHub仓库与组织项目看板的1:1同步及聚合视图?
Solution to Sync Repository Kanbans with Organization-Level Project Board in GitHub
Great question! GitHub doesn’t have built-in bidirectional sync for swimlane (status) changes between repo kanban boards and organization-level projects, but you can achieve this 1:1 sync using GitHub Actions and the GitHub API. Here’s a step-by-step solution tailored to your setup:
Prerequisites
First, make sure you have these in place:
- A GitHub Personal Access Token (PAT) with
repoandadmin:orgscopes (needed to modify both repo projects and org projects). - Consistent status values across all boards: Ensure every repo kanban and your org project uses exactly
Backlog,In Progress, andDoneas swimlane names/status options.
Step 1: Sync Repo Board Changes to Org Project
Create a workflow in each repository that triggers when an issue is moved between swimlanes, then updates the corresponding item in your org project.
- In each repo, create a file at
.github/workflows/sync-repo-to-org.yml - Paste this workflow code (replace placeholders with your org project details):
name: Sync Repo Board → Org Project on: project_card: types: [moved] jobs: sync-status: runs-on: ubuntu-latest steps: - name: Update Org Project Status uses: actions/github-script@v6 with: github-token: ${{ secrets.ORG_SYNC_PAT }} script: | # Skip if the card isn't an issue const card = context.payload.project_card; const issueUrl = card.content_url; if (!issueUrl?.includes('/issues/')) { console.log('Skipping non-issue card'); return; } # Get current swimlane name from repo board const repoColumn = await github.rest.projects.getColumn({ column_id: card.column_id }); const currentStatus = repoColumn.data.name; # Find the corresponding card in your org project const orgProjectId = 1234567; # Replace with your org project ID const orgCards = await github.rest.projects.listCards({ project_id: orgProjectId, content_type: 'Issue' }); const orgCard = orgCards.data.find(card => card.content_url === issueUrl); if (!orgCard) { console.log('Issue not found in org project'); return; } # Find the target swimlane in the org project const orgColumns = await github.rest.projects.listColumns({ project_id: orgProjectId }); const targetColumn = orgColumns.data.find(col => col.name === currentStatus); if (!targetColumn) { console.log(`Status ${currentStatus} missing in org project`); return; } # Move the org project card to the matching swimlane await github.rest.projects.moveCard({ card_id: orgCard.id, position: 'bottom', column_id: targetColumn.id }); console.log(`Synced issue to org project swimlane: ${currentStatus}`);
- Add your PAT as a secret named
ORG_SYNC_PATin each repo (under Settings → Secrets and variables → Actions).
Step 2: Sync Org Project Changes to Repo Boards
Create an org-level workflow to sync status updates from the org project back to the corresponding repo's kanban board.
- Create a dedicated repository in your org for shared workflows (e.g.,
org-workflows). - Add a file at
.github/workflows/sync-org-to-repo.yml:
name: Sync Org Project → Repo Boards on: project_card: types: [moved] jobs: sync-status: runs-on: ubuntu-latest steps: - name: Update Repo Board Swimlane uses: actions/github-script@v6 with: github-token: ${{ secrets.REPO_SYNC_PAT }} script: | # Skip non-issue cards const card = context.payload.project_card; const issueUrl = card.content_url; if (!issueUrl?.includes('/issues/')) { console.log('Skipping non-issue card'); return; } # Parse issue details from URL const [, , , , issueOwner, issueRepo, , issueNumber] = issueUrl.split('/'); # Get current status from org project const orgColumn = await github.rest.projects.getColumn({ column_id: card.column_id }); const currentStatus = orgColumn.data.name; # Find the repo's kanban board (adjust the name if needed) const repoProjects = await github.rest.repos.listProjects({ owner: issueOwner, repo: issueRepo }); const repoBoard = repoProjects.data.find(p => p.name === 'Kanban Board'); if (!repoBoard) { console.log(`No kanban board found in repo ${issueRepo}`); return; } # Find target swimlane in repo board const repoColumns = await github.rest.projects.listColumns({ project_id: repoBoard.id }); const targetColumn = repoColumns.data.find(col => col.name === currentStatus); if (!targetColumn) { console.log(`Status ${currentStatus} missing in repo ${issueRepo}`); return; } # Find the issue's card in the repo board const repoCards = await github.rest.projects.listCards({ project_id: repoBoard.id, content_type: 'Issue' }); const repoCard = repoCards.data.find(c => c.content_url === issueUrl); if (!repoCard) { console.log(`Issue #${issueNumber} not found in repo ${issueRepo} board`); return; } # Move the repo card to the matching swimlane await github.rest.projects.moveCard({ card_id: repoCard.id, position: 'bottom', column_id: targetColumn.id }); console.log(`Synced issue #${issueNumber} to repo ${issueRepo} swimlane: ${currentStatus}`);
- Add your PAT as a secret named
REPO_SYNC_PATin the org workflow repo.
Tips for Smooth Sync
- Test first: Roll out the workflow to one repo initially to verify it works before applying to all.
- Handle missing cards: If you want to auto-create missing cards in either board, extend the workflow with
github.rest.projects.createCardcalls. - New GitHub Projects: If using the modern GitHub Projects (not classic), adjust the API calls to use
projectsV2endpoints and update custom status fields instead of moving cards (you’ll need to fetch field/option IDs via the API first).
内容的提问来源于stack exchange,提问作者DottoreM
相关产品推荐
相关产品推荐

