<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-us"><generator uri="https://gohugo.io/" version="0.156.0">Hugo</generator><title type="html">Gitlab on Marcin Jasion - Pragmatic DevOps</title><link href="https://6f95e4af.mjasion.pages.dev/tags/gitlab/" rel="alternate" type="text/html" title="html"/><link href="https://6f95e4af.mjasion.pages.dev/tags/gitlab/index.xml" rel="alternate" type="application/rss+xml" title="rss"/><updated>2020-04-17T10:00:00+00:00</updated><id>https://6f95e4af.mjasion.pages.dev/tags/gitlab/</id><entry><title type="html">How to label GitLab notification in Gmail by headers?</title><link href="https://6f95e4af.mjasion.pages.dev/posts/development/label-gitlab-notifications/?utm_source=atom_feed" rel="alternate" type="text/html"/><id>https://6f95e4af.mjasion.pages.dev/posts/development/label-gitlab-notifications/</id><author><name>Marcin Jasion</name></author><published>2020-04-17T10:00:00+00:00</published><updated>2020-04-17T10:00:00+00:00</updated><content type="html"><![CDATA[<blockquote>You can label emails by headers in Gmail. To do this you have to create a script that periodically scans for new emails in your inbox. To demonstrate it I will use Gitlab notifications and we will add labels to messages basing on their headers</blockquote><p><em>Updated on 24-02-2026: Improved the script to use <code>getHeader()</code> instead of raw body search, added data-driven header configuration, and auto-creation of labels.</em></p>
<h2 id="-how-gitlab-sends-notifications">📨 How GitLab sends notifications?</h2>
<p>GitLab allows you to stay informed about what’s happening in your projects sending you the notifications via email. With enabled notifications, you can receive updates about activity in issues, merge requests or build results. All of those emails are sent from a single address which without a doubt makes it harder to do successful filtering and labeling.</p>
<p>However GitLab adds custom headers to every sent notification to allow you to better manage received notification and for example, you could add a label to all emails with pipelines results to mark them as important. Similarly, you could make the same scenario for notification about the issue assigned to you. Some of the headers that you can find in emails are:</p>
<center>
<table>
  <thead>
      <tr>
          <th style="text-align: left">Header name</th>
          <th style="text-align: left">Reason of message</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td style="text-align: left"><code>X-GitLab-Project</code></td>
          <td style="text-align: left">Notification from project</td>
      </tr>
      <tr>
          <td style="text-align: left"><code>X-GitLab-Issue-ID</code></td>
          <td style="text-align: left">Notification about a change in <strong>issue</strong>.</td>
      </tr>
      <tr>
          <td style="text-align: left"><code>X-GitLab-MergeRequest-ID</code></td>
          <td style="text-align: left">Notification about a change in <strong>merge request</strong>.</td>
      </tr>
      <tr>
          <td style="text-align: left"><code>X-GitLab-Pipeline-Id</code></td>
          <td style="text-align: left">Notification about the <strong>result of pipeline</strong>.</td>
      </tr>
  </tbody>
</table>
</center>
<p>As can be seen above headers allow you to create example condition: if the email contains the header <code>X-GitLab-Issue-ID</code> then add a label “GitLab Issue”.</p>
<p>Of course, there are more headers available. The full list of headers, which GitLab can include to emails is available in the section “<a href="https://docs.gitlab.com/ee/user/profile/notifications.html#filtering-email" target="_blank" rel="noopener">Filtering email</a>” of GitLab documentation. Every header also contains a value. Some headers contain an ID, some contain names of projects. You can check out them in the documentation.</p>
<h2 id="-how-to-filter-emails-in-gmail-by-header">📥 How to filter emails in Gmail by header?</h2>
<p>To automatically add labels in Gmail you have to create a filter. However, it does not allow to filter by headers. But this is not impossible.</p>
<p>Google provides a special service called Google Apps Scripts. It allows you to write short scripts in JavaScript, where you can extend default Gmail filtering.</p>
<h2 id="-how-can-i-add-a-label-to-message-by-headers">⌨️ How can I add a label to message by headers?</h2>
<p>Firstly you have to begin with function, which will be scheduled to query for new emails in the inbox and will execute further message processing:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">processInbox</span>() {
</span></span><span style="display:flex;"><span>   <span style="color:#75715e">// process all recent threads in the Inbox
</span></span></span><span style="display:flex;"><span>   <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">threads</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">GmailApp</span>.<span style="color:#a6e22e">search</span>(<span style="color:#e6db74">&#34;in:inbox from:gitlab@* newer_than:1h&#34;</span>);
</span></span><span style="display:flex;"><span>   <span style="color:#66d9ef">for</span> (<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span> <span style="color:#f92672">&lt;</span> <span style="color:#a6e22e">threads</span>.<span style="color:#a6e22e">length</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span>) {
</span></span><span style="display:flex;"><span>      <span style="color:#75715e">// get all messages in a given thread
</span></span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">messages</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">threads</span>[<span style="color:#a6e22e">i</span>].<span style="color:#a6e22e">getMessages</span>();
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">for</span> (<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">j</span> <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">j</span> <span style="color:#f92672">&lt;</span> <span style="color:#a6e22e">messages</span>.<span style="color:#a6e22e">length</span>; <span style="color:#a6e22e">j</span><span style="color:#f92672">++</span>) {
</span></span><span style="display:flex;"><span>         <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">message</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">messages</span>[<span style="color:#a6e22e">j</span>];
</span></span><span style="display:flex;"><span>         <span style="color:#a6e22e">processMessage</span>(<span style="color:#a6e22e">message</span>); <span style="color:#75715e">// function to process the message
</span></span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>   }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>As you see, the code is pretty simple. It uses <code>search()</code> function from <a href="https://developers.google.com/apps-script/reference/gmail" target="_blank" rel="noopener">GmailApp</a> which allows you to interact with Gmail service. The search query <code>&quot;in:inbox from:gitlab@* newer_than:1h&quot;</code> filters only GitLab emails from the last hour. After that we have to get the message content. We can do it by writing a loop to get every message from a thread. The <code>getMessages()</code> function returns a list of <a href="https://developers.google.com/apps-script/reference/gmail/gmail-message" target="_blank" rel="noopener">Gmail Messages</a> objects. Having them we can implement our actions based on the content.</p>
<p>To do that you can use the <code>getHeader()</code> function on the message object to read a specific email header by name. For example, to check the pipeline status:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">status</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">message</span>.<span style="color:#a6e22e">getHeader</span>(<span style="color:#e6db74">&#34;X-GitLab-Pipeline-Status&#34;</span>);
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">status</span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">// header exists - this is a pipeline notification
</span></span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>This is more reliable than searching the raw email body for header names, which could produce false positives if the header name appears in the message text.</p>
<p>To make the script easy to extend, we can define a configuration map that describes which headers to look for and what labels to apply. The full script looks like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#75715e">// Label to apply on any GitLab match
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">mainLabel</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;GitLab&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Configuration: header → label mapping
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// nestValue: true  → creates sub-label with the header value, e.g. Pipeline/failed
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// matchValue: &#34;x&#34;  → only applies the label when the header value equals &#34;x&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">headersMap</span> <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#e6db74">&#34;X-GitLab-Pipeline-Status&#34;</span><span style="color:#f92672">:</span>      { <span style="color:#e6db74">&#34;label_name&#34;</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;Pipeline&#34;</span>,   <span style="color:#e6db74">&#34;nestValue&#34;</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">true</span> },
</span></span><span style="display:flex;"><span>  <span style="color:#e6db74">&#34;X-GitLab-Project&#34;</span><span style="color:#f92672">:</span>              { <span style="color:#e6db74">&#34;label_name&#34;</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;Project&#34;</span>,    <span style="color:#e6db74">&#34;nestValue&#34;</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">true</span> },
</span></span><span style="display:flex;"><span>  <span style="color:#e6db74">&#34;X-GitLab-NotificationReason&#34;</span><span style="color:#f92672">:</span>   { <span style="color:#e6db74">&#34;label_name&#34;</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;Reason&#34;</span>,     <span style="color:#e6db74">&#34;nestValue&#34;</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">true</span> },
</span></span><span style="display:flex;"><span>  <span style="color:#e6db74">&#34;X-GitLab-Issue-ID&#34;</span><span style="color:#f92672">:</span>             { <span style="color:#e6db74">&#34;label_name&#34;</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;Issue&#34;</span> },
</span></span><span style="display:flex;"><span>  <span style="color:#e6db74">&#34;X-GitLab-MergeRequest-ID&#34;</span><span style="color:#f92672">:</span>      { <span style="color:#e6db74">&#34;label_name&#34;</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;MergeRequest&#34;</span> },
</span></span><span style="display:flex;"><span>  <span style="color:#e6db74">&#34;X-GitLab-Discussion-ID&#34;</span><span style="color:#f92672">:</span>        { <span style="color:#e6db74">&#34;label_name&#34;</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;Discussion&#34;</span> },
</span></span><span style="display:flex;"><span>  <span style="color:#e6db74">&#34;X-GitLab-MergeRequest-State&#34;</span><span style="color:#f92672">:</span>   { <span style="color:#e6db74">&#34;label_name&#34;</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;Merged&#34;</span>,     <span style="color:#e6db74">&#34;matchValue&#34;</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;merged&#34;</span> },
</span></span><span style="display:flex;"><span>};
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">processInbox</span>() {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">threads</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">GmailApp</span>.<span style="color:#a6e22e">search</span>(<span style="color:#e6db74">&#34;in:inbox from:gitlab@* newer_than:1h&#34;</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">for</span> (<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span> <span style="color:#f92672">&lt;</span> <span style="color:#a6e22e">threads</span>.<span style="color:#a6e22e">length</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">messages</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">threads</span>[<span style="color:#a6e22e">i</span>].<span style="color:#a6e22e">getMessages</span>();
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> (<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">j</span> <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">j</span> <span style="color:#f92672">&lt;</span> <span style="color:#a6e22e">messages</span>.<span style="color:#a6e22e">length</span>; <span style="color:#a6e22e">j</span><span style="color:#f92672">++</span>) {
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">processMessage</span>(<span style="color:#a6e22e">messages</span>[<span style="color:#a6e22e">j</span>]);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">processMessage</span>(<span style="color:#a6e22e">message</span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">firstMatch</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">for</span> (<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">header</span> <span style="color:#66d9ef">in</span> <span style="color:#a6e22e">headersMap</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">val</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">message</span>.<span style="color:#a6e22e">getHeader</span>(<span style="color:#a6e22e">header</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">val</span>) {
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">firstMatch</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">firstMatch</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">false</span>;
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">addOrCreateLabel</span>(<span style="color:#a6e22e">mainLabel</span>, <span style="color:#a6e22e">message</span>);
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">labelText</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">headersMap</span>[<span style="color:#a6e22e">header</span>][<span style="color:#e6db74">&#34;label_name&#34;</span>];
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">headersMap</span>[<span style="color:#a6e22e">header</span>][<span style="color:#e6db74">&#34;matchValue&#34;</span>]) {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">val</span> <span style="color:#f92672">!=</span> <span style="color:#a6e22e">headersMap</span>[<span style="color:#a6e22e">header</span>][<span style="color:#e6db74">&#34;matchValue&#34;</span>]) <span style="color:#66d9ef">continue</span>;
</span></span><span style="display:flex;"><span>      } <span style="color:#66d9ef">else</span> <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">headersMap</span>[<span style="color:#a6e22e">header</span>][<span style="color:#e6db74">&#34;nestValue&#34;</span>]) {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">labelText</span> <span style="color:#f92672">+=</span> <span style="color:#e6db74">&#34;/&#34;</span> <span style="color:#f92672">+</span> <span style="color:#a6e22e">val</span>;
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">addOrCreateLabel</span>(<span style="color:#a6e22e">labelText</span>, <span style="color:#a6e22e">message</span>);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">addOrCreateLabel</span>(<span style="color:#a6e22e">labelText</span>, <span style="color:#a6e22e">message</span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">label</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">GmailApp</span>.<span style="color:#a6e22e">getUserLabelByName</span>(<span style="color:#a6e22e">labelText</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">if</span> (<span style="color:#f92672">!</span><span style="color:#a6e22e">label</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">label</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">GmailApp</span>.<span style="color:#a6e22e">createLabel</span>(<span style="color:#a6e22e">labelText</span>);
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">message</span>.<span style="color:#a6e22e">getThread</span>().<span style="color:#a6e22e">addLabel</span>(<span style="color:#a6e22e">label</span>);
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The <code>headersMap</code> at the top is the only thing you need to edit to add new labels - no code changes required. Labels are created automatically if they don&rsquo;t exist yet.</p>
<h2 id="-how-to-turn-on-the-script-processing-for-you-gmail-inbox">▶️ How to turn on the script processing for you Gmail inbox?</h2>
<ol>
<li>
<p>Go to <a href="https://script.google.com/home" target="_blank" rel="noopener">Google Apps Scripts</a>.</p>
</li>
<li>
<p>Create a new project, put your code and save.</p>
</li>
<li>
<p>From the Web IDE you can perform the execution to check for errors. Select function <code>processInbox</code> and click Play button:
<img src="/posts/development/label-gitlab-notifications/google_script_run.png" alt="GAS Run"></p>
</li>
<li>
<p>You will be asked to permit a project access to your Gmail data. Choose your account:
<img src="/posts/development/label-gitlab-notifications/google_script_authorization.png" alt="GAS Authorization"></p>
</li>
<li>
<p>After successful authorization, you can re-run the project. It will be immediately executed.</p>
</li>
<li>
<p>When there is no errors, create a custom trigger. Find button: <img src="/posts/development/label-gitlab-notifications/google_script_trigger_button.png" alt="Trigger"></p>
</li>
<li>
<p>Click “Add trigger” button at the bottom of the page.</p>
</li>
<li>
<p>Select function <code>processInbox</code> and configure the time source. The execution frequency is your choice. If you receive a lot of messages and you will run this script every 1 minute you can hit the limits. In the above script, I am scanning for emails from the last hour so the script can be executed at least once an hour.
<img src="/posts/development/label-gitlab-notifications/google_script_new_trigger.png" alt="GAS Trigger"></p>
</li>
</ol>
<h2 id="and-thats-it">🏁And that’s it!</h2>
<p>Google should now start executing your script and checking for new emails to make actions which you just implemented. The result of running this script is to label new emails from GitLab as you want 🤗.
<img src="/posts/development/label-gitlab-notifications/gitlab_mails_labeled.png" alt="Labeled emails"></p>
<h2 id="-summarize">📖 Summarize</h2>
<p>Gmail filters are sufficient for most users&rsquo; needs. However, if your use case is more advanced, Google Apps Scripts comes to the rescue. It doesn&rsquo;t require deep programming knowledge and by searching online you can solve your problems. Remember that you can have multiple scripts to process your inbox.</p>
<p>Did you know about Google App Scripts before? Please share how are you using them in the comments below.</p>
]]></content><category scheme="https://6f95e4af.mjasion.pages.dev/tags/gitlab" term="gitlab" label="gitlab"/><category scheme="https://6f95e4af.mjasion.pages.dev/tags/gmail" term="gmail" label="gmail"/><category scheme="https://6f95e4af.mjasion.pages.dev/tags/google-apps-script" term="google-apps-script" label="google-apps-script"/><category scheme="https://6f95e4af.mjasion.pages.dev/tags/automation" term="automation" label="automation"/></entry></feed>