GitLab Projects

List of your last active GitLab projects. Now supports API v4

Image preview of GitLab Projects plugin.

gitlab_projects.js

Edit
Open on GitHub
#!/usr/bin/env /usr/local/bin/node
// jshint asi:true
// <xbar.title>GitLab Projects</xbar.title>
// <xbar.version>v2.0</xbar.version>
// <xbar.author>Shelton Koskie</xbar.author>
// <xbar.author.github>eightygrit</xbar.author.github>
// <xbar.desc>List of your last active GitLab projects. Now supports API v4</xbar.desc>
// <xbar.dependencies>node.js</xbar.dependencies>
// <xbar.image>http://i.imgur.com/4X40XIK.png</xbar.image>

/**
 * Information
 *
 * This was inspired by the work of Sylvain Baronnet (@sylvainbaronnet), who made the
 * first version of the "GITLAB Projects" for API v3. What started as a couple of edits
 * became a total rewrite.
 *
 * @see   GitLab API Documentation    https://docs.gitlab.com/ee/api/README.html
 * @see   Create GitLab Access Token  https://gitlab.com/profile/personal_access_tokens
 * @see   BitBar Node Module Docs     https://github.com/sindresorhus/bitbar
 */



/**
 * The domain your instance is hosted on. Leave the default if using gitlab.com
 *
 * @var       {string}
 */
var gitlab_domain = 'gitlab.com';

/**
 * Your private access token.
 *
 * @var       {string}
 * @see       Create a token  https://gitlab.com/profile/personal_access_tokens
 */
var private_token = '';

/**
 * When you select a project, which sub-page should it take you to?
 *
 * @var  {string}
 *
 * Options include:
 * - /issues
 * - /activity
 * - /commits
 */
var default_page  = '/issues';

/**
 * How would you like results ordered?
 *
 * @var  {string}
 *
 * Options include:
 * - last_activity_at
 * - id
 * - name
 * - path
 * - created_at
 * - updated_at
 */
var order_by      = 'last_activity_at';

/**
 * Total number of results you want returned
 *
 * @var       {string}
 */
var result_count  = '30';

/**
 * Font size of the project list
 *
 * @var       {string}
 */
var font_size     = '15';

/////////////////////////////////////////////////////////////////////////
// Do not edit below this line unless you know what you're doing. :)  //
///////////////////////////////////////////////////////////////////////
var bitbar;

// Verify bitbar node module is available or try to install it globally.
try { bitbar = require('bitbar'); }
catch(e) {

  try { bitbar = globalRequire('bitbar'); }
  catch(e) {

    installBitbarModule();

    // Not catching error if one is thrown.
    bitbar = globalRequire('bitbar');
  }
}

/**
 * Performs the GET request for a projects list for authenticated user
 *
 * @param   {Function}  callback  The function to call to handle the response
 *
 * @return  {void}                Does not return anything
 */
(function(callback) {
    'use strict';

    const httpTransport = require('https');
    const responseEncoding = 'utf8';
    const httpOptions = {
        hostname: gitlab_domain,
        port: '443',
        path: '/api/v4/projects?order_by=' + order_by + '&sort=desc&archived=false&per_page=' + result_count + '&owned=true',
        method: 'GET',
        headers: {"PRIVATE-TOKEN":private_token}
    };
    httpOptions.headers['User-Agent'] = 'bitbar/gitlab_projects - node ' + process.version;

    const request = httpTransport.request(httpOptions, (res) => {
        let responseBufs = [];
        let responseStr = '';

        res.on('data', (chunk) => {
            if (Buffer.isBuffer(chunk)) {
                responseBufs.push(chunk);
            }
            else {
                responseStr = responseStr + chunk;
            }
        }).on('end', () => {
            responseStr = responseBufs.length > 0 ?
                Buffer.concat(responseBufs).toString(responseEncoding) : responseStr;

            callback(null, res.statusCode, res.headers, responseStr);
        });

    })
    .setTimeout(0)
    .on('error', (error) => {
        callback(error);
    });
    request.write("")
    request.end();


})((error, statusCode, headers, body) => {
    // console.log('ERROR:', error);
    // console.log('STATUS:', statusCode);
    // console.log('HEADERS:', JSON.stringify(headers));

    const bitbar = globalRequire('bitbar');
    var content = [];

    content.push({
      text: 'GitLab',
      color: bitbar.darkMode ? 'white' : 'black',
      dropdown: true
    });

    content.push(bitbar.separator);

    var projects = JSON.parse(body);

    for(var p in projects) {

      var project = projects[p];
      var ta = timeago();
      var last_activity = ta.ago(project.last_activity_at);

      content.push({
        text: project.name + ' ⤏ ' + 'Last activity: ' + last_activity + ' | href="' + project.web_url + '/activity' + '" size=' + font_size
      });

      content.push({
        text: project.name + ' ⤏ ' + project.open_issues_count + ' issue' + (project.open_issues_count > 1 ? 's' : '') + ' | alternate=true href="' + project.web_url + '/issues" size=' + font_size
      });

      // Uncomment if you want a separator after each project.
      //content.push(bitbar.separator);
    }

    // Execute the dispaly.
    bitbar(content);


});

/* Source : https://github.com/digplan/time-ago */
function timeago() {

  var o = {
    second: 1000,
    minute: 60 * 1000,
    hour: 60 * 1000 * 60,
    day: 24 * 60 * 1000 * 60,
    week: 7 * 24 * 60 * 1000 * 60,
    month: 30 * 24 * 60 * 1000 * 60,
    year: 365 * 24 * 60 * 1000 * 60
  };
  var obj = {};

  obj.ago = function(nd) {
    var r = Math.round,
      pl = function(v, n) {
        return n + ' ' + v + (n > 1 ? 's' : '') + ' ago'
      },
      ts = new Date().getTime() - new Date(nd).getTime(),
      ii;
      if(ts < 0) {
        return 'Just now';
      }

    for (var i in o) {
      if (r(ts) < o[i]) return pl(ii || 'm', r(ts / (o[ii] || 1)))
      ii = i;
    }
    return pl(i, r(ts / o[i]));
  }
  return obj;
}

/**
 * Sets up the ability to require global node packages.
 *
 * @return     {object}  Returns the required node package object
 */
function globalRequire(package){
  var childProcess = require('child_process');
  var path = require('path');
  var fs = require('fs');
  var env = Object.assign({}, process.env);
  env.PATH = path.resolve("/usr/local/bin") + ':' + env.PATH;


  var globalNodeModulesDir = childProcess.execSync(npmBin() + ' root -g', {env: env}).toString().trim() + '/';
  var packageDir = path.join(globalNodeModulesDir, package, '/');

  //find package required by older versions of npm
  if (!fs.existsSync(packageDir)){
    packageDir = path.join(globalNodeModulesDir, 'npm/node_modules/', package);
  }

  // Package not found
  if (!fs.existsSync(packageDir)){
    throw new Error('Cannot find global module \'' + package + '\'');
  }

  var packageMeta = JSON.parse(fs.readFileSync(path.join(packageDir, 'package.json')).toString());
  var main = path.join(packageDir, packageMeta.files[0]);

  return require(main);
}

/**
 * Installs Bitbar node module if it doesn't exits.
 *
 * @see    BitBar node module on github    https://github.com/sindresorhus/bitbar
 */
function installBitbarModule() {

    // Allows one to run the npm command as if on the command line.
    var childProcess = require('child_process');
    var execSync = childProcess.execSync;
    var path = require('path');
    var fs = require('fs');

    var env = Object.assign({}, process.env);
    env.PATH = path.resolve("/usr/local/bin") + ':' + env.PATH;

    // Get the path to npm bin
    var npm = npmBin();

    // The install command
    var cmd = npm + ' install -g bitbar';

    console.log("Installing the BitBar Node module...");

    var output = execSync(cmd, {
        cwd: process.cwd(),
        env: env
    }).toString('utf8').trim();

    console.log("Installation complete.");

}

/**
 * Gets the path to your npm executable.
 *
 * @return  {string}  The full path to your npm executable
 */
function npmBin(){
  var path = require('path');
  var childProcess = require('child_process');
  var execSync = childProcess.execSync;
  var env = Object.assign({}, process.env);
  env.PATH = path.resolve("/usr/local/bin") + ':' + env.PATH;
  var buffs = [];

  // Get the path to npm bin
  return execSync('which npm', {env: env}).toString('utf8').trim();
}