Skip to content

Add Label on Project Status Change #628

Add Label on Project Status Change

Add Label on Project Status Change #628

name: Add Label on Project Status Change
on:
workflow_dispatch:
inputs:
project_ids:
description: 'Project IDs (comma-separated, e.g., PVT_xxx,PVT_yyy)'
required: true
default: 'PVT_kwDOAjmOms4BMOXr'
status_value:
description: 'Status value to match'
required: false
default: 'Need To Verify'
label_to_add:
description: 'Label to add'
required: false
default: 'kind/need-to-verify'
schedule:
- cron: '0 * * * *' # Every hour
jobs:
add-label:
runs-on: ubuntu-latest
steps:
- name: Check project items with specific status and add label
uses: actions/github-script@v7
env:
PROJECT_IDS: ${{ github.event.inputs.project_ids || 'PVT_kwDOAjmOms4BMOXr' }}
STATUS_VALUE: ${{ github.event.inputs.status_value || 'Need To Verify' }}
LABEL_TO_ADD: ${{ github.event.inputs.label_to_add || 'kind/need-to-verify' }}
with:
script: |
const projectIds = process.env.PROJECT_IDS.split(',').map(p => p.trim());
const statusValue = process.env.STATUS_VALUE;
const labelToAdd = process.env.LABEL_TO_ADD;
console.log(`Projects: ${projectIds.join(', ')}`);
console.log(`Status: ${statusValue}, Label: ${labelToAdd}`);
for (const projectId of projectIds) {
console.log(`\n=== Processing project: ${projectId} ===`);
// Get the Status field ID
const fieldQuery = `
query($id: ID!) {
node(id: $id) {
... on ProjectV2 {
fields(first: 20) {
nodes {
... on ProjectV2Field {
id
name
}
... on ProjectV2SingleSelectField {
id
name
options {
id
name
}
}
}
}
}
}
}
`;
const { data: project } = await github.graphql(fieldQuery, { id: projectId });
if (!project || !project.fields) {
console.log(`Could not fetch project ${projectId}, skipping`);
continue;
}
const statusField = project.fields.nodes.find(f => f.name === "Status");
if (!statusField) {
console.log("Status field not found in project");
continue;
}
// Find the option ID for the target status
const statusOption = statusField.options?.find(o => o.name === statusValue);
if (!statusOption) {
console.log(`Status option "${statusValue}" not found`);
continue;
}
console.log(`Status field ID: ${statusField.id}`);
// Query items in the project
const itemsQuery = `
query($id: ID!) {
node(id: $id) {
... on ProjectV2 {
items(first: 50) {
nodes {
id
content {
... on Issue {
number
title
state
repository {
name
owner { login }
}
labels {
nodes {
name
}
}
}
}
fieldValues(first: 20) {
nodes {
... on ProjectV2ItemFieldSingleSelectValue {
field { id }
name
}
}
}
}
}
}
}
}
`;
const { data: projectWithItems } = await github.graphql(itemsQuery, { id: projectId });
// Filter items with matching status and without the label
const items = projectWithItems.items.nodes;
console.log(`Found ${items.length} items in project`);
let labelAdded = 0;
for (const item of items) {
const statusFieldValue = item.fieldValues.nodes.find(
f => f.field?.id === statusField.id
);
if (statusFieldValue?.name === statusValue && item.content && item.content.state === 'OPEN') {
const issue = item.content;
const hasLabel = issue.labels.nodes.some(l => l.name === labelToAdd);
if (!hasLabel) {
console.log(`Adding label to issue #${issue.number}: ${issue.title}`);
try {
await github.rest.issues.addLabels({
owner: issue.repository.owner.login,
repo: issue.repository.name,
issue_number: issue.number,
labels: [labelToAdd]
});
labelAdded++;
} catch (e) {
console.error(`Failed to add label to #${issue.number}: ${e.message}`);
}
} else {
console.log(`Issue #${issue.number} already has label, skip`);
}
}
}
console.log(`Project ${projectId}: Added label to ${labelAdded} issues`);
}
console.log('\n=== Done ===');