@buildo/hophop
Version: 

128 lines (108 loc) • 4.62 kB
JavaScript
import R from 'ramda';
import fs from 'fs';
import promisify from 'es6-promisify';
import TogglClient from 'toggl-api';
import {
  rl,
  currBranch,
  issuesLabelMapper,
  ghHelper,
  warning, log,
  getHophopDir,
  HophopError
} from '../utils';
const togglTokenPath = `${getHophopDir()}/.toggl_token`;
function togglToken() {
  return fs.readFileSync(togglTokenPath, 'utf8');
}
function togglHelper() {
  const togglClient = new TogglClient({ apiToken: togglToken() });
  return { togglClient };
}
async function togglGetProjects(togglClient) {
  const togglUser = await promisify(togglClient.getUserData.bind(togglClient))({});
  const wid = togglUser.default_wid;
  const togglProjects = await promisify(togglClient.getWorkspaceProjects.bind(togglClient))(wid, {});
  return { togglProjects };
}
export async function togglSetup() {
  if (fs.existsSync(togglTokenPath)) {
    warning('\n⚠️  You already have a token for Toggl: if you proceed the current token will be overwritten.\n');
  }
  const token = await rl.question({ message: 'Personal access token: (from https://toggl.com/app/profile):' });
  if (!token) {
    throw new HophopError('No token inserted.');
  } else if (token.length !== 32) { // Toggl token length
    throw new HophopError('Invalid token: it should be 32 characters long.');
  }
  fs.writeFileSync(togglTokenPath, token);
}
export async function togglStart({ customTask } = {}) {
  const { togglClient } = togglHelper();
  const { repoName, client, repo } = await ghHelper(); // TODO; ghHelper gets also me -> this slows down the process
  const branch = await currBranch();
  if (branch === 'master' && !customTask) {
    throw new HophopError('You\'re not working on any PR, switch to a branch tracking a PR and retry.');
  }
  let issueno;
  let issue;
  if (!customTask) {
    issueno = R.take(2, branch.split('-').map((i) => parseInt(i, 10))).filter((x) => !!x)[0];
    log(`Inferred issue number ${issueno} from branch name '${branch}'`);
    if (!issueno) {
      log('Cannot infer the issue number from the branch name');
      const issues = await promisify(repo.issues.bind(repo))();
      const issuesChoices = issuesLabelMapper(issues.filter((i) => !i.pull_request));
      issueno = await rl.question({ type: 'list', message: 'Which issue?', choices: issuesChoices });
    }
    const issueRef = client.issue(repoName, issueno);
    issue = await promisify(issueRef.info.bind(issueRef))();
    if (!issue) {
      log('Invalid issue');
    }
  }
  const { togglProjects } = await togglGetProjects(togglClient);
  const projectName = R.compose(R.drop(1), R.split('/'))(repoName)[0];
  const startTimeEntry = promisify(togglClient.startTimeEntry.bind(togglClient));
  const getProject = (_projectName) => R.find(R.propEq('name', _projectName))(togglProjects);
  const togglProject = getProject(projectName);
  if (!togglProject) {
    warning(`\n🚨  '${projectName}' does not exist on Toggl\n`);
  }
  await startTimeEntry({
    description: customTask || `#${issueno}: ${issue.title}`,
    billable: togglProject ? togglProject.billable : undefined,
    pid: togglProject ? togglProject.id : undefined
  });
  const projectLog = togglProject ? ` on project '${togglProject.name}'` : '';
  const issueLog = !customTask ? `issue ${issueno}` : `'${customTask}'`;
  log(`⏰  Started tracking time for ${issueLog}${projectLog}`);
}
export async function togglMisc() {
  await togglStart({ customTask: 'miscellaneous' });
}
export async function togglStop() {
  const { togglClient } = togglHelper();
  const currentTimeEntry = await promisify(togglClient.getCurrentTimeEntry.bind(togglClient))();
  if (currentTimeEntry) {
    await promisify(togglClient.stopTimeEntry.bind(togglClient))(currentTimeEntry.id);
    log(`⏰  Stopped tracking time for '${currentTimeEntry.description}'`);
    const startGenericTask = await rl.question({ message: 'Start generic task? (y/n)', default: 'n' });
    if (startGenericTask === 'y') {
      await togglMisc();
    }
  } else {
    log('Mmh, it seems like your Toggl isn\'t running');
  }
}
export async function togglInstallHooks() {
  const exec = promisify(require('child_process').exec);
  const prefix = (await exec('npm prefix -g')).replace(/\r?\n|\r/g, '');
  const repoDir = (await exec('git rev-parse --show-toplevel')).replace(/\r?\n|\r/g, '');
  fs.symlinkSync(
    `${prefix}/lib/node_modules/@buildo/hophop/hooks/post-checkout`,
    `${repoDir}/.git/hooks/post-checkout`
  );
  log('🔧  Toggl git hooks correctly installed!');
}
export const togglTest = () => togglStart({ customTask: 'test' });