Skip to content

Commit

Permalink
Draft the CLI tool
Browse files Browse the repository at this point in the history
  • Loading branch information
lerebear committed Oct 7, 2023
0 parents commit 6f24ecf
Show file tree
Hide file tree
Showing 18 changed files with 412 additions and 0 deletions.
11 changes: 11 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
root = true

[*]
indent_style = space
indent_size = 2
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.md]
trim_trailing_whitespace = false
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/dist
6 changes: 6 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"extends": [
"oclif",
"oclif-typescript"
]
}
32 changes: 32 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Continuous Integration

on:
pull_request:
push:
branches:
- '*'

jobs:
test:
name: Run unit tests
runs-on: ubuntu-latest

steps:
- name: Checkout
id: checkout
uses: actions/checkout@v4

- name: Setup node.js
id: setup-node
uses: actions/setup-node@v3
with:
node-version: 20
cache: npm

- name: Install dependencies
id: npm-ci
run: npm ci

- name: Run tests
id: npm-test
run: npm test
12 changes: 12 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
**/.DS_Store
*-debug.log
*-error.log
/.idea
/.nyc_output
/dist
/lib
/package-lock.json
/tmp
/yarn.lock
node_modules
oclif.manifest.json
12 changes: 12 additions & 0 deletions .mocharc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"require": [
"test/helpers/init.js",
"ts-node/register"
],
"watch-extensions": [
"ts"
],
"recursive": true,
"reporter": "spec",
"timeout": 60000
}
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2023 Ọlalérè Williams

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
59 changes: 59 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# SizeUp CLI

This repository contains a CLI that wraps the [`sizeup` library](https://github.com/lerebear/sizeup) in order to provide a way to estimate the reviewability of a local diff.

## Installation

```sh
npm install sizeup-cli
```

## Usage

Run using `npx` e.g.

```sh
npx sizeup --help
```

The output of the above help command looks like this:

```sh
Estimate how difficult a diff will be to review

USAGE
$ sizeup [DIFF] [-c <value>] [-t <value>] [-v]

ARGUMENTS
DIFF
[default: @wip] An identifier for the diff to evalute.

The following identifiers are supported:

@wip - special identifier that denotes the modified files in the git working tree (i.e. the result of `git diff`)
@staged - special identifer that denotes the files in the git staging area (i.e. the result of `git diff --staged`)
<url> - The URL of a pull request on GitHub (e.g. "https://github.com/lerebear/sizeup/pull/1")

FLAGS
-c, --config-path=<value> Path to configuration file for the sizeup lib.
For more details, see: https://github.com/lerebear/sizeup#configuration
-t, --token-path=<value> Path to a file containing a GitHub API token.
If this flag is omitted and the `diff` argument is a URL, then this tool will prompt for a token instead.
-v, --verbose Explain scoring procedure in detail

DESCRIPTION
Estimate how difficult a diff will be to review

EXAMPLES
Estimate the reviewability of the diff of the modified files in the git working tree

$ sizeup @wip

Estimate the reviewability of the diff of the staged files in the git index using a custom configuration file

$ sizeup @staged --config-path experimental.yaml

(Re)compute the reviewability of the diff from an existing pull request

$ sizeup https://github.com/lerebear/sizeup/pull/1
```
17 changes: 17 additions & 0 deletions bin/dev
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/usr/bin/env node

const oclif = require('@oclif/core')

const path = require('path')
const project = path.join(__dirname, '..', 'tsconfig.json')

// In dev mode -> use ts-node and dev plugins
process.env.NODE_ENV = 'development'

require('ts-node').register({project})

// In dev mode, always show stack traces
oclif.settings.debug = true;

// Start the CLI
oclif.run().then(oclif.flush).catch(oclif.Errors.handle)
3 changes: 3 additions & 0 deletions bin/dev.cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@echo off

node "%~dp0\dev" %*
5 changes: 5 additions & 0 deletions bin/run
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env node

const oclif = require('@oclif/core')

oclif.run().then(require('@oclif/core/flush')).catch(require('@oclif/core/handle'))
3 changes: 3 additions & 0 deletions bin/run.cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@echo off

node "%~dp0\run" %*
68 changes: 68 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
{
"name": "sizeup-cli",
"version": "0.1.0",
"description": "CLI wrapper for the sizeup library",
"author": "Ọlálérè Williams",
"bin": {
"sizeup": "./bin/run"
},
"homepage": "https://github.com/lerebear/sizeup-cli",
"license": "MIT",
"main": "dist/index.js",
"repository": "lerebear/sizeup-cli",
"files": [
"/bin",
"/dist",
"/npm-shrinkwrap.json",
"/oclif.manifest.json"
],
"dependencies": {
"@oclif/core": "^3",
"@oclif/plugin-help": "^5",
"@oclif/plugin-plugins": "^3.6.1",
"octokit": "^3.1.1",
"simple-git": "^3.20.0",
"sizeup": "github:lerebear/sizeup#v0.1.1"
},
"devDependencies": {
"@oclif/test": "^2.5.3",
"@types/chai": "^4",
"@types/mocha": "^9.0.0",
"@types/node": "^16.18.50",
"chai": "^4",
"eslint": "^7.32.0",
"eslint-config-oclif": "^4",
"eslint-config-oclif-typescript": "^1.0.3",
"mocha": "^9",
"oclif": "^3.15.0",
"shx": "^0.3.3",
"ts-node": "^10.9.1",
"tslib": "^2.6.2",
"typescript": "^4.9.5"
},
"oclif": {
"bin": "sizeup",
"dirname": "sizeup",
"default": ".",
"commands": "./dist",
"plugins": [
"@oclif/plugin-help"
]
},
"scripts": {
"build": "shx rm -rf dist && tsc -b",
"lint": "eslint . --ext .ts --config .eslintrc",
"postpack": "shx rm -f oclif.manifest.json",
"posttest": "npm run lint",
"prepack": "npm run build && oclif manifest",
"test": "mocha --forbid-only \"test/**/*.test.ts\""
},
"engines": {
"node": ">=12.0.0"
},
"bugs": "https://github.com/lerebear/sizeup-cli/issues",
"keywords": [
"oclif"
],
"types": "dist/index.d.ts"
}
120 changes: 120 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import {Args, Command, Flags, ux} from '@oclif/core'
import {simpleGit} from 'simple-git'
import SizeUpLibrary from 'sizeup'
import {Octokit} from 'octokit'
import * as fs from 'node:fs'

export default class SizeUp extends Command {
static description = 'Estimate how difficult a diff will be to review';

static examples = [
{
description: 'Estimate the reviewability of the diff of the modified files in the git working tree',
command: '<%= config.bin %> @wip',
},
{
description: 'Estimate the reviewability of the diff of the staged files in the git index using a custom configuration file',
command: '<%= config.bin %> @staged --config-path experimental.yaml',
},
{
description: '(Re)compute the reviewability of the diff from an existing pull request',
command: '<%= config.bin %> https://github.com/lerebear/sizeup/pull/1',
},
];

static flags = {
'config-path': Flags.string({
char: 'c',
description: 'Path to configuration file for the sizeup lib.\n' +
'For more details, see: https://github.com/lerebear/sizeup#configuration',
required: false,
}),
'token-path': Flags.string({
char: 't',
description: 'Path to a file containing a GitHub API token.\n' +
'If this flag is omitted and the `diff` argument is a URL, then this tool will prompt for a token instead.',
required: false,
}),
verbose: Flags.boolean({
char: 'v',
description: 'Explain scoring procedure in detail',
required: false,
}),
};

static args = {
diff: Args.string({
description: 'An identifier for the diff to evalute.\n\n' +
'The following identifiers are supported:\n\n' +
' @wip - special identifier that denotes the modified files in the git working tree (i.e. the result of `git diff`)\n' +
' @staged - special identifer that denotes the files in the git staging area (i.e. the result of `git diff --staged`)\n' +
' <url> - The URL of a pull request on GitHub (e.g. "https://github.com/lerebear/sizeup/pull/1")',
required: false,
default: '@wip',
}),
};

async run(): Promise<void> {
const {args, flags} = await this.parse(SizeUp)

const git = simpleGit()
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [_scheme, _blank, _domain, owner, repo, _path, number] = args.diff.split('/')
let octokit: Octokit | undefined
let token: string | undefined
let diff: string | undefined

switch (args.diff) {
case '@wip':
ux.action.start('Retrieving diff from the working tree')
diff = await git.diff()
ux.action.stop()
break
case '@stage':
case '@staged':
case '@cache':
case '@cached':
ux.action.start('Retrieving diff from the staging area')
diff = await git.diff(['--staged'])
ux.action.stop()
break
default:
token = flags['token-path'] ?
fs.readFileSync(flags['token-path']).toString().trim() :
await ux.prompt('Please enter a GitHub API token', {type: 'hide'})
octokit = new Octokit({auth: token})
try {
ux.action.start(`Retrieving diff from ${args.diff}`)
diff = (
await octokit.rest.pulls.get({
owner: owner,
repo: repo,
// eslint-disable-next-line camelcase
pull_number: Number.parseInt(number, 10),
mediaType: {format: 'diff'},
})
).data as unknown as string
} catch (error) {
const message = (error instanceof Error) ? error.message : ''
ux.action.stop(`failed (${message.toLowerCase()})`)
return
}

ux.action.stop()
break
}

if (diff) {
ux.action.start(`Evaluating the diff with the ${flags['config-path'] ? `config from ${flags['config-path']}` : 'default config'}`)
const score = SizeUpLibrary.evaluate(diff!, flags['config-path'])
ux.action.stop()
this.log(`Your diff scored ${score.result}${(score.category ? ` (${score.category.name})` : '')}.`)

if (flags.verbose) {
this.log(`The score was computed as follows:\n${score.toString()}`)
}
} else {
this.log(`The diff identified by '${args.diff}' was empty.`)
}
}
}
6 changes: 6 additions & 0 deletions test/helpers/init.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const path = require('path')
process.env.TS_NODE_PROJECT = path.resolve('test/tsconfig.json')
process.env.NODE_ENV = 'development'

global.oclif = global.oclif || {}
global.oclif.columns = 80
Loading

0 comments on commit 6f24ecf

Please sign in to comment.