-
Notifications
You must be signed in to change notification settings - Fork 0
feat: Add automatic project board assignment workflow #9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,152 @@ | ||||||
| # SPDX-FileCopyrightText: 2025 SecPal | ||||||
| # SPDX-License-Identifier: CC0-1.0 | ||||||
|
|
||||||
| name: Add Issues to Project | ||||||
|
|
||||||
| on: | ||||||
| issues: | ||||||
| types: | ||||||
| - opened | ||||||
| - reopened | ||||||
|
|
||||||
| env: | ||||||
| # Project ID for SecPal Roadmap (organization project) | ||||||
| # To find your project ID: gh api graphql -f query='query{organization(login:"YOUR_ORG"){projectV2(number:YOUR_PROJECT_NUMBER){id}}}' | ||||||
| # Replace "YOUR_ORG" with your organization login, and "YOUR_PROJECT_NUMBER" with your project number. | ||||||
| PROJECT_ID: PVT_kwDOCUodoc4BGgjL | ||||||
|
|
||||||
| jobs: | ||||||
| add-to-project: | ||||||
| name: Add issue to project board | ||||||
| runs-on: ubuntu-latest | ||||||
| permissions: | ||||||
| issues: write | ||||||
| contents: read | ||||||
| outputs: | ||||||
| project-add-outcome: ${{ steps.set-outcome.outputs.outcome }} | ||||||
| steps: | ||||||
| - name: Add to project | ||||||
| id: add-to-project | ||||||
| continue-on-error: true | ||||||
| uses: actions/github-script@v8 | ||||||
| with: | ||||||
| # NOTE: A fine-grained Personal Access Token is required here because GITHUB_TOKEN | ||||||
| # lacks organization-level project permissions. | ||||||
| # Required permissions for PROJECT_TOKEN (configured as org secret): | ||||||
| # - Organization: Projects (Read & Write) | ||||||
| # - Repository: Issues (Read & Write) | ||||||
| # - Repository: Metadata (Read-only, automatically included) | ||||||
| # Fallback: If this step fails, a helpful error comment is posted (see step: Comment if project add failed) | ||||||
| github-token: ${{ secrets.PROJECT_TOKEN }} | ||||||
| script: | | ||||||
| const projectId = process.env.PROJECT_ID; | ||||||
| const contentId = context.payload.issue.node_id; | ||||||
|
|
||||||
| const mutation = ` | ||||||
| mutation($projectId: ID!, $contentId: ID!) { | ||||||
| addProjectV2ItemById(input: {projectId: $projectId, contentId: $contentId}) { | ||||||
| item { | ||||||
| id | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
| `; | ||||||
|
|
||||||
| try { | ||||||
| const result = await github.graphql(mutation, { | ||||||
| projectId, | ||||||
| contentId | ||||||
| }); | ||||||
| console.log('✅ Successfully added to project:', result); | ||||||
| } catch (error) { | ||||||
| console.error('❌ Failed to add to project:', error.message, error.status, error.response, error); | ||||||
| throw error; | ||||||
| } | ||||||
|
|
||||||
| - name: Set outcome output | ||||||
| id: set-outcome | ||||||
| if: always() | ||||||
| run: echo "outcome=\"${{ steps.add-to-project.outcome }}\"" >> "$GITHUB_OUTPUT" | ||||||
|
|
||||||
| - name: Comment if project add failed | ||||||
| if: steps.add-to-project.outcome == 'failure' | ||||||
| uses: actions/github-script@v8 | ||||||
| with: | ||||||
| script: | | ||||||
| const body = [ | ||||||
| '⚠️ **Manual Action Required**', | ||||||
| '', | ||||||
| 'This issue could not be automatically added to the [SecPal Feature Roadmap](https://github.com/orgs/SecPal/projects/1) due to missing permissions.', | ||||||
| '', | ||||||
| '**To add manually:**', | ||||||
| '```bash', | ||||||
| `gh project item-add 1 --owner SecPal --url ${context.payload.issue.html_url}`, | ||||||
| '```', | ||||||
| '', | ||||||
| '**To enable automation:** An organization admin needs to:', | ||||||
| '1. Create a fine-grained Personal Access Token with `Contents: Read` and `Projects: Read & Write` permissions', | ||||||
| '2. Add it as a repository secret named `PROJECT_TOKEN`', | ||||||
| '3. Update this workflow to use `github-token: \\${{ secrets.PROJECT_TOKEN }}`', | ||||||
| '', | ||||||
| 'See: https://docs.github.com/en/issues/planning-and-tracking-with-projects/automating-your-project/using-the-api-to-manage-projects' | ||||||
| ].join('\n'); | ||||||
|
|
||||||
| github.rest.issues.createComment({ | ||||||
| issue_number: context.issue.number, | ||||||
| owner: context.repo.owner, | ||||||
| repo: context.repo.repo, | ||||||
| body: body | ||||||
| }); | ||||||
|
|
||||||
| set-status-field: | ||||||
| name: Set initial status based on labels | ||||||
| runs-on: ubuntu-latest | ||||||
| needs: add-to-project | ||||||
| permissions: | ||||||
| issues: write | ||||||
| contents: read | ||||||
| if: | | ||||||
| (success() || failure()) && | ||||||
| (contains(github.event.issue.labels.*.name, 'enhancement') || contains(github.event.issue.labels.*.name, 'core-feature')) | ||||||
| steps: | ||||||
| - name: Determine initial status | ||||||
| id: status | ||||||
| run: | | ||||||
| # Check labels to determine initial project status | ||||||
| CORE_FEATURE="${{ contains(github.event.issue.labels.*.name, 'core-feature') }}" | ||||||
| BLOCKER="${{ contains(github.event.issue.labels.*.name, 'priority: blocker') }}" | ||||||
|
|
||||||
| if [[ "$CORE_FEATURE" == "true" ]]; then | ||||||
| echo "status=📋 Planned" >> "$GITHUB_OUTPUT" | ||||||
| echo "description=Core feature - scheduled for implementation" >> "$GITHUB_OUTPUT" | ||||||
| elif [[ "$BLOCKER" == "true" ]]; then | ||||||
| echo "status=📥 Backlog" >> "$GITHUB_OUTPUT" | ||||||
| echo "description=High priority - needs immediate attention" >> "$GITHUB_OUTPUT" | ||||||
| else | ||||||
| echo "status=💡 Ideas" >> "$GITHUB_OUTPUT" | ||||||
| echo "description=New idea - needs discussion and evaluation" >> "$GITHUB_OUTPUT" | ||||||
| fi | ||||||
|
|
||||||
| - name: Comment on issue | ||||||
| uses: actions/github-script@v8 | ||||||
| with: | ||||||
| script: | | ||||||
| const status = '${{ steps.status.outputs.status }}'; | ||||||
| const description = '${{ steps.status.outputs.description }}'; | ||||||
| const projectAdded = '${{ needs.add-to-project.outputs.project-add-outcome }}' === 'success'; | ||||||
| const message = projectAdded | ||||||
| ? `✅ This issue has been automatically added to the [SecPal Feature Roadmap](https://github.com/orgs/SecPal/projects/1) with status: **${status}**\n\n_${description}_` | ||||||
| : `📋 Suggested status for [SecPal Feature Roadmap](https://github.com/orgs/SecPal/projects/1): **${status}**\n\n_${description}_`; | ||||||
|
|
||||||
| const statusFlow = ` | ||||||
| **Status Flow:** | ||||||
| 💡 Ideas → 💬 Discussion → 📥 Backlog → 📋 Planned → 🚧 In Progress → 👀 In Review → ✅ Done | ||||||
|
||||||
| 💡 Ideas → 💬 Discussion → 📥 Backlog → 📋 Planned → 🚧 In Progress → 👀 In Review → ✅ Done | |
| 💡 Ideas → 📥 Backlog → 📋 Planned → ✅ Done |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The status '📥 Backlog' contradicts the documented status flow on line 143, which shows the flow as '💡 Ideas → 💬 Discussion → 📥 Backlog → 📋 Planned → ...'. According to this flow, blocker priority issues should go to '📥 Backlog', but the description mentions this status doesn't appear in the PR description's flow ('💡 Ideas → 📋 Planned → ✅ Done'). This inconsistency could cause confusion about the actual status values being used.