iT邦幫忙

2025 iThome 鐵人賽

DAY 11
0

在團隊合作中,及時進行 Code Review 是確保程式碼品質與專案進度的關鍵。然而,隨著專案日漸龐大,待審核的 Pull Request (PR) 很容易被忽略

今天,我們將一步步教你如何建立一個自動化流程,讓系統在每天固定的時間檢查 GitHub 倉庫,找出待審核的 PR,並將彙整好的提醒訊息自動發送到指定的 Discord 頻道

workflow

步驟一:設定排程觸發

步驟二:從 GitHub 取得 Pull Request 資料

  • 接著下個節點選擇「GitHub」

    image 6.png

  • 選擇「Get pull request of a repository」

    image 7.png

  • 接著建立並設定憑證

    image 8.png

  • 到 GitHub 設定的 Token 頁面,新增一個 Token

    https://github.com/settings/tokens
    

    image 9.png

  • 權限打開

    image 10.png

  • 把 Token 複製起來

    image 11.png

  • User 寫上自己 GitHub 的名字

    image 12.png

  • 填上想追蹤的 REPO 網址

    image 13.png

步驟三:過濾掉最新的 PR

有時候我們不需要提醒剛發出的 PR,而是希望專注在那些已經等待一段時間的。這時就可以用「Filter」節點來篩選

  • 下個節點選擇「Filter」來過濾資料

    image 14.png

  • 判斷資料為最後活動時間

    {{ $json.updated_at }}
    
  • 選擇「Date & Time」的「is before」,後面內容寫

    {{ $now.minus({ days: 1 }) }}
    

    image 15.png

步驟四:彙整並格式化提醒訊息

  • 下個節點選擇「Code」來彙整 PR 資料

    image 16.png

  • 撰寫底下的程式碼

    const items = $input.all();
    
    // 如果沒有任何 PR,就直接結束,不往下執行
    if (items.length === 0) {
      return [];
    }
    
    // 訊息的開頭
    let message = `**📢 Code Review 每日提醒!**\n\n共有 ${items.length} 則 PR 等待審核中:\n`;
    
    // 透過 for 迴圈,將每一筆 PR 的資訊都加到 message 字串中
    for (const item of items) {
      const pr = item.json;
      const reviewers =
        pr.requested_reviewers.length > 0
          ? pr.requested_reviewers.map((r) => r.login).join(", ")
          : "⚠️ **尚未指定**";
    
      message += `\n------------------------------------\n`;
      message += `**標題:** [${pr.title}](${pr.html_url})\n`;
      message += `**作者:** ${pr.user.login}\n`;
      message += `**待審核:** ${reviewers}\n`;
    }
    
    // 最後,回傳一個包含單一訊息的物件
    return [
      {
        json: {
          digestMessage: message,
        },
      },
    ];
    

步驟五:發送訊息到 Discord

  • 下個節點選擇 Discord 來傳送訊息

    image 17.png

    image 18.png

  • 「Connection Type」選擇「Webhook」,憑證的串接在之前的文章有撰寫過,這邊就不重複惹

    image 19.png

  • Message 填入底下變數

    {
      {
        $json.digestMessage;
      }
    }
    
  • 接著點選上方的「Execute step」來測試一下

    image 20.png

  • Discord 看到訊息代表成功啦

    image 21.png

  • 完成的流程會長這樣,記得到上方切換為「Active」哦

    image 22.png

  • 最後也附上完整流程的 JSON

    {
      "name": "Code Review",
      "nodes": [
        {
          "parameters": {
            "rule": {
              "interval": [
                {
                  "field": "cronExpression",
                  "expression": "0 10 * * 1-5"
                }
              ]
            }
          },
          "type": "n8n-nodes-base.scheduleTrigger",
          "typeVersion": 1.2,
          "position": [0, 0],
          "id": "68dd4969-837d-4514-b697-c6ee5022f74f",
          "name": "Schedule Trigger"
        },
        {
          "parameters": {
            "resource": "repository",
            "operation": "getPullRequests",
            "owner": {
              "__rl": true,
              "value": "https://github.com/[YOUR_GITHUB_OWNER]",
              "mode": "url"
            },
            "repository": {
              "__rl": true,
              "value": "https://github.com/[YOUR_GITHUB_OWNER]/[YOUR_GITHUB_REPO]",
              "mode": "url"
            },
            "getRepositoryPullRequestsFilters": {}
          },
          "type": "n8n-nodes-base.github",
          "typeVersion": 1.1,
          "position": [220, 0],
          "id": "31b1cc64-fd64-463a-a248-175257fb34b6",
          "name": "GitHub",
          "webhookId": "[REDACTED_WEBHOOK_ID]",
          "credentials": {
            "githubApi": {
              "id": "[REDACTED_CREDENTIAL_ID]",
              "name": "[REDACTED_CREDENTIAL_NAME]"
            }
          }
        },
        {
          "parameters": {
            "conditions": {
              "options": {
                "caseSensitive": true,
                "leftValue": "",
                "typeValidation": "strict",
                "version": 2
              },
              "conditions": [
                {
                  "id": "22038ef4-d866-4d86-b94f-edda9186d59c",
                  "leftValue": "={{ $json.updated_at }}",
                  "rightValue": "={{ $now.minus({ days: 1 }) }}",
                  "operator": {
                    "type": "dateTime",
                    "operation": "before"
                  }
                }
              ],
              "combinator": "and"
            },
            "options": {}
          },
          "type": "n8n-nodes-base.filter",
          "typeVersion": 2.2,
          "position": [440, 0],
          "id": "8a8d50bc-7863-4ca6-81c-09a6f65c5151",
          "name": "Filter"
        },
        {
          "parameters": {
            "jsCode": "const items = $input.all();\n\n// 如果沒有任何 PR,就直接結束,不往下執行\nif (items.length === 0) {\n  return [];\n}\n\n// 訊息的開頭\nlet message = `**📢 Code Review 每日提醒!**\\n\\n共有 ${items.length} 則 PR 等待審核中:\\n`;\n\n// 透過 for 迴圈,將每一筆 PR 的資訊都加到 message 字串中\nfor (const item of items) {\n  const pr = item.json;\n  const reviewers = pr.requested_reviewers.length > 0 \n    ? pr.requested_reviewers.map(r => r.login).join(', ') \n    : '⚠️ **尚未指定**';\n\n  message += `\\n------------------------------------\\n`;\n  message += `**標題:** [${pr.title}](${pr.html_url})\\n`;\n  message += `**作者:** ${pr.user.login}\\n`;\n  message += `**待審核:** ${reviewers}\\n`;\n}\n\n// 最後,回傳一個包含單一訊息的物件\nreturn [{\n  json: {\n    digestMessage: message\n  }\n}];"
          },
          "type": "n8n-nodes-base.code",
          "typeVersion": 2,
          "position": [660, 0],
          "id": "6013e0ab-ec37-4418-a642-ebc5d480f39d",
          "name": "Code"
        },
        {
          "parameters": {
            "authentication": "webhook",
            "content": "={{ $json.digestMessage }}",
            "options": {}
          },
          "type": "n8n-nodes-base.discord",
          "typeVersion": 2,
          "position": [880, 0],
          "id": "78e7dfa0-a736-4e85-98a5-87903249df62",
          "name": "Discord",
          "webhookId": "[REDACTED_WEBHOOK_ID]",
          "credentials": {
            "discordWebhookApi": {
              "id": "[REDACTED_CREDENTIAL_ID]",
              "name": "[REDACTED_CREDENTIAL_NAME]"
            }
          }
        }
      ],
      "pinData": {},
      "connections": {
        "Schedule Trigger": {
          "main": [
            [
              {
                "node": "GitHub",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "GitHub": {
          "main": [
            [
              {
                "node": "Filter",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Filter": {
          "main": [
            [
              {
                "node": "Code",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Code": {
          "main": [
            [
              {
                "node": "Discord",
                "type": "main",
                "index": 0
              }
            ]
          ]
        }
      },
      "active": true,
      "settings": {
        "executionOrder": "v1",
        "timezone": "Asia/Taipei",
        "callerPolicy": "workflowsFromSameOwner"
      },
      "versionId": "36ad75db-9169-4aaf-89b2-33068e43f8b0",
      "meta": {
        "templateCredsSetupCompleted": true,
        "instanceId": "[REDACTED_INSTANCE_ID]"
      },
      "id": "po8AkJzMqVhNfUgv",
      "tags": []
    }
    

上一篇
[Day10]_當季片單資料庫
下一篇
[Day12]_網站健康檢查員
系列文
告別重複瑣事: n8n workflow 自動化工作實踐18
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言