You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现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 repo and admin:org scopes (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, and Done as 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.

  1. In each repo, create a file at .github/workflows/sync-repo-to-org.yml
  2. 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}`);
  1. Add your PAT as a secret named ORG_SYNC_PAT in 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.

  1. Create a dedicated repository in your org for shared workflows (e.g., org-workflows).
  2. 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}`);
  1. Add your PAT as a secret named REPO_SYNC_PAT in 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.createCard calls.
  • New GitHub Projects: If using the modern GitHub Projects (not classic), adjust the API calls to use projectsV2 endpoints and update custom status fields instead of moving cards (you’ll need to fetch field/option IDs via the API first).

内容的提问来源于stack exchange,提问作者DottoreM

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 10:57:31