iT邦幫忙

0

從 Email 到 Slack:實作 GitLab Merge Request 個人化通知流程

  • 分享至 

  • xImage
  •  

目的:將自己發的 GitLab Merge Request,在發生 Merge、Draft、Conflict 時,自動發送客製化 Slack 通知給自己


背景

過去我試過一個簡單的方法:把 GitLab 發送的通知 Email 直接轉發到 Slack
雖然能用,但缺點是過濾不精準、訊息格式也很死板

某天突發奇想:既然通知都是透過 Email 送來的,那我能不能用 Gmail + Google Apps Script + Slack Workflow,做一個「屬於自己的 MR 通知系統」?
最後實作出來的效果大概長這樣:
https://ithelp.ithome.com.tw/upload/images/20250914/20140925wHfftXzh31.png
https://ithelp.ithome.com.tw/upload/images/20250914/20140925mM5LCdDGtR.png
https://ithelp.ithome.com.tw/upload/images/20250914/20140925l5FIpN8kMf.png


實作流程

整個流程可以分成三個部分:

  1. Gmail → 設定篩選器,幫不同 MR 狀態加標籤
  2. Slack → 建立一個 Workflow,接受 Webhook
  3. App Script → 定時撈 Gmail,把訊息送進 Slack

1. Gmail 設定信件篩選器

首先,我需要讓 Gmail 自動幫 MR 通知信分類,這樣後續腳本才知道哪些要處理推送

  1. 到 Gmail 設定 → 篩選器與封鎖的地址
  2. 建立篩選條件(例如主旨或寄件人),然後套用「加標籤」
    • 標籤名稱建議用:mr-mergedmr-draftmr-conflict
    • 也可以直接匯入篩選器設定檔(參考下方),但要記得把 你的名字 改成自己的 GitLab 帳號名稱
      https://ithelp.ithome.com.tw/upload/images/20250914/20140925ERaIV97soS.png
<?xml version='1.0' encoding='UTF-8'?>
<feed xmlns='http://www.w3.org/2005/Atom' xmlns:apps='http://schemas.google.com/apps/2006'>
	<entry>
		<category term='filter'></category>
		<title>Mail Filter</title>
		<id></id>
		<updated>2025-09-14T00:00:00Z</updated>
		<content></content>
		<apps:property name='hasTheWord' value='&quot;marked merge request&quot;, &quot;as draft&quot;'/>
		<apps:property name='label' value='mr-draft'/>
		<apps:property name='sizeOperator' value='s_sl'/>
		<apps:property name='sizeUnit' value='s_smb'/>
	</entry>
	<entry>
		<category term='filter'></category>
		<title>Mail Filter</title>
		<id></id>
		<updated>2025-09-14T00:00:00Z</updated>
		<content></content>
		<apps:property name='hasTheWord' value='&quot;Merge request&quot;, &quot;was merged&quot;, &quot;Author: 你的名字&quot;'/>
		<apps:property name='label' value='mr-merged'/>
		<apps:property name='sizeOperator' value='s_sl'/>
		<apps:property name='sizeUnit' value='s_smb'/>
	</entry>
	<entry>
		<category term='filter'></category>
		<title>Mail Filter</title>
		<id></id>
		<updated>2025-09-14T00:00:00Z</updated>
		<content></content>
		<apps:property name='hasTheWord' value='&quot;Merge request&quot;, &quot;can no longer be merged due to conflict.&quot;, &quot;Author: 你的名字&quot;'/>
		<apps:property name='label' value='mr-conflict'/>
		<apps:property name='sizeOperator' value='s_sl'/>
		<apps:property name='sizeUnit' value='s_smb'/>
	</entry>
</feed>

2. Slack Workflow 設定

接著要在 Slack 建立一個 Workflow,接收 Webhook。

  1. 新增一個私人用的 Slack channel(例如 #my-mr-notify)
  2. 到坐下角 Create NewWorkflows
  3. 新增Trigger(Start the workflow…) From a Webhook,複製它的 Web Request URL
  4. 在 Workflow 裡加一個步驟:Send a message
    • 訊息格式可以自訂,像是 {{EventName}}: {{Title}} ({{GitlabLink}})
    • 可以順便設定機器人名稱、頭像
      https://ithelp.ithome.com.tw/upload/images/20260124/20140925nwFScr8qj7.png

最後按 Publish 儲存,並把 Webhook URL 留下來,下一步會用到。


3. App Script 自動化

最後一步就是寫一個 Google Apps Script,每分鐘檢查 Gmail,如果有符合標籤的 MR 通知,就轉發到 Slack。

建立 App Script 專案

  1. Google Apps Script 建立新專案
  2. 把下面的程式碼貼進去
// 個人設定:填入剛剛從 Slack Workflow 複製的 Webhook URL
var slackWebhook = "https://hooks.slack.com/triggers/......";

// 信件主旨的正則表達式
// 範例主旨:Re: 專案名稱 | MR 標題 (!(123))
var subjectRegex = /Re: ([^|]+) \| (.+) \(\!(\d+)\)/;

// GitLab 連結的正則表達式
var gitlabLinkRegex = /View it on GitLab: (https:\/\/[^\s]+)/;

// 發送訊息到 Slack
function sendMessageToSlack(eventName, title, gitlabLink, projectName) {
  var payload = {
    "EventName": eventName,
    "Title": title,
    "GitlabLink": gitlabLink,
    "ProjectName": projectName
  };
  
  var res = UrlFetchApp.fetch(slackWebhook, {
    method      : 'post',
    contentType : 'application/json',
    payload     : JSON.stringify(payload)
  });
  
  if (res.getResponseCode() != 200) {
    Logger.log("發送 Slack 訊息失敗,內容為:" + JSON.stringify(payload));
  }
}

// 讀取 Gmail 標籤,轉發 MR 通知
function forwardEmailsToSlack(eventName, labelName) {
  var label = GmailApp.getUserLabelByName(labelName);
  var threads = label.getThreads(0, 20); // 每次最多抓 20 封
  
  if (!threads || threads.length == 0) {
    return;
  }
  
  for (var i = 0; i < threads.length; i++) {
    var subject = threads[i].getFirstMessageSubject();
    var subjectMatchs = subject.match(subjectRegex);
    if (!subjectMatchs || subjectMatchs.length != 4) {
      Logger.log("主旨匹配非預期");
      break;
    }
    
    var projectName = subjectMatchs[1];
    var title = subjectMatchs[2];
    var mrId = subjectMatchs[3];
    
    // 取出最後一封信件,解析 GitLab 連結
    var messages = threads[i].getMessages();
    var mailBodyContent = messages[messages.length - 1].getPlainBody();
    var bodyMatchs = mailBodyContent.match(gitlabLinkRegex);
    if (!bodyMatchs || bodyMatchs.length != 2) {
      Logger.log("信件內容匹配非預期");
      break;
    }
    
    var gitlabLink = bodyMatchs[1];
    
    // 發送到 Slack
    sendMessageToSlack(eventName, title, gitlabLink, projectName);
    
    // 後續處理:標記已讀
    threads[i].markRead();
    // threads[i].moveToArchive(); // 如果想自動封存可以打開
    // threads[i].moveToTrash();   // 如果想直接刪掉可以打開
  }
  
  // 移除標籤,避免重複處理
  label.removeFromThreads(threads);
}

// 主程式
function Main() {
  forwardEmailsToSlack(":merged: MR 已合併", "mr-merged");
  forwardEmailsToSlack(":memo: MR 轉草稿", "mr-draft");
  forwardEmailsToSlack(":warning: MR 有衝突", "mr-conflict");
  Logger.log("執行完成");
}

新增觸發器

  1. 左側選單 → Triggers (觸發條件)
  2. 新增一個觸發器,設定:
  • 執行函式:Main
  • 事件來源:時間驅動(time-driven)
  • 頻率:每分鐘或每 5 分鐘一次
  1. 儲存並允許 Apps Script 存取 Gmail

成品效果

到這裡應該就完成了
之後,系統會定時檢查 Gmail:

  • 如果有符合條件的 MR 通知信,就會自動標記已讀(可改成封存或刪除)
  • 然後把訊息發送到 Slack 的個人 channel

結語

這個方法雖然不是最「正統」的 GitLab–Slack 整合,但好處是:

  • 不需要額外安裝 GitLab 插件
  • 不需要額外權限
  • 可以完全客製化訊息內容
    簡單幾個步驟,就能打造一個專屬於自己的 MR 通知小幫手

圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言