• Contenu
  • Bas de page
logo ouidoulogo ouidoulogo ouidoulogo ouidou
  • Qui sommes-nous ?
  • Offres
    • 💻 Applications métier
    • 🤝 Collaboration des équipes
    • 🛡️ Sécurisation et optimisation du système d’information
    • 🔗 Transformation numérique
  • Expertises
    • 🖥️ Développement logiciel
    • ♾️ DevSecOps
    • ⚙️ Intégration de logiciels et négoce de licences
      • Atlassian : Jira, Confluence, Bitbucket…
      • Plateforme monday.com
      • GitLab
      • SonarQube
    • 📚​ Logiciel de CRM et de gestion
    • 🎨 UX/UI design
    • 🌐 Accessibilité Numérique
    • 🗂️​ Démarches simplifiées
    • 📝 Formations Atlassian
  • Références
  • Carrières
    • 🧐 Pourquoi rejoindre Ouidou ?
    • ✍🏻 Nous rejoindre
    • 👨‍💻 Rencontrer nos collaborateurs
    • 🚀 Grandir chez Ouidou
  • RSE
  • Ressources
    • 🗞️ Actualités
    • 🔍 Articles techniques
    • 📖 Livres blancs
    • 🎙️ Interviews Clients
Nous contacter
✕
Ouidou accélère sa croissance en ouvrant une nouvelle agence à Lyon !
Ouidou accélère sa croissance en ouvrant une nouvelle agence à Lyon !
10 mai 2021
Signer nos commits
Signer nos commits
28 mai 2021
Ressources > Articles techniques > GitLab userscript to ease label transitions

GitLab userscript to ease label transitions

Article écrit par Martin Catty

GitLab

For the past years we, at Synbioz, have been heavy users of GitLab. It progressively became our central tool for day to day job.

This choice has mostly been driven by its devops capabilities.

Otherwise, I have still found its UX quite painful when it comes to change a label on a given issue.

What’s wrong with label change on GitLab ?

We have a common workflow based on boards, moving an issue to the right or to the left. Original, isn’t it ?

We also have a few automations to help us create boards, milestones etc… automatically ; even define permissions on the whole GitLab, but that’s another story.

For now, here is what our boards headers look like :

board part 1 board part 2

Sure we can easily drag’n’drop the issue from a column to an other.

When we are doing some testing we can’t satisfied with the issue’s title only.

Issue details

So let’s consider this issue is OK. I will have to remove the current label to validate::functionally and put another one : to validate::technically.

To do so I have to go through no less than 6 (!) steps :

  • click edit
  • type a few characters
  • scroll to the wanted label or use the arrows
  • click or hit enter to select it
  • do not forget to «exit» the dropdown to apply the label (you can’t use esc key)
  • remove the old label (this one is easy, just click on the cross)

This is far from ideal from my point of view. When you process a lot of issues this is a massive waste of time and error-prone.

Label proposal

Considering there is always a human decision behind label changes, we can’t automatically apply a label based on another one removal or addition.

That’s why I decided to make a tiny userscript offering a new section right into the sidebar, called Labels proposal.

Labels proposal

Based on the current label and some predefined «states machine», the interface offers to apply the next label.

Clicking on a proposed label will set it on the issue and remove the other ones (only those related to the states we defined).

Removing the labels (by simulating a click on the cross) has the advantage of refreshing this part of the toolbar.

Show me the code

This userscript needs you to install an extension offering the ability to run userscripts beforehand.

I have been a long time user of Greasemonkey that now seems to be superseeded by Tampermonkey.

So this is just JS code that will run inside your browser. Tampermonkey lets you decide when you want to run the script. Could be once the document starts to load, ends to load, or everything has been loaded and executed (especially other scripts). There is also a few other choices detailed in the documentation.

I chose the last one because I needed to ensure labels have been loaded before trying to manipulate them.

// ==UserScript== // @name         GitLab label proposal // @namespace    https://www.synbioz.com/ // @version      0.2.0 // @description  Label proposal for GitLab // @author       Martin Catty // @include      /^https://git.synbioz.com/.*/issues/d // @icon         https://www.google.com/s2/favicons?domain=gitlab.com // @run-at       document-idle // @grant        none // ==/UserScript==  (function () {   Array.prototype.compact = function () {     return this.filter((x) => x !== undefined && x !== null);   };    const states = [     ["to do", "doing"],     ["doing", "to validate::functionally"],     ["to validate::functionally", ["to do", "to validate::technically"]],     ["to validate::technically", "to deploy::staging"],     ["to deploy::staging", "to deploy::production"]   ];    const initialStates = states.map((node) => node[0]);   const transitions = new Map(states);   const label = document.querySelector(".labels");    // Create a map from existing labels, using this form :   // { "label" => [node1, node2] }   const labelsToNodes = Array.from(     document.querySelectorAll(".labels .gl-label-text")   ).reduce((map, node) => {     const sibling = node.nextElementSibling;     let scope = "";      if (sibling !== undefined && sibling !== null) {       scope = "::" + sibling.textContent.trim();     }      const content = node.textContent.trim() + scope;      // we don't want to keep track of label's node not part     // of the states we are watching     if (initialStates.includes(content)) {       map.set(content, [node, sibling].compact());     }      return map;   }, new Map());    const nextLabels = Array.from(labelsToNodes.keys())     .map((label) => transitions.get(label))     .compact()     .flat();    const header = `       <div class="title hide-collapsed gl-mb-3">         Labels proposal       </div>     `;    const html = nextLabels.reduce((acc, label) => {     acc += `           <div class="hide-collapsed value issuable-show-labels js-value has-labels">             <span data-qa-selector="selected_label_content" data-qa-label-name="to do" class="gl-label gl-label-text-light"               style="--label-background-color:#D10069; --label-inset-border:inset 0 0 0 2px #D10069;">               <a href="#" class="gl-link gl-label-link">                 <span class="gl-label-text label-proposal">                   ${label}                 </span>               </a>             </span>           </div>         `;      return acc;   }, header);    const block = document.createElement("div");    block.className = "block";   label.after(block);   block.innerHTML = html;    const csrf = document     .querySelector('meta[name="csrf-token"]')     .getAttribute("content");    document.querySelectorAll(".label-proposal").forEach((node) => {     node.addEventListener("click", (event) => {       event.preventDefault();        const label = event.target.textContent.trim();       const url = `/api/v4/projects/${document.body.dataset.projectId}/issues/${         document.body.dataset.pageTypeId       }?add_labels=${encodeURIComponent(label)}`;        const headers = new Headers({         "Content-Type": "application/json",         "x-csrf-token": csrf,         Accept: "application/json"       });        fetch(url, {         method: "PUT",         headers: headers,         withCredentials: true       }).then((response) => {         if (response.ok) {           node.remove();            // remove existing labels           labelsToNodes.forEach((nodes, key) => {             // there can be 2 nodes (including the scope), we only need one,             // they have the same parent             const node = nodes[0];             const cross = node.parentNode.nextElementSibling;              if (cross) {               const event = new Event("click");                cross.dispatchEvent(event);             }           });         }       });     });   }); })(); 

You may not be able to use this script as is, as it’s meant for our own workflow, but it should be easy to update and enhance.

I hope this will help some, do not hesitate to leave a message on Twitter.

À lire aussi

Fresque numérique miniature image
16 avril 2025

Fresque du Numérique

Lire la suite

intelligence artificielle Ouicommit miniature image
17 mars 2025

Ouicommit – L’intelligence artificielle en entreprise, on y est ! 

Lire la suite

Image miniature Hackathon Women in Tech
13 mars 2025

Hackathon Women in Tech :  un engagement pour une tech plus inclusive 

Lire la suite

image miniature les nouveautés Atlassian
26 février 2025

Les nouveautés Atlassian en 2025

Lire la suite

Articles associés

Fresque numérique miniature image
16 avril 2025

Fresque du Numérique


Lire la suite
intelligence artificielle Ouicommit miniature image
17 mars 2025

Ouicommit – L’intelligence artificielle en entreprise, on y est ! 


Lire la suite
Image miniature Hackathon Women in Tech
13 mars 2025

Hackathon Women in Tech :  un engagement pour une tech plus inclusive 


Lire la suite

À propos

  • Qui sommes-nous ?
  • Références
  • RSE
  • Ressources

Offres

  • Applications métier
  • Collaboration des équipes
  • Sécurisation et optimisation du système d’information
  • Transformation numérique

Expertises

  • Développement logiciel
  • DevSecOps
  • Intégration de logiciels et négoce de licences
  • Logiciel de CRM et de gestion
  • UX/UI design
  • Accessibilité Numérique
  • Démarches simplifiées
  • Formations Atlassian

Carrières

  • Pourquoi rejoindre Ouidou ?
  • Nous rejoindre
  • Rencontrer nos collaborateurs
  • Grandir chez Ouidou

SIEGE SOCIAL
70-74 boulevard Garibaldi, 75015 Paris

Ouidou Nord
165 Avenue de Bretagne, 59000 Lille

Ouidou Rhône-Alpes
4 place Amédée Bonnet, 69002 Lyon

Ouidou Grand-Ouest
2 rue Crucy, 44000 Nantes

Ouidou Grand-Est
7 cour des Cigarières, 67000 Strasbourg

  • Linkedin Ouidou
  • GitHub Ouidou
  • Youtube Ouidou
© 2024 Ouidou | Tous droits réservés | Plan du site | Mentions légales | Déclaration d'accessibilité
    Nous contacter