Skip to content

Instantly share code, notes, and snippets.

@MoOx
Last active Jun 21, 2022
Embed
What would you like to do?
Export/import github labels
// go on you labels pages
// eg https://github.com/cssnext/cssnext/labels
// paste this script in your console
// copy the output and now you can import it using https://github.com/popomore/github-labels !
var labels = [];
[].slice.call(document.querySelectorAll(".label-link"))
.forEach(function(element) {
labels.push({
name: element.textContent.trim(),
// using style.backgroundColor might returns "rgb(...)"
color: element.getAttribute("style")
.replace("background-color:", "")
.replace(/color:.*/,"")
.trim()
// github wants hex code only without # or ;
.replace(/^#/, "")
.replace(/;$/, "")
.trim(),
})
})
console.log(JSON.stringify(labels, null, 2))
@apotek
Copy link

apotek commented Feb 5, 2016

Useful! Thank you.

@vincentaudebert
Copy link

vincentaudebert commented Apr 15, 2016

Awesome! Saved me a lot of time :)

@Isaddo
Copy link

Isaddo commented May 6, 2016

Another approach to import github labels via console command

https://gist.github.com/Isaddo/7efebcb673a0957b9c6f07cd14826ea4

@wo0dyn
Copy link

wo0dyn commented Oct 13, 2016

@MoOx: Thank you so much mate! ❤️

@jpike88
Copy link

jpike88 commented Jun 21, 2017

This script doesn't work on Safari

@oguennec
Copy link

oguennec commented Jul 4, 2017

Great, thank you !

@ntwb
Copy link

ntwb commented Jul 12, 2017

I just used the following which was quick and easy https://github.com/jvandemo/copy-github-labels-cli

(I was linked to this after the fact)

@martindafonte
Copy link

martindafonte commented Jun 22, 2018

Hi, you can add one line and also get the description:

var labels = [];
[].slice.call(document.querySelectorAll(".label-link"))
.forEach(function(element) {
  labels.push({
    name: element.textContent.trim(),
    description: element.getAttribute("aria-label"),
    // using style.backgroundColor might returns "rgb(...)"
    color: element.getAttribute("style")
      .replace("background-color:", "")
      .replace(/color:.*/,"")
      .trim()
      // github wants hex code only without # or ;
      .replace(/^#/, "")
      .replace(/;$/, "")
      .trim(),
  })
})
console.log(JSON.stringify(labels, null, 2))

@micalevisk
Copy link

micalevisk commented Jul 27, 2018

and this function to download labels as file directly

function saveJSON(data, filename) {
  const blob = new Blob([JSON.stringify(data, undefined, 4)], { type: 'text/json' });
  const e = document.createEvent('MouseEvents');
  const a = document.createElement('a');

  a.download = filename;
  a.href = window.URL.createObjectURL(blob);
  a.dataset.downloadurl = ['text/json', a.download, a.href].join(':');
  e.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
  a.dispatchEvent(e);
}

example

saveJSON(labels, 'labels.json')

@TreONeill
Copy link

TreONeill commented Oct 10, 2018

Thanks for this! Very helpful

@vict0rsch
Copy link

vict0rsch commented Apr 11, 2019

Adding support for description (aria-label does not work for me):

description: element.parentElement.nextElementSibling.textContent.trim()

Support for settings.yml:

function saveYML(text, filename) {
  const blob = new Blob([text], { type: 'text/plain' });
  const e = document.createEvent('MouseEvents');
  const a = document.createElement('a');

  a.download = filename;
  a.href = window.URL.createObjectURL(blob);
  a.dataset.downloadurl = ['text/json', a.download, a.href].join(':');
  e.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
  a.dispatchEvent(e);
}
let out = "labels:\n";
for (const l of labels){
    out += `  - name: ${l.name}\n    color: ${l.color}\n    description: ${l.description || '""'}\n`;
}
console.log(out) // or saveYML(out, "labels.yml")

@m-lukas
Copy link

m-lukas commented Oct 18, 2019

The HTML of the /labels site changed and the querySelector in the code of the gist doesn't find any elements.
To fix it, edit the script and replace:

[].slice.call(document.querySelectorAll(".label-link"))

with:

[].slice.call(document.querySelectorAll(".js-label-link")) (e.g.: .js- label-link)

@jamesperrin
Copy link

jamesperrin commented Oct 29, 2019

Here my code example that includes changes to CSS classname and captures the label's description.

var labels = [];
[].slice.call(document.querySelectorAll(".js-label-link"))
.forEach(function(element) {
  labels.push({
    name: element.textContent.trim(),
    description: element.getAttribute("title"),
    // using style.backgroundColor might returns "rgb(...)"
    color: element.getAttribute("style")
      .replace("background-color:", "")
      .replace(/color:.*/,"")
      .trim()
      // github wants hex code only without # or ;
      .replace(/^#/, "")
      .replace(/;$/, "")
      .trim(),
  })
})

console.log(JSON.stringify(labels, null, 2))

@hannakim91
Copy link

hannakim91 commented Oct 16, 2020

Thanks for the original script and thanks @jamesperrin for the update!!

@dademaru
Copy link

dademaru commented Jan 28, 2021

Thanks, very helpful!

Unfortunately seems that GitHub (recently?) changed the color output in style:

<a href="bug" title="Something isn't working" data-name="bug" style="--label-r:215;--label-g:58;--label-b:74;--label-h:353;--label-s:66;--label-l:53;" class="IssueLabel hx_IssueLabel IssueLabel--big lh-condensed js-label-link d-inline-block v-align-top">
<span>bug</span>
</a>

so in JSON you get:

{
    "name": "bug",
    "description": "Something isn't working",
    "color": "--label-r:215;--label-g:58;--label-b:74;--label-h:353;--label-s:66;--label-l:53"
  }

and import fails throwing:
An invalid form control with name='label[color]' is not focusable.
with an error on color input required format.

@NibrasAbuAyyash
Copy link

NibrasAbuAyyash commented Feb 11, 2021

Hi all,
I updated the code, to solve the color output problem (RGBA to Hex). (@dademaru)
I also changed the attribute from which the description will be pulled.

var labels = [];
function hex(x) {
    return ("0" + parseInt(x).toString(16)).slice(-2);
}
function rgba2hex(rgba) {
    rgba = rgba.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)(,\s*\d+\.*\d+)?\)$/);
    return hex(rgba[1]) + hex(rgba[2]) + hex(rgba[3]);
}
[].slice.call(document.querySelectorAll(".js-label-link"))
    .forEach(function (element) {
        labels.push({
            name: element.textContent.trim(),
            description: element.getAttribute("title"),
            color: rgba2hex(window.getComputedStyle(element).getPropertyValue("background-color")),
        })
    })
console.log(JSON.stringify(labels, null, 2))

@trentm
Copy link

trentm commented Mar 31, 2021

One can also use: gh api /repos/elastic/$repo/labels to list a repo's label data, e.g.:

% gh api /repos/elastic/apm-agent-nodejs/labels
[
  {
    "id": 2510059555,
    "node_id": "MDU6TGFiZWwyNTEwMDU5NTU1",
    "url": "https://api.github.com/repos/elastic/apm-agent-nodejs/labels/agent-nodejs",
    "name": "agent-nodejs",
    "color": "2c4bba",
    "default": false,
    "description": "Make available for APM Agents project planning."
  },
  {
    "id": 2411255376,
    "node_id": "MDU6TGFiZWwyNDExMjU1Mzc2",
    "url": "https://api.github.com/repos/elastic/apm-agent-nodejs/labels/agent-spec",
    "name": "agent-spec",
    "color": "d8136f",
    "default": false,
    "description": "Related to a cross agent functionality spec."
  },
  {
    "id": 1151180629,
    "node_id": "MDU6TGFiZWwxMTUxMTgwNjI5",
    "url": "https://api.github.com/repos/elastic/apm-agent-nodejs/labels/automation",
    "name": "automation",
    "color": "81a2ef",
    "default": false,
    "description": null
  },
  {
    "id": 1322034517,
    "node_id": "MDU6TGFiZWwxMzIyMDM0NTE3",
    "url": "https://api.github.com/repos/elastic/apm-agent-nodejs/labels/awaiting%20reply",
    "name": "awaiting reply",
    "color": "f3a0f7",
    "default": false,
    "description": ""
  },
...

@avillen-dgtic
Copy link

avillen-dgtic commented Aug 12, 2021

Useful, thanks @NibrasAbuAyyash

@davorpa
Copy link

davorpa commented Aug 24, 2021

@davorpa
Copy link

davorpa commented Aug 24, 2021

I make my 2 penny's contrib 🤗

https://gist.github.com/davorpa/8201479803ff2a9bcc93a76a8fe71e43

All in one: gen + file export, wrapped in an IIFE, to merely save it as browser bookmark

@giuliogallerini
Copy link

giuliogallerini commented Oct 21, 2021

the class has changed to js-label-link and the description attribute to title. The colour is now saved in variables and I haven't found a way to grab the computed colour that is anyhow in rgb not hex

var labels = [];
[].slice.call(document.querySelectorAll(".js-label-link"))
.forEach(function(element) {
  labels.push({
    name: element.textContent.trim(),
    description: element.getAttribute("title"),
    // using style.backgroundColor might returns "rgb(...)"
    color: element.getAttribute("style")
      .replace("background-color:", "")
      .replace(/color:.*/,"")
      .trim()
      // github wants hex code only without # or ;
      .replace(/^#/, "")
      .replace(/;$/, "")
      .trim(),
  })
})
console.log(JSON.stringify(labels, null, 2))

@davorpa
Copy link

davorpa commented Oct 21, 2021

@giuliogallerini, I can't see something new.

var labels = [];
[].slice.call(document.querySelectorAll(".js-label-link"))
.forEach(function(element) {
  labels.push({
    name: element.textContent.trim(),
    description: element.getAttribute("title"),
    // using style.backgroundColor might returns "rgb(...)"
    color: element.getAttribute("style")
      .replace("background-color:", "")
      .replace(/color:.*/,"")
      .trim()
      // github wants hex code only without # or ;
      .replace(/^#/, "")
      .replace(/;$/, "")
      .trim(),
  })
})
console.log(JSON.stringify(labels, null, 2))

is duplicated of https://gist.github.com/MoOx/93c2853fee760f42d97f#gistcomment-3069259

the class has changed to js-label-link and the description attribute to title. The colour is now saved in variables and I haven't found a way to grab the computed colour that is anyhow in rgb not hex

Using computed style window.getComputedStyle(element).getPropertyValue("background-color"), as suggested by @NibrasAbuAyyash, avoids headaches of this kind of frontend changes

@BraidenCutforth
Copy link

BraidenCutforth commented Dec 7, 2021

Per @NibrasAbuAyyash and @davorpa using the background-color property, I got this to work in Chrome

function rgbaToHex(rgba) {
    const match = rgba.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i);
    const r = Number(match[1]);
    const g = Number(match[2]);
    const b = Number(match[3]);
    
    return ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
}

var labels = [];
[].slice.call(document.querySelectorAll(".js-label-link"))
.forEach(function(element) {
  labels.push({
    name: element.textContent.trim(),
    description: element.getAttribute("title"),
    color: rgbaToHex(window.getComputedStyle(element).backgroundColor),
  })
})
console.log(JSON.stringify(labels, null, 2))

@DeathGOD7
Copy link

DeathGOD7 commented Jan 11, 2022

Also to easily import and export things.
Use @davorpa code mentioned here. (direct link)

And for importing just install "Settings" app (link) by probot and make this file :
.github/settings.yml

# Labels: define labels for Issues and Pull Requests
labels:
- name: api
  description: This issue or pull request is related to API
  color: b60205
- name: not related
  description: not related to us or our plugins/addons
  color: d93f0b
- name: awaiting response
  description: Waiting for response

Happy importing. And thanks to @davorpa for both json and yml / yaml file saver.

@jamesperrin
Copy link

jamesperrin commented May 10, 2022

Here are my updates for exporting and importing issues labels.
It should be cross browser compatible except for IE which is pretty much dead.

github-labels-export.js
https://gist.github.com/jamesperrin/c2bf6d32fbb8142682f6107e561b664d

/*
Instructions
1. Navigate to desired GitHub repository.
2. Navigate to Issues tab.
3. Navigate to Labels link.
4. Open web browser Developer Tools
5. Navigate to the Console
6. Copy and Paste below code into the Console.
7. Save github-labels.json to a desired computer folder location.
*/

/**
 * @description Exports GitHub repository Issues labels to a JSON file
 *
 */
(function () {
    function hex(x) {
        return ('0' + parseInt(x).toString(16)).slice(-2);
    }

    function rgba2hex(rgba) {
        rgba = rgba.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)(,\s*\d+\.*\d+)?\)$/);
        return hex(rgba[1]) + hex(rgba[2]) + hex(rgba[3]);
    }

    // Process to export labels into a JSON object
    function getLabels() {
        const jsLabels = document.querySelectorAll('.js-label-link');

        if (!jsLabels || jsLabels.length < 1) {
            console.error('Unable to find GitHub labels');

            return;
        }

        const labels = [];

        [].slice.call(jsLabels).map(function (jsLabel) {
            labels.push({
                name: jsLabel.textContent.trim(),
                description: jsLabel.parentElement.nextElementSibling.firstElementChild.innerText.trim(),
                color: rgba2hex(window.getComputedStyle(jsLabel).getPropertyValue('background-color')),
            });
        });

        // Outputs labels to the Console
        console.log(JSON.stringify(labels, null, 2));

        return labels;
    }

    // Function save JSON object to a file
    function saveJSON(data, filename) {
        if (!data || data.length < 1) {
            console.error('No data');

            return;
        }

        const blob = new Blob([JSON.stringify(data, undefined, 4)], { type: 'text/json' });
        const e = document.createEvent('MouseEvents');
        const a = document.createElement('a');

        a.download = filename;
        a.href = window.URL.createObjectURL(blob);
        a.dataset.downloadurl = ['text/json', a.download, a.href].join(':');
        e.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
        a.dispatchEvent(e);
    }

    // Saves labels to JSON file.
    saveJSON(getLabels(), 'github-labels.json');
})();

github-labels-import.js
https://gist.github.com/jamesperrin/d811fadea2bd199ecf98195d96513afd

/**
 * 1. Update the labels JSON object.
 * 2. Open web browsers
 * 3. Navigate to desired GitHub repository.
 * 4. Navigate to Issues tab.
 * 5. Navigate to Labels link.
 * 6. Open web browswer Developer Tools
 * 7. Navigate to the Console window.
 * 8. Copy and Paste the below code snippets into the Console window.
  */

const labels = [
    {
        "name": "bug",
        "description": "Something isn't working",
        "color": "d73a4a"
    },
    {
        "name": "duplicate",
        "description": "This issue or pull request already exists",
        "color": "cfd3d7"
    },
    {
        "name": "enhancement",
        "description": "New feature or request",
        "color": "a2eeef"
    },
    {
        "name": "good first issue",
        "description": "Good for newcomers",
        "color": "7057ff"
    },
    {
        "name": "help wanted",
        "description": "Extra attention is needed",
        "color": "008672"
    },
    {
        "name": "invalid",
        "description": "This doesn't seem right",
        "color": "e4e669"
    },
    {
        "name": "question",
        "description": "Further information is requested",
        "color": "d876e3"
    },
    {
        "name": "wontfix",
        "description": "This will not be worked on",
        "color": "ffffff"
    }
];

// Function to update an existing label
function updateLabel(label) {
    let flag = false;
    [].slice.call(document.querySelectorAll('.labels-list-item'))
        .forEach(function (element) {
            if (element.querySelector('.label-link').textContent.trim() === label.name) {
                flag = true;
                element.querySelector('.js-edit-label').click();
                element.querySelector('.js-new-label-name-input').value = label.name;
                element.querySelector('.js-new-label-description-input').value = label.description;
                element.querySelector('.js-new-label-color-input').value = '#' + label.color;
                element.querySelector('.js-edit-label-cancel ~ .btn-primary').click();
            }
        });
    return flag;
}

// Function to add a new label
function addNewLabel(label) {
    document.querySelector('.js-new-label-name-input').value = label.name;
    document.querySelector('.js-new-label-description-input').value = label.description;
    document.querySelector('.js-new-label-color-input').value = '#' + label.color;
    document.querySelector('.js-details-target ~ .btn-primary').disabled = false;
    document.querySelector('.js-details-target ~ .btn-primary').click();
}

// Function to add a new label
function addLabel(label) {
    if (!updateLabel(label)) {
        addNewLabel(label);
    }
}

// Importing labels from JSON.
// This one is not available natively in IE, but there are polyfills available.
Promise.resolve().then(function () {
    labels.forEach(function (label) {
        addLabel(label);
    });
});

@jakobe
Copy link

jakobe commented Jun 10, 2022

Update June 2022: As far as I can see the description no longer exists as title on the label element itself.
Though it might be brittle to rely on the DOM structure of the labels page, this is my fix getting the description from the column after the label itself:

...
description: jsLabel.parentElement.nextElementSibling.firstElementChild.innerText.trim(),
...

@jamesperrin
Copy link

jamesperrin commented Jun 10, 2022

@jakobe I updated my code based on your comments. At the moment, looks like a good option.

@jakobe
Copy link

jakobe commented Jun 10, 2022

@jakobe I updated my code based on your comments. At the moment, looks like a good option.

Awesome 👍

@Brend-Smits
Copy link

Brend-Smits commented Jun 17, 2022

The official GitHub CLI now includes functionality that allows you to clone labels easily from one repo to another.
Example syntax:
gh label clone org-name/repo-to-clone-from --repo org-name/repo-to-clone-to

See the documentation for more information.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment