From b8ac91c425002d2885d1c36cb64a38cb54cdf9ec Mon Sep 17 00:00:00 2001 From: Holger Schmermbeck Date: Tue, 28 Oct 2025 02:30:47 +0100 Subject: [PATCH 1/2] feat: Add automatic project board assignment workflow Automatically adds issues with 'enhancement' or 'core-feature' labels to the SecPal Roadmap organization project. Features: - Direct GraphQL mutation for reliable project assignment - Fine-grained PAT authentication via PROJECT_TOKEN secret - Graceful error handling with helpful comments - Status suggestions based on issue labels Related: SecPal/.github#96, SecPal/.github#101 --- .github/workflows/project-automation.yml | 142 +++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 .github/workflows/project-automation.yml diff --git a/.github/workflows/project-automation.yml b/.github/workflows/project-automation.yml new file mode 100644 index 0000000..095afb4 --- /dev/null +++ b/.github/workflows/project-automation.yml @@ -0,0 +1,142 @@ +# 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@v7 + 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@v7 + 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=📋 Backlog" >> "$GITHUB_OUTPUT" + elif [[ "$BLOCKER" == "true" ]]; then + echo "status=🎯 Ready" >> "$GITHUB_OUTPUT" + else + echo "status=💡 Ideas" >> "$GITHUB_OUTPUT" + fi + + - name: Comment on issue + uses: actions/github-script@v7 + with: + script: | + const status = '${{ steps.status.outputs.status }}'; + 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}**` + : `📋 Suggested status for [SecPal Feature Roadmap](https://github.com/orgs/SecPal/projects/1): **${status}**`; + + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: `${message}\n\nNext steps:\n- Review and refine the issue description\n- Add relevant labels (area, priority)\n- Link related issues/PRs\n- Move to appropriate project column as needed` + }); From 84efab0cc52e4024af816c270d16909838de1c7c Mon Sep 17 00:00:00 2001 From: Holger Schmermbeck Date: Tue, 28 Oct 2025 02:30:47 +0100 Subject: [PATCH 2/2] feat: Update project automation workflow with new status flow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sync with SecPal/.github changes: - Use 💡 Ideas for regular enhancements - Use 📋 Planned for core-features - Document status flow including 🚫 Won't Do - Add status descriptions Related: SecPal/.github#104 --- .github/workflows/project-automation.yml | 26 ++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/.github/workflows/project-automation.yml b/.github/workflows/project-automation.yml index 095afb4..99109f4 100644 --- a/.github/workflows/project-automation.yml +++ b/.github/workflows/project-automation.yml @@ -28,7 +28,7 @@ jobs: - name: Add to project id: add-to-project continue-on-error: true - uses: actions/github-script@v7 + uses: actions/github-script@v8 with: # NOTE: A fine-grained Personal Access Token is required here because GITHUB_TOKEN # lacks organization-level project permissions. @@ -70,7 +70,7 @@ jobs: - name: Comment if project add failed if: steps.add-to-project.outcome == 'failure' - uses: actions/github-script@v7 + uses: actions/github-script@v8 with: script: | const body = [ @@ -117,26 +117,36 @@ jobs: BLOCKER="${{ contains(github.event.issue.labels.*.name, 'priority: blocker') }}" if [[ "$CORE_FEATURE" == "true" ]]; then - echo "status=📋 Backlog" >> "$GITHUB_OUTPUT" + echo "status=📋 Planned" >> "$GITHUB_OUTPUT" + echo "description=Core feature - scheduled for implementation" >> "$GITHUB_OUTPUT" elif [[ "$BLOCKER" == "true" ]]; then - echo "status=🎯 Ready" >> "$GITHUB_OUTPUT" + 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@v7 + 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}**` - : `📋 Suggested status for [SecPal Feature Roadmap](https://github.com/orgs/SecPal/projects/1): **${status}**`; + ? `✅ 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 + + _Note: Ideas that won't be implemented should be moved to 🚫 Won't Do with a reason._`; github.rest.issues.createComment({ issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, - body: `${message}\n\nNext steps:\n- Review and refine the issue description\n- Add relevant labels (area, priority)\n- Link related issues/PRs\n- Move to appropriate project column as needed` + body: `${message}\n${statusFlow}\n\n**Next steps:**\n- Review and refine the issue description\n- Add relevant labels (area, priority)\n- Discuss feasibility and approach\n- Move through status flow as work progresses` });