Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 152 additions & 0 deletions .github/workflows/project-automation.yml
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"
Copy link

Copilot AI Oct 28, 2025

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.

Copilot uses AI. Check for mistakes.
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
Copy link

Copilot AI Oct 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The status flow documented here includes '💬 Discussion', '📥 Backlog', '🚧 In Progress', and '👀 In Review', but the PR description only mentions '💡 Ideas → 📋 Planned → ✅ Done' with '🚫 Won't Do'. This discrepancy between the documented flow and the described flow may confuse users about which statuses are actually available or expected in the project board.

Suggested change
💡 Ideas → 💬 Discussion → 📥 Backlog → 📋 Planned → 🚧 In Progress → 👀 In Review → ✅ Done
💡 Ideas → 📥 Backlog → 📋 Planned → ✅ Done

Copilot uses AI. Check for mistakes.

_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${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`
});