mirror of
https://github.com/seanmorley15/AdventureLog.git
synced 2026-03-24 17:22:10 -04:00
168 lines
5.7 KiB
YAML
168 lines
5.7 KiB
YAML
name: Sync Project Status
|
|
|
|
on:
|
|
issues:
|
|
types: [labeled]
|
|
|
|
jobs:
|
|
update-project:
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: read
|
|
issues: write
|
|
repository-projects: write
|
|
|
|
steps:
|
|
- name: Update project status from label
|
|
uses: actions/github-script@v7
|
|
with:
|
|
github-token: ${{ secrets.PROJECT_AUTOMATION_TOKEN != '' && secrets.PROJECT_AUTOMATION_TOKEN || github.token }}
|
|
script: |
|
|
|
|
const labelMap = {
|
|
"backlog": "Backlog",
|
|
"needs discussion": "Needs discussion",
|
|
"approved": "Approved",
|
|
"ready": "Ready",
|
|
"in progress": "In progress",
|
|
"in review": "In review",
|
|
"done": "Done"
|
|
};
|
|
|
|
const label = context.payload.label.name.toLowerCase();
|
|
const targetOptionName = labelMap[label];
|
|
|
|
if (!targetOptionName) return;
|
|
|
|
const issueNodeId = context.payload.issue.node_id;
|
|
|
|
const configuredProjectId = "PVT_kwHOBeIeKs4AfmUO";
|
|
const fieldId = "PVTSSF_lAHOBeIeKs4AfmUOzgU5pCI";
|
|
|
|
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
|
|
async function fetchIssueProjectItems() {
|
|
const result = await github.graphql(`
|
|
query($issueId: ID!) {
|
|
node(id: $issueId) {
|
|
... on Issue {
|
|
projectItems(first: 100) {
|
|
nodes {
|
|
id
|
|
project {
|
|
id
|
|
title
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`, {
|
|
issueId: issueNodeId
|
|
});
|
|
|
|
return result.node?.projectItems?.nodes ?? [];
|
|
}
|
|
|
|
// Retry a few times in case project membership is still syncing.
|
|
let issueProjectItems = [];
|
|
for (let attempt = 1; attempt <= 4; attempt++) {
|
|
issueProjectItems = await fetchIssueProjectItems();
|
|
if (issueProjectItems.length > 0) break;
|
|
if (attempt < 4) await sleep(3000);
|
|
}
|
|
|
|
let exactMatch = issueProjectItems.find(
|
|
(node) => node.project?.id === configuredProjectId
|
|
);
|
|
|
|
let itemId = exactMatch?.id;
|
|
let projectId = exactMatch?.project?.id;
|
|
|
|
// If issue is not in the configured project yet, try to add it.
|
|
if (!itemId) {
|
|
try {
|
|
const added = await github.graphql(`
|
|
mutation($projectId: ID!, $contentId: ID!) {
|
|
addProjectV2ItemById(input: { projectId: $projectId, contentId: $contentId }) {
|
|
item {
|
|
id
|
|
}
|
|
}
|
|
}
|
|
`, {
|
|
projectId: configuredProjectId,
|
|
contentId: issueNodeId
|
|
});
|
|
|
|
itemId = added.addProjectV2ItemById?.item?.id;
|
|
projectId = configuredProjectId;
|
|
} catch (error) {
|
|
const availableProjects = issueProjectItems
|
|
.map((node) => `${node.project?.title ?? "(untitled)"} [${node.project?.id ?? "no-id"}]`)
|
|
.join(", ");
|
|
console.log(`Issue not in configured project ${configuredProjectId}. Available: ${availableProjects || "none"}`);
|
|
console.log(`Failed to auto-add issue to configured project: ${error.message}`);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!itemId || !projectId) {
|
|
const availableProjects = issueProjectItems
|
|
.map((node) => `${node.project?.title ?? "(untitled)"} [${node.project?.id ?? "no-id"}]`)
|
|
.join(", ");
|
|
console.log(`Issue not in configured project ${configuredProjectId}. Available: ${availableProjects || "none"}`);
|
|
return;
|
|
}
|
|
|
|
const fieldResult = await github.graphql(`
|
|
query($fieldId: ID!) {
|
|
node(id: $fieldId) {
|
|
... on ProjectV2SingleSelectField {
|
|
id
|
|
options {
|
|
id
|
|
name
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`, {
|
|
fieldId: fieldId
|
|
});
|
|
|
|
const options = fieldResult.node?.options ?? [];
|
|
const selectedOption = options.find(
|
|
(option) => option.name?.toLowerCase() === targetOptionName.toLowerCase()
|
|
);
|
|
|
|
if (!selectedOption?.id) {
|
|
const availableOptions = options.map((option) => option.name).join(", ");
|
|
console.log(`Could not find option '${targetOptionName}' for field ${fieldId}. Available options: ${availableOptions || "none"}`);
|
|
return;
|
|
}
|
|
|
|
// update status field
|
|
await github.graphql(`
|
|
mutation($projectId: ID!, $itemId: ID!, $fieldId: ID!, $optionId: String!) {
|
|
updateProjectV2ItemFieldValue(
|
|
input: {
|
|
projectId: $projectId
|
|
itemId: $itemId
|
|
fieldId: $fieldId
|
|
value: { singleSelectOptionId: $optionId }
|
|
}
|
|
) {
|
|
projectV2Item {
|
|
id
|
|
}
|
|
}
|
|
}
|
|
`, {
|
|
projectId: projectId,
|
|
itemId: itemId,
|
|
fieldId: fieldId,
|
|
optionId: selectedOption.id
|
|
});
|