UNPKG

techdebt

Version:

An attempt to show technical debt to sponsors

413 lines (302 loc) 9.57 kB
# techdebt Utils function to compute technical debt metrics and display them on the support of your choice. ## Install ```bash npm install --save-dev techdebt ``` ## Get started This package is nothing more than an aggregation of helpers to get, save and display metrics. It helps you to easily write a small script that computes metrics representing your technical debt. A good idea would be to run this script during continuous integration so that it updates a technical debt dashboard or Slack channel. ### Simple example: display the todo comments in Slack ```javascript const techdebt = require('techdebt') const slackClient = require('techdebt/clients/slack') const fsHelper = require('techdebt/helpers/fs') slackClient.initialize({ hookUrl: 'https://hooks.slack.com/services/xx/xx/xx', channel: '#techdebt', username: 'Techdebt bot' }) techdebt.run([ { get: () => fsHelper.getRegexMatches('src', /todo(.*)/gi), format: (matches) => ({ title: 'Todos in code', text: matches.length > 0 : matches.join('\n') : 'No todos in code ☀️', color: matches.length > 0 : 'warning' : 'good' }) }, ]) .then(formatedMetrics => slackClient.post('Techdebt report', formatedMetrics)) .then(() => { return process.exit() }) .catch(error => { console.error(error) return process.exit(1) }) ``` ![example result](https://github.com/lalalilo/techdebt/raw/master/doc/screenshot1.png "Example result") ### More complex example: display the historic of todos count in Slack Sometimes it's useful to get the history of a metric and display it as a graph. To save a metric you can use any API that you like. The simplest API I found is [datadog](https://www.datadoghq.com/) that allows to save metrics and take a snapshot of a graph of the metric. ```javascript const techdebt = require('techdebt') const slackClient = require('techdebt/clients/slack') const fsHelper = require('techdebt/helpers/fs') slackClient.initialize({ hookUrl: 'https://hooks.slack.com/services/xx/xx/xx', channel: '#techdebt', username: 'Techdebt bot' }) datadogClient.initialize({ api_key: 'xxx', app_key: 'xxx' }) techdebt.run([ { get: () => { const count = fsHelper.countRegex('src', /todo/gi) return count }, save: (count) => datadogClient.post('techdebt.todos', count), format: (count) => datadogClient.get('techdebt.todos', 3600 * 24 * 30) .then(snapshotUrl => ({ text: 'Todos in code', image_url: snapshotUrl })) } ]) .then(formatedMetrics => slackClient.post('Techdebt report', formatedMetrics)) .then(() => { return process.exit() }) .catch(error => { console.error(error) return process.exit(1) }) ``` ![example result](https://github.com/lalalilo/techdebt/raw/master/doc/screenshot2.png "Example result") ## API doc ### main #### techdebt.run(metrics) Takes an array of [metrics](#metric) and return a promise of formated metrics. Example: ```javascript const techdebt = require('techdebt') techdebt.run(metrics) .then(formatedMetrics => { // do whatever you want with formated metrics }) ``` ### Metric ```javascript { get: Function save: Function format: Function } ``` #### get() A function that fetch the metric value. It returns a value of any type (you'll have to handle the value). It can return a value or the promise of a value. Example: ```javascript get: () => fsHelper.countRegex('src', /todo/gi) ``` #### save(metricValue) (optional) A function that save the value on a third party tool such as Datadog, Google Spreadsheet etc. If it returns a promise, the format function will wait for this promise to be resolved before execution. Example: ```javascript const save = (metricValue) => datadogClient.post('techdebt.my_metric', metricValue) ``` #### format(metricValue) A function that format the value as expected. Example: ```javascript format = (metricValue) => ({ title: 'Todos count in code', text: metricValue }) ``` ### Clients #### Slack ##### slackClient.initialize(options) Initialize the client with options Example: ```javascript const slackClient = require('techdebt/clients/slack') slackClient.initialize({ hookUrl: 'https://hooks.slack.com/services/xx/xx/xx', // required channel: '#techdebt', // required username: 'Techdebt bot' // optional }) ``` ##### slackClient.post(title, attachments) Post attachments to Slack. #### Datadog Using this client requires to install dogapi package: ```bash npm install --save-dev dogapi ``` ##### datadogClient.initialize(options) ```javascript const datadogClient = require('techdebt/clients/datadog') datadogClient.initialize({ api_key: 'xxx', //required app_key: 'xxx' // required }) ``` #### Codecov ##### codecovClient.initialize(options) ```javascript const codecovClient = require('techdebt/clients/codecov') codecovClient.initialize({ accessToken: 'xxx', //required repo: 'owner/repo', // required branch: 'xxx' // required }) ``` ##### codecovClient.get() Return a promise on the last code coverage ratio. ```javascript codecovClient.get() .then(({ timestamp, ratio }) => { // ratio between 0 and 100 }) ``` ##### datadogClient.post(metricName, metricValue, timestamp) Post a metric value that will be saved to datadog and wait for the metric to be fetchable (datadog has a delay in serving posted metrics). The **timestamp** attribute is optional (default is now) It returns a promise that resolves when the metric is saved. ##### datadogClient.get(metricName, period) Get the snapshot of a graph of a metric. It returns a promise of the URL of the snapshot image. #### missing a client? Trello, Google sheet ? Please [write an issue](https://github.com/lalalilo/techdebt/issues) ### Helpers #### packages This helper requires to install `npm-check` ```bash npm install --save-dev npm-check ``` This helper allow you to know which of your dependencies are unused or need upgrades ##### packages.get(type) type can be one of `major`, `minor`, `unused`, `upToDate`. It returns a promise of the list of packages corresponding to the type. ##### packages.slackFormat(type, packages) Returns a predefined Slack attachment format Example: ```javascript const slackClient = require('techdebt/clients/slack') const packages = require('techdebt/helpers/packages') const techdebt = require('techdebt') slackClient.initialize({ hookUrl: 'https://hooks.slack.com/services/xx/xx/xx', channel: '#techdebt', username: 'Techdebt bot' }) techdebt.run([ { get: () => packages.get('major'), format: (items) => packages.slackFormat('major', items) }, { get: () => packages.get('minor'), format: (items) => packages.slackFormat('minor', items) }, { get: () => packages.get('unused'), format: (items) => packages.slackFormat('unused', items) }, { get: () => packages.get('upToDate'), format: (items) => packages.slackFormat('upToDate', items) } ]) .then(formatedMetrics => { return slackClient.post('Techdebt report', formatedMetrics) }) .then(() => { return process.exit() }) .catch(error => { console.error(error) return process.exit(1) }) ``` ![example result](https://github.com/lalalilo/techdebt/raw/master/doc/screenshot3.png "Example result") #### fsHelper A librairy of helper functions to analyse source files. ##### fsHelper.readRecursively(root) Get the recursive list of files in a directory. Example: ```javascript fsHelper.readRecursively('src') ``` ##### fsHelper.countLines(root, options) Sum the lines of files in a directory (recursively). You can specify an extension in the options. The returned value is a promise of the line count. Example: ```javascript fsHelper.countLines('src', { extension: 'js' }).then(lineCount => ...) ``` ##### fsHelper.countRegex(root, regex, options) Sum the number of occurence if a regex in a directory (recursively). You can specify an extension in the options. The returned value is a promise of the count. Example: ```javascript fsHelper.countRegex('src', /todo/gi, { extension: 'js' }).then(count => ...) ``` ```javascript const slackClient = require('techdebt/clients/slack') const datadogClient = require('techdebt/clients/datadog') const fsHelper = require('techdebt/helpers/fs') const techdebt = require('techdebt') slackClient.initialize({ hookUrl: 'https://hooks.slack.com/services/xx/xx/xx', channel: '#techdebt', username: 'Techdebt bot' }) datadogClient.initialize({ api_key: 'xxx', app_key: 'xxx' }) techdebt.run([ { get: () => { const count = fsHelper.countRegex('src', /todo/gi) return count }, save: (value) => datadogClient.post('techdebt.todos', value), format: (count) => datadogClient.get('techdebt.todos', 3600 * 24 * 30).then(snapshotUrl => ({ text: 'Todos in code', image_url: snapshotUrl })) }, { get: () => fsHelper.getRegexMatches('src', /todo(.*)/gi), format: (matches) => ({ title: 'Todos in code', text: matches.join('\n'), color: 'warning' }) } ]) .then(formatedMetrics => { return slackClient.post('Techdebt report', formatedMetrics) }) .then(() => { return process.exit() }) .catch(error => { console.error(error) return process.exit(1) }) ``` ##### fsHelper.getRegexMatches(root, regex, options) Get the matches of a regex in a directory (recursively). You can specify an extension in the options. The returned value is a promise of the matches. Example: ```javascript fsHelper.getRegexMatches('src', /todo(.*)/gi, { extension: 'js' }).then(matches => ...) ```