From 902d2b262394ebf58a18a1762c9922ec46f7ed20 Mon Sep 17 00:00:00 2001 From: Suitably Mysterious Date: Sat, 2 Aug 2025 07:57:50 +0000 Subject: [PATCH 1/7] add sync to plane workflow --- .github/workflows/planeSync.yml | 93 +++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 .github/workflows/planeSync.yml diff --git a/.github/workflows/planeSync.yml b/.github/workflows/planeSync.yml new file mode 100644 index 0000000..745d5c0 --- /dev/null +++ b/.github/workflows/planeSync.yml @@ -0,0 +1,93 @@ +name: Sync GitHub Issues & PRs to Plane + +on: + issues: + types: [opened, edited, closed, reopened] + pull_request: + types: [opened, edited, closed, reopened] + +jobs: + sync: + runs-on: ubuntu-latest + steps: + - name: Sync to Plane + uses: actions/github-script@v7 + with: + script: | + const fetch = require("node-fetch"); + + const planeToken = process.env.PLANE_API_TOKEN; + const workspaceId = "library-system"; + const projectId = "a4e04f53-ca9d-47c4-9a2e-48b32595aaee"; + + // Detect if it's an issue or PR + const isIssue = !!context.payload.issue; + const isPR = !!context.payload.pull_request; + + let item, action, type; + if (isIssue) { + item = context.payload.issue; + action = context.payload.action; + type = "Issue"; + } else if (isPR) { + item = context.payload.pull_request; + action = context.payload.action; + type = "PR"; + } + + // Map state → Plane status + let status = "Backlog"; + if (item.state === "closed" && !item.merged_at) { + status = "Done"; + } else if (item.merged_at) { + status = "Merged"; // Consider creating "Merged" status in Plane + } else if (action === "reopened") { + status = "In Progress"; + } + + const externalId = `${type}-${item.id}`; + + // Task data for Plane + const taskData = { + title: `${type}: ${item.title}`, + description: item.body || "", + external_id: externalId, + status: status, + priority: "Medium", + assignees: [], + }; + + const planeApiBase = `https://app.plane.so/api/workspaces/${workspaceId}/projects/${projectId}/issues`; + + // 1. Check if this task already exists + const existing = await fetch(`${planeApiBase}/?external_id=${encodeURIComponent(externalId)}`, { + headers: { "Authorization": `Bearer ${planeToken}` } + }).then(res => res.json()); + + if (existing.results && existing.results.length > 0) { + // 2. Update existing task + const taskId = existing.results[0].id; + console.log(`Updating existing ${type} in Plane: ${taskId}`); + + await fetch(`${planeApiBase}/${taskId}/`, { + method: "PATCH", + headers: { + "Content-Type": "application/json", + "Authorization": `Bearer ${planeToken}`, + }, + body: JSON.stringify(taskData), + }).then(r => r.json()).then(d => console.log("Updated:", d)); + } else { + // 3. Create new task + console.log(`Creating new ${type} in Plane`); + await fetch(`${planeApiBase}/`, { + method: "POST", + headers: { + "Content-Type": "application/json", + "Authorization": `Bearer ${planeToken}`, + }, + body: JSON.stringify(taskData), + }).then(r => r.json()).then(d => console.log("Created:", d)); + } + env: + PLANE_API_TOKEN: ${{ secrets.PLANE_API_TOKEN }} From f3f0791ed7a9052b18bed5b46eb2a38615aea786 Mon Sep 17 00:00:00 2001 From: Suitably Mysterious Date: Sat, 2 Aug 2025 07:59:43 +0000 Subject: [PATCH 2/7] some rndom glitch that meant this didnt get commited before --- .github/workflows/planeSync.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/planeSync.yml b/.github/workflows/planeSync.yml index 745d5c0..a158888 100644 --- a/.github/workflows/planeSync.yml +++ b/.github/workflows/planeSync.yml @@ -19,8 +19,6 @@ jobs: const planeToken = process.env.PLANE_API_TOKEN; const workspaceId = "library-system"; const projectId = "a4e04f53-ca9d-47c4-9a2e-48b32595aaee"; - - // Detect if it's an issue or PR const isIssue = !!context.payload.issue; const isPR = !!context.payload.pull_request; From bc6089e165d5a7adebc22311a7cccf159b3f9fbe Mon Sep 17 00:00:00 2001 From: Suitably Mysterious Date: Sat, 2 Aug 2025 09:02:39 +0100 Subject: [PATCH 3/7] Update .github/workflows/planeSync.yml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .github/workflows/planeSync.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/planeSync.yml b/.github/workflows/planeSync.yml index a158888..1050a53 100644 --- a/.github/workflows/planeSync.yml +++ b/.github/workflows/planeSync.yml @@ -14,8 +14,6 @@ jobs: uses: actions/github-script@v7 with: script: | - const fetch = require("node-fetch"); - const planeToken = process.env.PLANE_API_TOKEN; const workspaceId = "library-system"; const projectId = "a4e04f53-ca9d-47c4-9a2e-48b32595aaee"; From 840e675894438fcb8896ab32586a0731e43a97eb Mon Sep 17 00:00:00 2001 From: Suitably Mysterious Date: Sat, 2 Aug 2025 09:03:25 +0100 Subject: [PATCH 4/7] Update .github/workflows/planeSync.yml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .github/workflows/planeSync.yml | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/.github/workflows/planeSync.yml b/.github/workflows/planeSync.yml index 1050a53..b073f25 100644 --- a/.github/workflows/planeSync.yml +++ b/.github/workflows/planeSync.yml @@ -33,12 +33,20 @@ jobs: // Map state → Plane status let status = "Backlog"; - if (item.state === "closed" && !item.merged_at) { - status = "Done"; - } else if (item.merged_at) { - status = "Merged"; // Consider creating "Merged" status in Plane - } else if (action === "reopened") { - status = "In Progress"; + if (type === "Issue") { + if (item.state === "closed") { + status = "Done"; + } else if (action === "reopened") { + status = "In Progress"; + } + } else if (type === "PR") { + if (item.merged_at) { + status = "Merged"; // Consider creating "Merged" status in Plane + } else if (item.state === "closed") { + status = "Done"; + } else if (action === "reopened") { + status = "In Progress"; + } } const externalId = `${type}-${item.id}`; From 4f63f634c462d019c7ef29ddcea05523e4d51386 Mon Sep 17 00:00:00 2001 From: Suitably Mysterious Date: Sat, 2 Aug 2025 09:03:44 +0100 Subject: [PATCH 5/7] Update .github/workflows/planeSync.yml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .github/workflows/planeSync.yml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/planeSync.yml b/.github/workflows/planeSync.yml index b073f25..8ee8835 100644 --- a/.github/workflows/planeSync.yml +++ b/.github/workflows/planeSync.yml @@ -73,14 +73,21 @@ jobs: const taskId = existing.results[0].id; console.log(`Updating existing ${type} in Plane: ${taskId}`); - await fetch(`${planeApiBase}/${taskId}/`, { + const patchResponse = await fetch(`${planeApiBase}/${taskId}/`, { method: "PATCH", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${planeToken}`, }, body: JSON.stringify(taskData), - }).then(r => r.json()).then(d => console.log("Updated:", d)); + }); + if (!patchResponse.ok) { + const errorText = await patchResponse.text(); + console.error(`Failed to update ${type} in Plane: ${patchResponse.status} ${patchResponse.statusText} - ${errorText}`); + throw new Error(`Plane PATCH request failed with status ${patchResponse.status}`); + } + const updatedData = await patchResponse.json(); + console.log("Updated:", updatedData); } else { // 3. Create new task console.log(`Creating new ${type} in Plane`); From af79e71ade3f034afd9ca988fdfeed243ad6f451 Mon Sep 17 00:00:00 2001 From: Suitably Mysterious Date: Sat, 2 Aug 2025 09:03:57 +0100 Subject: [PATCH 6/7] Update .github/workflows/planeSync.yml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .github/workflows/planeSync.yml | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/.github/workflows/planeSync.yml b/.github/workflows/planeSync.yml index 8ee8835..e62414c 100644 --- a/.github/workflows/planeSync.yml +++ b/.github/workflows/planeSync.yml @@ -91,14 +91,25 @@ jobs: } else { // 3. Create new task console.log(`Creating new ${type} in Plane`); - await fetch(`${planeApiBase}/`, { - method: "POST", - headers: { - "Content-Type": "application/json", - "Authorization": `Bearer ${planeToken}`, - }, - body: JSON.stringify(taskData), - }).then(r => r.json()).then(d => console.log("Created:", d)); + try { + const response = await fetch(`${planeApiBase}/`, { + method: "POST", + headers: { + "Content-Type": "application/json", + "Authorization": `Bearer ${planeToken}`, + }, + body: JSON.stringify(taskData), + }); + if (!response.ok) { + const errorBody = await response.text(); + throw new Error(`Failed to create ${type} in Plane: ${response.status} ${response.statusText} - ${errorBody}`); + } + const data = await response.json(); + console.log("Created:", data); + } catch (error) { + console.error(`Error creating ${type} in Plane:`, error); + throw error; + } } env: PLANE_API_TOKEN: ${{ secrets.PLANE_API_TOKEN }} From 34cfa08e806133c6166e08c656f83efd622ad0c9 Mon Sep 17 00:00:00 2001 From: Suitably Mysterious Date: Sat, 2 Aug 2025 09:06:48 +0100 Subject: [PATCH 7/7] Potential fix for code scanning alert no. 2: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- .github/workflows/planeSync.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/planeSync.yml b/.github/workflows/planeSync.yml index e62414c..dea8e95 100644 --- a/.github/workflows/planeSync.yml +++ b/.github/workflows/planeSync.yml @@ -1,4 +1,5 @@ name: Sync GitHub Issues & PRs to Plane +permissions: {} on: issues: