├── .eslintignore ├── bin ├── run.cmd └── run ├── src ├── index.ts ├── pagination.ts ├── opener.ts ├── hooks │ └── init │ │ ├── auth-questions.ts │ │ └── auth.ts ├── auth-file.ts ├── commands │ ├── config.ts │ ├── commits.ts │ ├── code.ts │ ├── repositories.ts │ ├── notifications.ts │ └── issues.ts ├── api.ts ├── notifications-api.ts └── base-command.ts ├── .gitattributes ├── .prettierrc.yaml ├── tslint.json ├── .vscode └── settings.json ├── .prettierignore ├── test ├── helpers │ ├── init.js │ └── utils.ts ├── tsconfig.json ├── __fixtures__ │ ├── commands_repositories_errors_unknown_user.json │ ├── commands_notifications_issue_comment.json │ ├── commands_notifications_release.json │ ├── commands_repositories_runs_repo_user_feinoujc.json │ ├── commands_notifications_default.json │ ├── commands_code_runs_runs_oclif_repo_parser.json │ ├── commands_code_runs_runs_oclif_repo_parser_full_text.json │ └── commands_commits_runs_runs_oclif_repo_parser.json └── commands │ ├── config.test.ts │ ├── auth.test.ts │ └── commands.test.ts ├── .gitignore ├── .mocharc.yml ├── .editorconfig ├── tsconfig.json ├── .github ├── workflows │ ├── auto-merge.yml │ ├── publish.yaml │ └── build.yaml └── dependabot.yml ├── .eslintrc ├── LICENSE ├── package.json └── README.md /.eslintignore: -------------------------------------------------------------------------------- 1 | /lib 2 | -------------------------------------------------------------------------------- /bin/run.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | node "%~dp0\run" %* 4 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export { run } from '@oclif/command'; 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.js text eol=lf 3 | *.ts text eol=lf 4 | -------------------------------------------------------------------------------- /.prettierrc.yaml: -------------------------------------------------------------------------------- 1 | singleQuote: true 2 | useTabs: true 3 | trailingComma: all 4 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@oclif/tslint", "tslint-config-prettier"] 3 | } 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib" 3 | } -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | package.json 2 | package-lock.json 3 | .nyc_output/ 4 | coverage/ 5 | bin/ 6 | lib/ 7 | .vscode/ 8 | -------------------------------------------------------------------------------- /bin/run: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | require('@oclif/command').run() 4 | .catch(require('@oclif/errors/handle')) 5 | -------------------------------------------------------------------------------- /test/helpers/init.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | process.env.TS_NODE_PROJECT = path.resolve('test/tsconfig.json'); 3 | -------------------------------------------------------------------------------- /src/pagination.ts: -------------------------------------------------------------------------------- 1 | import cli from 'cli-ux'; 2 | 3 | export default { 4 | next: () => cli.anykey('Press key to continue...'), 5 | }; 6 | -------------------------------------------------------------------------------- /src/opener.ts: -------------------------------------------------------------------------------- 1 | import { cli } from 'cli-ux'; 2 | 3 | export default { 4 | open: (...args: Parameters) => cli.open(...args), 5 | }; 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *-debug.log 2 | *-error.log 3 | .oclif.manifest.json 4 | yarn.lock 5 | /lib 6 | /node_modules 7 | /tmp 8 | .nyc_output 9 | coverage/ 10 | -------------------------------------------------------------------------------- /test/helpers/utils.ts: -------------------------------------------------------------------------------- 1 | import { randomBytes } from 'crypto'; 2 | 3 | export function random(): string { 4 | return randomBytes(16).toString('hex'); 5 | } 6 | -------------------------------------------------------------------------------- /test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig", 3 | "compilerOptions": { 4 | "sourceMap": true 5 | }, 6 | "include": ["./**/*", "../src/**/*"] 7 | } 8 | -------------------------------------------------------------------------------- /.mocharc.yml: -------------------------------------------------------------------------------- 1 | extension: 2 | - ts 3 | forbid-only: true 4 | recursive: true 5 | reporter: spec 6 | require: 'ts-node/register' 7 | timeout: 20000 8 | watch-files: 9 | - '**/*.ts' 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.md] 11 | trim_trailing_whitespace = false 12 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "declaration": true, 4 | "forceConsistentCasingInFileNames": true, 5 | "importHelpers": true, 6 | "module": "commonjs", 7 | "outDir": "./lib", 8 | "pretty": true, 9 | "rootDirs": ["./src"], 10 | "strict": true, 11 | "target": "es2017" 12 | }, 13 | "include": ["./src/**/*"] 14 | } 15 | -------------------------------------------------------------------------------- /.github/workflows/auto-merge.yml: -------------------------------------------------------------------------------- 1 | name: auto-merge 2 | 3 | on: 4 | pull_request_target: 5 | 6 | jobs: 7 | auto-merge: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - uses: ahmadnassri/action-dependabot-auto-merge@v2 12 | with: 13 | target: minor 14 | github-token: ${{ secrets.DEPENDABOT_GITHUB_TOKEN }} 15 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "oclif", 4 | "oclif-typescript", 5 | "prettier" 6 | ], 7 | "rules": { 8 | "@typescript-eslint/no-unused-vars": [ 9 | "error", 10 | { 11 | "vars": "local", 12 | "args": "after-used", 13 | "argsIgnorePattern": "^_", 14 | "ignoreRestSiblings": true 15 | } 16 | ] 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/__fixtures__/commands_repositories_errors_unknown_user.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "Validation Failed", 3 | "errors": [ 4 | { 5 | "message": "The listed users and repositories cannot be searched either because the resources do not exist or you do not have permission to view them.", 6 | "resource": "Search", 7 | "field": "q", 8 | "code": "invalid" 9 | } 10 | ], 11 | "documentation_url": "https://developer.github.com/v3/search/" 12 | } 13 | -------------------------------------------------------------------------------- /src/hooks/init/auth-questions.ts: -------------------------------------------------------------------------------- 1 | import cli from 'cli-ux'; 2 | 3 | export default { 4 | ghe: () => cli.confirm('Do you have a github enterprise instance?'), 5 | apiUrl: () => 6 | cli.prompt('What is your api url? (ex: https://github.company.com/api/v3)'), 7 | username: () => cli.prompt('What is your github username?'), 8 | password: () => 9 | cli.prompt('What is your github password (not stored)?', { type: 'hide' }), 10 | otp: () => cli.prompt('What is your github OTP/2FA code?', { type: 'hide' }), 11 | }; 12 | -------------------------------------------------------------------------------- /test/commands/config.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from '@oclif/test'; 2 | import * as sinon from 'sinon'; 3 | 4 | import AuthFile from '../../src/auth-file'; 5 | 6 | describe('config', () => { 7 | const sandbox = sinon.createSandbox(); 8 | let clearStub: sinon.SinonStub<[], Promise>; 9 | beforeEach(() => { 10 | clearStub = sandbox.stub(AuthFile.prototype, 'clear').resolves(); 11 | }); 12 | 13 | afterEach(() => { 14 | sandbox.restore(); 15 | }); 16 | 17 | test 18 | .stdout() 19 | .command(['config', '--clear']) 20 | .it('runs config --clear', ctx => { 21 | expect(clearStub.callCount).to.equal(1); 22 | expect(ctx.stdout).to.contain('config file cleared'); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /.github/workflows/publish.yaml: -------------------------------------------------------------------------------- 1 | name: Node.js Package 2 | 3 | on: 4 | release: 5 | types: [created] 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v1 12 | - uses: actions/setup-node@v1 13 | with: 14 | node-version: 12 15 | - run: npm ci 16 | - run: npm test 17 | 18 | publish-npm: 19 | needs: build 20 | runs-on: ubuntu-latest 21 | steps: 22 | - uses: actions/checkout@v1 23 | - uses: actions/setup-node@v1 24 | with: 25 | node-version: 12 26 | registry-url: https://registry.npmjs.org/ 27 | - run: npm ci 28 | - run: npm publish 29 | env: 30 | NODE_AUTH_TOKEN: ${{secrets.npm_token}} 31 | -------------------------------------------------------------------------------- /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | name: Node CI 2 | 3 | on: [push] 4 | 5 | env: 6 | CI: 'true' 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | 12 | strategy: 13 | matrix: 14 | node-version: [14.x, 16.x] 15 | 16 | steps: 17 | - uses: actions/checkout@v1 18 | - uses: actions/cache@v1 19 | with: 20 | path: node_modules 21 | key: ${{ runner.OS }}-build-${{ hashFiles('**/package-lock.json') }} 22 | 23 | - name: Use Node.js ${{ matrix.node-version }} 24 | uses: actions/setup-node@v1 25 | with: 26 | node-version: ${{ matrix.node-version }} 27 | - run: npm ci 28 | - run: npm run build 29 | - name: smoke test cli 30 | run: | 31 | ./bin/run --version 32 | ./bin/run --help 33 | - run: npm test 34 | - name: upload coverage to codecov 35 | uses: codecov/codecov-action@v1 36 | with: 37 | token: ${{ secrets.CODECOV_TOKEN }} 38 | -------------------------------------------------------------------------------- /src/auth-file.ts: -------------------------------------------------------------------------------- 1 | import * as Config from '@oclif/config'; 2 | import * as fs from 'fs-extra'; 3 | import * as path from 'path'; 4 | 5 | export type AuthConfig = { 6 | token: string; 7 | baseUrl: string; 8 | }; 9 | 10 | export default class AuthFile { 11 | config: Config.IConfig; 12 | 13 | path: string; 14 | 15 | constructor(config: Config.IConfig) { 16 | this.config = config; 17 | this.path = path.join(this.config.configDir, 'auth.json'); 18 | } 19 | 20 | async getConfig(): Promise> { 21 | try { 22 | const auth = await fs.readJSON(this.path); 23 | if (auth && auth.token && auth.baseUrl) { 24 | return auth; 25 | } 26 | await fs.outputJson(this.path, {}); 27 | return {}; 28 | } catch (error) { 29 | if (error.code === 'ENOENT') { 30 | await fs.outputJson(this.path, {}); 31 | return {}; 32 | } 33 | throw error; 34 | } 35 | } 36 | 37 | setConfig(config: AuthConfig): Promise { 38 | return fs.outputJson(this.path, config); 39 | } 40 | 41 | clear(): Promise { 42 | return fs.outputJson(this.path, {}); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Joseph Feinour 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/commands/config.ts: -------------------------------------------------------------------------------- 1 | import { Command, flags } from '@oclif/command'; 2 | 3 | import AuthFile from '../auth-file'; 4 | 5 | export default class Config extends Command { 6 | static description = 'Configure ghs settings'; 7 | 8 | static examples = [ 9 | `$ ghs config --clear 10 | config cleared 11 | `, 12 | ]; 13 | 14 | static flags = { 15 | clear: flags.boolean({ 16 | description: 'clears the local config file including the auth token.', 17 | }), 18 | token: flags.string({ description: 'sets the github token to use.' }), 19 | 'base-url': flags.string({ 20 | description: 21 | 'sets the github base url for github enterprise instances (ex: https://github.company.com/api/v3).', 22 | }), 23 | }; 24 | 25 | fail() { 26 | this._help(); 27 | this.exit(-1); 28 | } 29 | 30 | async run() { 31 | const { flags } = this.parse(Config); 32 | const { clear, token, 'base-url': baseUrl } = flags; 33 | const file = new AuthFile(this.config); 34 | if (clear) { 35 | await file.clear(); 36 | this.log('config file cleared'); 37 | } else if (token && baseUrl) { 38 | await file.setConfig({ token, baseUrl }); 39 | } else if (token) { 40 | const config = await file.getConfig(); 41 | await file.setConfig({ 42 | baseUrl: 'https://api.github.com', 43 | ...config, 44 | token, 45 | }); 46 | } else if (baseUrl) { 47 | const config = await file.getConfig(); 48 | if (config.token) { 49 | await file.setConfig({ token: config.token, baseUrl }); 50 | } else { 51 | this.fail(); 52 | } 53 | } else { 54 | this.fail(); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "02:00" 8 | open-pull-requests-limit: 10 9 | labels: 10 | - dependencies 11 | ignore: 12 | - dependency-name: "@types/nock" 13 | versions: 14 | - ">= 11.a, < 12" 15 | - dependency-name: chai 16 | versions: 17 | - 4.3.0 18 | - 4.3.1 19 | - dependency-name: "@typescript-eslint/eslint-plugin" 20 | versions: 21 | - 4.14.2 22 | - 4.15.0 23 | - 4.15.1 24 | - 4.15.2 25 | - 4.18.0 26 | - 4.19.0 27 | - 4.20.0 28 | - 4.21.0 29 | - dependency-name: "@typescript-eslint/parser" 30 | versions: 31 | - 4.14.2 32 | - 4.15.0 33 | - 4.15.1 34 | - 4.15.2 35 | - 4.18.0 36 | - 4.19.0 37 | - 4.20.0 38 | - 4.21.0 39 | - dependency-name: "@types/node" 40 | versions: 41 | - 14.14.24 42 | - 14.14.25 43 | - 14.14.26 44 | - 14.14.28 45 | - 14.14.30 46 | - 14.14.32 47 | - 14.14.34 48 | - 14.14.35 49 | - 14.14.36 50 | - 14.14.37 51 | - 14.14.39 52 | - 14.14.41 53 | - 15.0.0 54 | - dependency-name: eslint 55 | versions: 56 | - 7.20.0 57 | - 7.22.0 58 | - 7.23.0 59 | - 7.24.0 60 | - dependency-name: "@types/fs-extra" 61 | versions: 62 | - 9.0.10 63 | - 9.0.7 64 | - 9.0.9 65 | - dependency-name: typescript 66 | versions: 67 | - 4.1.4 68 | - 4.1.5 69 | - 4.2.2 70 | - dependency-name: y18n 71 | versions: 72 | - 4.0.1 73 | - 4.0.2 74 | - dependency-name: sinon 75 | versions: 76 | - 10.0.0 77 | - dependency-name: mocha 78 | versions: 79 | - 8.3.1 80 | - dependency-name: nock 81 | versions: 82 | - 13.0.10 83 | -------------------------------------------------------------------------------- /test/__fixtures__/commands_notifications_issue_comment.json: -------------------------------------------------------------------------------- 1 | { 2 | "url": "https://api.github.com/repos/DefinitelyTyped/DefinitelyTyped/issues/comments/534729067", 3 | "html_url": "https://github.com/DefinitelyTyped/DefinitelyTyped/pull/38554#issuecomment-534729067", 4 | "issue_url": "https://api.github.com/repos/DefinitelyTyped/DefinitelyTyped/issues/38554", 5 | "id": 534729067, 6 | "node_id": "MDEyOklzc3VlQ29tbWVudDUzNDcyOTA2Nw==", 7 | "user": { 8 | "login": "typescript-bot", 9 | "id": 23042052, 10 | "node_id": "MDQ6VXNlcjIzMDQyMDUy", 11 | "avatar_url": "https://avatars0.githubusercontent.com/u/23042052?v=4", 12 | "gravatar_id": "", 13 | "url": "https://api.github.com/users/typescript-bot", 14 | "html_url": "https://github.com/typescript-bot", 15 | "followers_url": "https://api.github.com/users/typescript-bot/followers", 16 | "following_url": "https://api.github.com/users/typescript-bot/following{/other_user}", 17 | "gists_url": "https://api.github.com/users/typescript-bot/gists{/gist_id}", 18 | "starred_url": "https://api.github.com/users/typescript-bot/starred{/owner}{/repo}", 19 | "subscriptions_url": "https://api.github.com/users/typescript-bot/subscriptions", 20 | "organizations_url": "https://api.github.com/users/typescript-bot/orgs", 21 | "repos_url": "https://api.github.com/users/typescript-bot/repos", 22 | "events_url": "https://api.github.com/users/typescript-bot/events{/privacy}", 23 | "received_events_url": "https://api.github.com/users/typescript-bot/received_events", 24 | "type": "User", 25 | "site_admin": false 26 | }, 27 | "created_at": "2019-09-24T20:15:19Z", 28 | "updated_at": "2019-09-24T20:15:19Z", 29 | "author_association": "CONTRIBUTOR", 30 | "body": "I just published [`@types/pollyjs__utils@2.6.0` to npm](https://www.npmjs.com/package/@types/pollyjs__utils)." 31 | } 32 | -------------------------------------------------------------------------------- /src/hooks/init/auth.ts: -------------------------------------------------------------------------------- 1 | import { Hook, IConfig } from '@oclif/config'; 2 | import { createHash } from 'crypto'; 3 | import * as request from 'request-promise-native'; 4 | import { StatusCodeError } from 'request-promise-native/errors'; 5 | 6 | import AuthFile, { AuthConfig } from '../../auth-file'; 7 | 8 | import questions from './auth-questions'; 9 | 10 | type Creds = { 11 | username: string; 12 | password: string; 13 | otp: string; 14 | }; 15 | 16 | async function prompt(config: IConfig): Promise { 17 | let baseUrl = 'https://api.github.com'; 18 | const enterprise = await questions.ghe(); 19 | if (enterprise) { 20 | baseUrl = await questions.apiUrl(); 21 | } 22 | const req = request.defaults({ 23 | json: true, 24 | baseUrl, 25 | headers: { 26 | 'User-Agent': config.userAgent, 27 | }, 28 | }); 29 | const auth = async (creds: Partial = {}): Promise => { 30 | let username = creds.username; 31 | let password = creds.password; 32 | if (!username) { 33 | username = await questions.username(); 34 | } 35 | if (!password) { 36 | password = await questions.password(); 37 | } 38 | try { 39 | const fingerprint = createHash('md5') 40 | .update(`${config.userAgent}|${Date.now()}`) 41 | .digest('hex'); 42 | const { token } = await req.post({ 43 | url: '/authorizations', 44 | headers: { 45 | 'X-GitHub-OTP': creds.otp, 46 | }, 47 | auth: { username, password }, 48 | body: { 49 | scopes: ['notifications'], 50 | note: config.name, 51 | fingerprint, 52 | }, 53 | }); 54 | return { token, baseUrl }; 55 | } catch (error) { 56 | const sce = error as StatusCodeError; 57 | if (sce) { 58 | if ( 59 | sce.statusCode === 401 && 60 | sce.response.headers['x-github-otp'] && 61 | sce.response.headers['x-github-otp']!.includes('required') 62 | ) { 63 | const otp = await questions.otp(); 64 | return auth({ username, password, otp }); 65 | } 66 | 67 | if (sce.statusCode === 401) { 68 | process.stderr.write('Bad login. Try again\n'); 69 | return auth({}); 70 | } 71 | 72 | throw error; 73 | 74 | } 75 | throw error; 76 | } 77 | }; 78 | return auth(); 79 | } 80 | 81 | const hook: Hook<'init'> = async function({ config, id }) { 82 | const blacklist = ['config', 'help', '--help', '--verson']; 83 | if (blacklist.includes(id!)) return; 84 | 85 | const file = new AuthFile(config); 86 | const auth = await file.getConfig(); 87 | const token = auth?.token; 88 | const baseUrl = auth?.baseUrl; 89 | 90 | if (!token || !baseUrl) { 91 | await file.setConfig(await prompt(config)); 92 | } 93 | }; 94 | 95 | export default hook; 96 | -------------------------------------------------------------------------------- /src/api.ts: -------------------------------------------------------------------------------- 1 | import * as headerParser from 'parse-link-header'; 2 | import * as request from 'request-promise-native'; 3 | 4 | export type ApiOptions = { 5 | apiToken: string; 6 | baseUrl: string; 7 | sort: string; 8 | order: string; 9 | userAgent: string; 10 | textMatch: boolean; 11 | }; 12 | 13 | export type ApiResponse = { 14 | items: Array; 15 | links: { 16 | [rel: string]: () => Promise; 17 | }; 18 | rate: { 19 | limit: number; 20 | remaining: number; 21 | reset: Date; 22 | }; 23 | }; 24 | 25 | const baseOpts = { 26 | json: true, 27 | resolveWithFullResponse: true, 28 | headers: { 29 | Accept: ['application/json', 'application/vnd.github.cloak-preview'], 30 | }, 31 | }; 32 | const req = request.defaults({ ...baseOpts }); 33 | 34 | function parseResponse(resp: any, opts: ApiOptions): ApiResponse { 35 | async function linkContinuation(url: string): Promise { 36 | const res = await req({ 37 | auth: { 38 | user: '', 39 | password: opts.apiToken, 40 | }, 41 | headers: { 42 | 'User-Agent': opts.userAgent, 43 | Accept: opts.textMatch 44 | ? [ 45 | ...baseOpts.headers.Accept, 46 | 'application/vnd.github.v3.text-match+json', 47 | ] 48 | : baseOpts.headers.Accept, 49 | }, 50 | url, 51 | }); 52 | return parseResponse(res, opts); 53 | } 54 | const result = { 55 | items: resp.body.items, 56 | links: {}, 57 | rate: { 58 | limit: resp.headers['x-ratelimit-limit'] || Number.MAX_SAFE_INTEGER, 59 | remaining: 60 | resp.headers['x-ratelimit-remaining'] || Number.MAX_SAFE_INTEGER, 61 | reset: new Date((resp.headers['x-ratelimit-reset'] || 0) * 1000), 62 | }, 63 | }; 64 | if (resp.headers.link) { 65 | const links = headerParser(resp.headers.link) as headerParser.Links; 66 | result.links = Object.entries(links).reduce( 67 | (link, [k, v]) => ({ 68 | ...link, 69 | [k]: () => linkContinuation(v.url), 70 | }), 71 | {}, 72 | ); 73 | } 74 | return result; 75 | } 76 | 77 | export default async function search( 78 | type: string, 79 | query: string, 80 | opts: ApiOptions, 81 | ): Promise { 82 | const options = { 83 | auth: { 84 | user: '', 85 | password: opts.apiToken, 86 | }, 87 | headers: { 88 | 'User-Agent': opts.userAgent, 89 | Accept: opts.textMatch 90 | ? [ 91 | ...baseOpts.headers.Accept, 92 | 'application/vnd.github.v3.text-match+json', 93 | ] 94 | : baseOpts.headers.Accept, 95 | }, 96 | baseUrl: opts.baseUrl, 97 | url: `/search/${type}`, 98 | qs: { 99 | q: query, 100 | sort: opts.sort || undefined, 101 | order: opts.order || undefined, 102 | }, 103 | }; 104 | const resp = await req(options); 105 | return parseResponse(resp, opts); 106 | } 107 | -------------------------------------------------------------------------------- /test/__fixtures__/commands_notifications_release.json: -------------------------------------------------------------------------------- 1 | { 2 | "url": "https://api.github.com/repos/typescript-eslint/typescript-eslint/releases/20186922", 3 | "assets_url": "https://api.github.com/repos/typescript-eslint/typescript-eslint/releases/20186922/assets", 4 | "upload_url": "https://uploads.github.com/repos/typescript-eslint/typescript-eslint/releases/20186922/assets{?name,label}", 5 | "html_url": "https://github.com/typescript-eslint/typescript-eslint/releases/tag/v2.3.1", 6 | "id": 20186922, 7 | "node_id": "MDc6UmVsZWFzZTIwMTg2OTIy", 8 | "tag_name": "v2.3.1", 9 | "target_commitish": "master", 10 | "name": "v2.3.1", 11 | "draft": false, 12 | "author": { 13 | "login": "JamesHenry", 14 | "id": 900523, 15 | "node_id": "MDQ6VXNlcjkwMDUyMw==", 16 | "avatar_url": "https://avatars1.githubusercontent.com/u/900523?v=4", 17 | "gravatar_id": "", 18 | "url": "https://api.github.com/users/JamesHenry", 19 | "html_url": "https://github.com/JamesHenry", 20 | "followers_url": "https://api.github.com/users/JamesHenry/followers", 21 | "following_url": "https://api.github.com/users/JamesHenry/following{/other_user}", 22 | "gists_url": "https://api.github.com/users/JamesHenry/gists{/gist_id}", 23 | "starred_url": "https://api.github.com/users/JamesHenry/starred{/owner}{/repo}", 24 | "subscriptions_url": "https://api.github.com/users/JamesHenry/subscriptions", 25 | "organizations_url": "https://api.github.com/users/JamesHenry/orgs", 26 | "repos_url": "https://api.github.com/users/JamesHenry/repos", 27 | "events_url": "https://api.github.com/users/JamesHenry/events{/privacy}", 28 | "received_events_url": "https://api.github.com/users/JamesHenry/received_events", 29 | "type": "User", 30 | "site_admin": false 31 | }, 32 | "prerelease": false, 33 | "created_at": "2019-09-23T17:02:22Z", 34 | "published_at": "2019-09-23T17:02:25Z", 35 | "assets": [], 36 | "tarball_url": "https://api.github.com/repos/typescript-eslint/typescript-eslint/tarball/v2.3.1", 37 | "zipball_url": "https://api.github.com/repos/typescript-eslint/typescript-eslint/zipball/v2.3.1", 38 | "body": "## [2.3.1](https://github.com/typescript-eslint/typescript-eslint/compare/v2.3.0...v2.3.1) (2019-09-23)\n\n\n### Bug Fixes\n\n* **eslint-plugin:** [cons-type-assns] handle namespaced types ([#975](https://github.com/typescript-eslint/typescript-eslint/issues/975)) ([c3c8b86](https://github.com/typescript-eslint/typescript-eslint/commit/c3c8b86))\n* **eslint-plugin:** [pfa] Allow async getter/setter in classes ([#980](https://github.com/typescript-eslint/typescript-eslint/issues/980)) ([e348cb2](https://github.com/typescript-eslint/typescript-eslint/commit/e348cb2))\n* **typescript-estree:** parsing error for await in non-async func ([#988](https://github.com/typescript-eslint/typescript-eslint/issues/988)) ([19abbe0](https://github.com/typescript-eslint/typescript-eslint/commit/19abbe0))\n\n\n\n" 39 | } 40 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gh-search-cli", 3 | "description": "github.com search cli", 4 | "version": "3.1.0", 5 | "author": "feinoujc @feinoujc", 6 | "bin": { 7 | "ghs": "./bin/run" 8 | }, 9 | "bugs": "https://github.com/feinoujc/gh-search-cli/issues", 10 | "dependencies": { 11 | "@oclif/command": "^1.4.4", 12 | "@oclif/config": "^1.3.60", 13 | "@oclif/parser": "^3.8.4", 14 | "@oclif/plugin-help": "5", 15 | "chalk": "^4.0.0", 16 | "cli-ux": "^6.0.6", 17 | "fs-extra": "^10.0.0", 18 | "parse-link-header": "^2.0.0", 19 | "request": "^2.85.0", 20 | "request-promise-native": "^1.0.5", 21 | "tslib": "^2.0.0" 22 | }, 23 | "devDependencies": { 24 | "@oclif/dev-cli": "^1.22.0", 25 | "@oclif/nyc-config": "1", 26 | "@oclif/test": "^1.2.4", 27 | "@types/chai": "^4.1.2", 28 | "@types/fs-extra": "^9.0.1", 29 | "@types/git-user-name": "^2.0.0", 30 | "@types/mocha": "^9.0.0", 31 | "@types/nock": "^10.0.3", 32 | "@types/node": "^17.0.0", 33 | "@types/parse-link-header": "^2.0.0", 34 | "@types/request-promise-native": "^1.0.14", 35 | "@types/sinon": "^10.0.2", 36 | "@typescript-eslint/eslint-plugin": "^4.4.0", 37 | "@typescript-eslint/parser": "^4.4.0", 38 | "chai": "^4.1.2", 39 | "cross-env": "^7.0.0", 40 | "eslint": "^7.11.0", 41 | "eslint-config-oclif": "^3.1.0", 42 | "eslint-config-oclif-typescript": "^1.0.2", 43 | "eslint-config-prettier": "^8.1.0", 44 | "globby": "^11.0.0", 45 | "mocha": "^9.0.3", 46 | "nock": "^13.0.2", 47 | "nyc": "^15.0.0", 48 | "prettier": "^2.0.2", 49 | "sinon": "^13.0.1", 50 | "ts-node": "^10.2.0", 51 | "typescript": "^4.0.3" 52 | }, 53 | "engines": { 54 | "node": ">=10" 55 | }, 56 | "files": [ 57 | ".oclif.manifest.json", 58 | "/bin", 59 | "/lib" 60 | ], 61 | "homepage": "https://github.com/feinoujc/gh-search-cli", 62 | "keywords": [ 63 | "oclif", 64 | "github", 65 | "search", 66 | "api", 67 | "cli" 68 | ], 69 | "license": "MIT", 70 | "main": "lib/index.js", 71 | "oclif": { 72 | "commands": "./lib/commands", 73 | "bin": "ghs", 74 | "plugins": [ 75 | "@oclif/plugin-help" 76 | ], 77 | "hooks": { 78 | "init": "./lib/hooks/init/auth" 79 | } 80 | }, 81 | "repository": "feinoujc/gh-search-cli", 82 | "scripts": { 83 | "posttest": "eslint . --ext .ts --config .eslintrc", 84 | "prepack": "rm -rf lib && tsc -b", 85 | "test": "nyc --reporter lcov mocha \"test/**/*.test.ts\"", 86 | "build": "rm -rf lib && tsc", 87 | "clean": "rm -f .oclif.manifest.json", 88 | "prettier:check": "prettier --check **/*.{js,json,ts,yaml,yml}", 89 | "prettier": "prettier --write **/*.{js,json,ts,yaml,yml}", 90 | "postpublish": "npm run clean", 91 | "prepublishOnly": "npm run build && oclif-dev manifest", 92 | "preversion": "npm run clean", 93 | "version": "oclif-dev readme && git add README.md", 94 | "debug": "cross-env DEBUG=${DEBUG:-ghs:*} ./bin/run" 95 | }, 96 | "types": "lib/index.d.ts" 97 | } 98 | -------------------------------------------------------------------------------- /src/commands/commits.ts: -------------------------------------------------------------------------------- 1 | import { flags } from '@oclif/command'; 2 | import * as chalk from 'chalk'; 3 | 4 | import { ApiResponse } from '../api'; 5 | import Command, { buildFlags, TableResult } from '../base-command'; 6 | 7 | export default class Code extends Command { 8 | static description = 9 | 'search github commits. https://developer.github.com/v3/search/#search-commits'; 10 | 11 | static examples = [ 12 | `$ ghs commit --repo octocat/Spoon-Knife css 13 | `, 14 | ]; 15 | 16 | static flags = buildFlags( 17 | { 18 | author: flags.string({ 19 | description: 20 | 'Matches commits authored by a user (based on email settings).', 21 | }), 22 | committer: flags.string({ 23 | description: 24 | 'Matches commits committed by a user (based on email settings).', 25 | }), 26 | 'author-name': flags.string({ 27 | description: 'Matches commits by author name.', 28 | }), 29 | 'committer-name': flags.string({ 30 | description: 'Matches commits by committer name.', 31 | }), 32 | 'author-email': flags.string({ 33 | description: 'Matches commits by author email.', 34 | }), 35 | 'committer-email': flags.string({ 36 | description: 'Matches commits by committer email.', 37 | }), 38 | 'author-date': flags.string({ 39 | description: 'Matches commits by author date range.', 40 | }), 41 | 'committer-date': flags.string({ 42 | description: 'Matches commits by committer date range.', 43 | }), 44 | merge: flags.boolean({ 45 | allowNo: true, 46 | description: 47 | '--merge filters to merge commits, --no-merge filters out merge commits.', 48 | }), 49 | hash: flags.string({ 50 | description: 'Matches commits by hash.', 51 | }), 52 | tree: flags.string({ 53 | description: 'Matches commits with the specified git tree hash.', 54 | }), 55 | parent: flags.string({ 56 | description: 'Matches commits that have a particular parent.', 57 | }), 58 | is: flags.enum({ 59 | options: ['public', 'private'], 60 | description: 'Matches public or private repositories.', 61 | }), 62 | user: flags.string({ 63 | description: 64 | 'Limits searches to a specific user. Use @me for your username.', 65 | }), 66 | org: flags.string({ 67 | description: 'Limits searches to a specific organization.', 68 | }), 69 | repo: flags.string({ 70 | description: 'Limits searches to a specific repository.', 71 | }), 72 | sort: flags.enum({ 73 | char: 's', 74 | options: ['author-date', 'committer-date'], 75 | description: 76 | 'The sort field. Can be author-date or committer-date. Default: results are sorted by best match.', 77 | }), 78 | }, 79 | ['sort'], 80 | ); 81 | 82 | static args = [...Command.args]; 83 | 84 | format(data: ApiResponse): TableResult { 85 | const rows = data.items.reduce((acc, item) => { 86 | const message = chalk.bold(item.commit.message); 87 | const ssha = item.sha.substring(0, 7); 88 | const shortenedUrl = item.html_url.replace(item.sha, ssha); 89 | const url = shortenedUrl; 90 | const repo = chalk.cyan(item.repository.name); 91 | acc.push({ message, repo, url }); 92 | return acc; 93 | }, []); 94 | 95 | return { 96 | rows, 97 | columns: { 98 | repo: {}, 99 | message: {}, 100 | url: {}, 101 | }, 102 | }; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/commands/code.ts: -------------------------------------------------------------------------------- 1 | import { flags } from '@oclif/command'; 2 | import * as chalk from 'chalk'; 3 | 4 | import { ApiResponse } from '../api'; 5 | import Command, { 6 | buildFlags, 7 | FormatOptions, 8 | TableResult, 9 | } from '../base-command'; 10 | 11 | export default class Code extends Command { 12 | static description = 13 | 'search github code. https://developer.github.com/v3/search/#search-code'; 14 | 15 | static examples = [ 16 | `$ ghs code --extension js "import _ from 'lodash'" 17 | `, 18 | ]; 19 | 20 | static flags = buildFlags( 21 | { 22 | in: flags.string({ 23 | description: 24 | 'Qualifies which fields are searched. With this qualifier you can restrict the search to the file contents (file), the file path (path), or both.', 25 | }), 26 | language: flags.string({ 27 | char: 'l', 28 | description: "Searches code based on the language it's written in.", 29 | }), 30 | size: flags.string({ 31 | description: 'Finds files that match a certain size (in bytes).', 32 | }), 33 | path: flags.string({ 34 | description: 35 | 'Specifies the path prefix that the resulting file must be under.', 36 | }), 37 | filename: flags.string({ 38 | description: 'Matches files by a substring of the filename.', 39 | }), 40 | extension: flags.string({ 41 | description: 'Matches files with a certain extension after a dot.', 42 | }), 43 | user: flags.string({ 44 | char: 'u', 45 | description: 46 | 'Limits searches to a specific user. Use @me for your username', 47 | }), 48 | repo: flags.string({ 49 | char: 'r', 50 | description: 'Limits searches to a specific repository.', 51 | }), 52 | org: flags.string({ 53 | char: 'o', 54 | description: 'Limits searchs to a specific organization', 55 | }), 56 | text: flags.boolean({ 57 | char: 't', 58 | description: 'Show full text match', 59 | }), 60 | sort: flags.enum({ 61 | char: 's', 62 | options: ['indexed'], 63 | description: 64 | 'The sort field. Can only be indexed, which indicates how recently a file has been indexed by the GitHub search infrastructure. Default: results are sorted by best match.', 65 | }), 66 | }, 67 | ['sort', 'text'], 68 | ); 69 | 70 | static args = [...Command.args]; 71 | 72 | format(data: ApiResponse, opts: FormatOptions): TableResult { 73 | const rows = data.items.reduce((acc, item) => { 74 | const repo = chalk.cyan(item.repository.name); 75 | const fullPath: string = item.html_url; 76 | 77 | const [blobSegment] = fullPath.match( 78 | /blob\/[0-9a-f]{40}\//, 79 | ) as RegExpMatchArray; 80 | let shortenedPath = fullPath; 81 | if (blobSegment) { 82 | shortenedPath = fullPath.replace( 83 | blobSegment, 84 | `${blobSegment.substring(0, 12)}/`, 85 | ); 86 | } 87 | const url = shortenedPath; 88 | acc.push({ 89 | repo, 90 | url, 91 | text: opts.text 92 | ? chalk.green( 93 | item.text_matches 94 | .map((textMatch: any) => textMatch.fragment) 95 | .join('/n'), 96 | ) 97 | : undefined, 98 | }); 99 | return acc; 100 | }, []); 101 | 102 | return { 103 | rows, 104 | columns: { 105 | ...(opts.text ? { text: { header: 'code' } } : { repo: {} }), 106 | url: {}, 107 | }, 108 | options: opts.text ? { 'no-truncate': true } : undefined, 109 | }; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/notifications-api.ts: -------------------------------------------------------------------------------- 1 | import * as headerParser from 'parse-link-header'; 2 | import * as request from 'request-promise-native'; 3 | import { StatusCodeError } from 'request-promise-native/errors'; 4 | 5 | export interface ApiOptions { 6 | apiToken: string; 7 | baseUrl: string; 8 | userAgent: string; 9 | } 10 | 11 | export interface ApiResponse { 12 | items: Array; 13 | links: { 14 | [rel: string]: () => Promise; 15 | }; 16 | } 17 | 18 | export interface ApiParams { 19 | all?: boolean; 20 | participating?: boolean; 21 | since?: string; 22 | before?: string; 23 | owner?: string; 24 | repo?: string; 25 | } 26 | 27 | const baseOpts = { 28 | json: true, 29 | resolveWithFullResponse: true, 30 | headers: { 31 | Accept: 'application/json', 32 | }, 33 | }; 34 | const req = request.defaults({ ...baseOpts }); 35 | 36 | async function fetchAndAssignSubjectHtmlUrl( 37 | subject: any, 38 | opts: ApiOptions, 39 | ): Promise { 40 | try { 41 | const { body } = await req({ 42 | auth: { 43 | user: '', 44 | password: opts.apiToken, 45 | }, 46 | headers: { 47 | 'User-Agent': opts.userAgent, 48 | Accept: baseOpts.headers.Accept, 49 | }, 50 | url: subject.latest_comment_url || subject.url, 51 | }); 52 | // eslint-disable-next-line camelcase 53 | Object.assign(subject, { latest_comment_html_url: body.html_url }); 54 | } catch (error) { 55 | // can fail on permissions issues 56 | if (error instanceof StatusCodeError && error.statusCode === 404) { 57 | // eslint-disable-next-line camelcase 58 | Object.assign(subject, { latest_comment_html_url: null }); 59 | return; 60 | } 61 | throw error; 62 | } 63 | } 64 | 65 | async function parseResponse( 66 | resp: any, 67 | opts: ApiOptions, 68 | ): Promise { 69 | async function linkContinuation(url: string): Promise { 70 | const res = await req({ 71 | auth: { 72 | user: '', 73 | password: opts.apiToken, 74 | }, 75 | headers: { 76 | 'User-Agent': opts.userAgent, 77 | Accept: baseOpts.headers.Accept, 78 | }, 79 | url, 80 | }); 81 | return parseResponse(res, opts); 82 | } 83 | 84 | const subjects = resp.body 85 | .map((notification: any) => notification.subject) 86 | .filter((subject: any) => subject.latest_comment_url || subject.url); 87 | 88 | await Promise.all( 89 | subjects.map((subject: any) => fetchAndAssignSubjectHtmlUrl(subject, opts)), 90 | ); 91 | 92 | const result = { items: resp.body, links: {} }; 93 | 94 | if (resp.headers.link) { 95 | const links = headerParser(resp.headers.link)!; 96 | result.links = Object.entries(links).reduce( 97 | (link, [k, v]) => ({ 98 | ...link, 99 | [k]: () => linkContinuation(v.url), 100 | }), 101 | {}, 102 | ); 103 | } 104 | return result; 105 | } 106 | 107 | export default async function getNotifications( 108 | params: ApiParams, 109 | opts: ApiOptions, 110 | ): Promise { 111 | const { owner, repo, ...qs } = params; 112 | const url = 113 | owner && repo ? `/repos/${owner}/${repo}/notifications` : '/notifications'; 114 | const options = { 115 | auth: { 116 | user: '', 117 | password: opts.apiToken, 118 | }, 119 | headers: { 120 | 'User-Agent': opts.userAgent, 121 | Accept: baseOpts.headers.Accept, 122 | }, 123 | baseUrl: opts.baseUrl, 124 | url, 125 | qs, 126 | }; 127 | const resp = await req(options); 128 | return parseResponse(resp, opts); 129 | } 130 | -------------------------------------------------------------------------------- /src/commands/repositories.ts: -------------------------------------------------------------------------------- 1 | import { flags } from '@oclif/command'; 2 | import * as chalk from 'chalk'; 3 | 4 | import { ApiResponse } from '../api'; 5 | import Command, { buildFlags, TableResult } from '../base-command'; 6 | 7 | export default class Repositories extends Command { 8 | static description = 9 | 'search github repositories. https://developer.github.com/v3/search/#search-repositories'; 10 | 11 | static examples = [ 12 | `$ ghs repo puppeteer 13 | GoogleChrome/puppeteer (https://github.com/GoogleChrome/puppeteer) 14 | `, 15 | ]; 16 | 17 | static flags = buildFlags( 18 | { 19 | in: flags.string({ 20 | description: 21 | 'Qualifies which fields are searched. With this qualifier you can restrict the search to just the repository name, description, readme, or any combination of these.', 22 | }), 23 | size: flags.string({ 24 | description: 25 | 'Finds repositories that match a certain size (in kilobytes).', 26 | }), 27 | forks: flags.string({ 28 | description: 'Filters repositories based on the number of forks.', 29 | }), 30 | fork: flags.boolean({ 31 | allowNo: true, 32 | char: 'f', 33 | description: 34 | 'Filters whether forked repositories should be included (--fork) or not (--no-fork).', 35 | }), 36 | created: flags.string({ 37 | char: 'c', 38 | description: 39 | 'Filters repositories based on date of creation, or when they were last updated.', 40 | }), 41 | pushed: flags.string({ 42 | char: 'p', 43 | description: 44 | 'Filters repositories based on date of creation, or when they were last updated.', 45 | }), 46 | user: flags.string({ 47 | char: 'u', 48 | description: 49 | 'Limits searches to a specific user. Use @me for your username.', 50 | }), 51 | repo: flags.string({ 52 | char: 'r', 53 | description: 'Limits searches to a specific repo.', 54 | }), 55 | language: flags.string({ 56 | char: 'l', 57 | description: 58 | "Searches repositories based on the language they're written in.", 59 | }), 60 | license: flags.string({ 61 | description: 62 | 'Filters repositories by license or license family, using the license keyword.', 63 | }), 64 | stars: flags.string({ 65 | description: 'Searches repositories based on the number of stars.', 66 | }), 67 | followers: flags.string({ 68 | description: 'Searches repositories based on the number of followers.', 69 | }), 70 | topic: flags.string({ 71 | description: 'Filters repositories based on the specified topic.', 72 | }), 73 | topics: flags.string({ 74 | description: 75 | 'Search repositories by the number of topics that have been applied to them.', 76 | }), 77 | mirror: flags.boolean({ 78 | allowNo: true, 79 | description: 80 | "Search repositories based on whether or not they're a mirror and are hosted elsewhere.", 81 | }), 82 | archived: flags.boolean({ 83 | allowNo: true, 84 | description: 85 | 'Filters whether archived repositories should be included (--archived) or not (--no-archived).', 86 | }), 87 | 'good-first-issues': flags.string({ 88 | description: 89 | 'Search for repositories that have a minimum number of issues labeled help-wanted.', 90 | }), 91 | 'help-wanted-issues': flags.string({ 92 | description: 93 | 'Search for repositories that have a minimum number of issues labeled good-first-issue.', 94 | }), 95 | sort: flags.enum({ 96 | char: 's', 97 | description: 98 | 'The sort field. Default: results are sorted by best match.', 99 | options: ['stars', 'forks', 'updated'], 100 | }), 101 | }, 102 | ['sort'], 103 | ); 104 | 105 | static aliases = ['repo', 'repository']; 106 | 107 | static args = [...Command.args]; 108 | 109 | format(data: ApiResponse): TableResult { 110 | const rows = data.items.reduce((acc, item) => { 111 | const repo = chalk.cyan(item.full_name); 112 | const url = item.html_url; 113 | acc.push({ repo, url }); 114 | return acc; 115 | }, []); 116 | 117 | return { 118 | rows, 119 | columns: { 120 | repo: {}, 121 | url: {}, 122 | }, 123 | }; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/commands/notifications.ts: -------------------------------------------------------------------------------- 1 | import { Command, flags } from '@oclif/command'; 2 | import * as chalk from 'chalk'; 3 | import { cli } from 'cli-ux'; 4 | import { StatusCodeError } from 'request-promise-native/errors'; 5 | 6 | import AuthFile from '../auth-file'; 7 | import BaseCommand, { TableResult } from '../base-command'; 8 | import getNotifications, { ApiResponse } from '../notifications-api'; 9 | import opener from '../opener'; 10 | import paginator from '../pagination'; 11 | 12 | function filteredBaseOptions(): Pick< 13 | typeof BaseCommand['flags'], 14 | 'api-base-url' | 'api-token' | 'json' | 'open' 15 | > { 16 | const { order, ...opts } = BaseCommand.flags; 17 | 18 | return opts; 19 | } 20 | 21 | export default class Notifications extends Command { 22 | static description = 'List notifications'; 23 | 24 | static flags = { 25 | ...filteredBaseOptions(), 26 | all: flags.boolean({ 27 | char: 'a', 28 | default: false, 29 | description: 'If true, show notifications marked as read. Default: false', 30 | }), 31 | participating: flags.boolean({ 32 | char: 'p', 33 | default: false, 34 | description: 35 | 'If true, only shows notifications in which the user is directly participating or mentioned. Default: false', 36 | }), 37 | since: flags.string({ 38 | char: 's', 39 | parse: input => new Date(input).toISOString(), 40 | description: 41 | 'Only show notifications updated after the given time. This is a timestamp in ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ', 42 | }), 43 | before: flags.string({ 44 | char: 'b', 45 | parse: input => new Date(input).toISOString(), 46 | description: 47 | 'Only show notifications updated before the given time. This is a timestamp in ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ.', 48 | }), 49 | owner: flags.string({ 50 | description: 'Filter notifications to a owner, required with --repo flag', 51 | }), 52 | repo: flags.string({ 53 | description: 54 | 'Filter notifications to a repository, required with --owner flag', 55 | }), 56 | }; 57 | 58 | format(data: ApiResponse): TableResult { 59 | const rows = data.items.reduce((acc, item) => { 60 | const subject = chalk.cyan(item.subject.title); 61 | const url = item.subject.latest_comment_html_url; 62 | const reason = item.reason; 63 | acc.push({ subject, reason, url }); 64 | return acc; 65 | }, []); 66 | 67 | return { 68 | rows, 69 | columns: { 70 | subject: {}, 71 | reason: {}, 72 | url: {}, 73 | }, 74 | }; 75 | } 76 | 77 | async run() { 78 | const { flags } = this.parse(Notifications); 79 | let { 80 | 'api-token': apiToken, 81 | 'api-base-url': baseUrl, 82 | // eslint-disable-next-line prefer-const 83 | ...params 84 | } = flags; 85 | 86 | this.debug('options: %o', params); 87 | 88 | const authFile = new AuthFile(this.config); 89 | const authConfig = await authFile.getConfig(); 90 | this.debug('auth config: %o', authConfig); 91 | if (!apiToken && authConfig) { 92 | apiToken = authConfig.token; 93 | } 94 | 95 | if (!baseUrl && authConfig) { 96 | baseUrl = authConfig.baseUrl; 97 | } else if (!baseUrl) { 98 | baseUrl = 'https://api.github.com'; 99 | } 100 | 101 | const print = async ( 102 | resp: ApiResponse, 103 | opts: { json?: boolean; open?: boolean }, 104 | ) => { 105 | if (opts.json) { 106 | this.log(JSON.stringify(resp.items)); 107 | } else if (resp.items.length === 0) { 108 | this.warn('no results found'); 109 | } else { 110 | if (opts.open) { 111 | const firstLink = resp.items 112 | .map(({ subject }) => subject.latest_comment_html_url) 113 | .find(Boolean); 114 | if (firstLink) { 115 | await opener.open(firstLink); 116 | } 117 | } 118 | const { rows, columns, options } = this.format(resp); 119 | cli.table(rows, columns, options); 120 | } 121 | }; 122 | 123 | const next = async (results: ApiResponse, opts: { json?: boolean }) => { 124 | if (!opts.json && results.links.next) { 125 | await paginator.next(); 126 | const resp = await results.links.next(); 127 | await print(resp, opts); 128 | await next(resp, opts); 129 | } 130 | }; 131 | 132 | const resp = await getNotifications(params, { 133 | baseUrl: baseUrl!, 134 | apiToken: apiToken!, 135 | userAgent: this.config.userAgent, 136 | }); 137 | await print(resp, flags); 138 | return next(resp, flags); 139 | } 140 | 141 | async catch(err: Error) { 142 | if (err instanceof StatusCodeError) { 143 | const lines: Array = []; 144 | lines.push(err.error.message); 145 | (err.error.errors ?? []).forEach((_err: Error) => 146 | lines.push(_err.message), 147 | ); 148 | this.warn(lines.join('\n')); 149 | } else { 150 | this.warn(err.message); 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /test/commands/auth.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from '@oclif/test'; 2 | import * as nock from 'nock'; 3 | import * as sinon from 'sinon'; 4 | 5 | import AuthFile, { AuthConfig } from '../../src/auth-file'; 6 | import questions from '../../src/hooks/init/auth-questions'; 7 | 8 | describe('hooks', () => { 9 | const sandbox = sinon.createSandbox(); 10 | afterEach(() => { 11 | sandbox.restore(); 12 | nock.cleanAll(); 13 | }); 14 | describe('already configured', () => { 15 | beforeEach(() => { 16 | sandbox 17 | .stub(AuthFile.prototype, 'getConfig') 18 | .resolves({ token: 'abc123', baseUrl: 'https://api.github.com' }); 19 | }); 20 | test 21 | .stderr() 22 | .stdout() 23 | .hook('init', { id: 'commits' }) 24 | .do(output => { 25 | expect(output.stderr).to.be.equal(''); 26 | expect(output.stdout).to.be.equal(''); 27 | }) 28 | .it('does nothing'); 29 | }); 30 | 31 | describe('skips if command is config', () => { 32 | test 33 | .stderr() 34 | .stdout() 35 | .hook('init', { id: 'config' }) 36 | .do(output => { 37 | expect(output.stderr).to.be.equal(''); 38 | expect(output.stdout).to.be.equal(''); 39 | }) 40 | .it('does nothing'); 41 | }); 42 | 43 | describe('needs configured (github, no 2fa)', () => { 44 | let configStub: sinon.SinonStub<[AuthConfig], Promise>; 45 | beforeEach(() => { 46 | sandbox.stub(AuthFile.prototype, 'getConfig').resolves({}); 47 | configStub = sandbox.stub(AuthFile.prototype, 'setConfig').resolves(); 48 | sandbox.stub(questions, 'ghe').resolves(false); 49 | sandbox.stub(questions, 'username').resolves('testuser'); 50 | sandbox.stub(questions, 'password').resolves('password'); 51 | nock('https://api.github.com') 52 | .post('/authorizations') 53 | .reply(200, { 54 | token: 'abc123', 55 | }); 56 | }); 57 | 58 | test 59 | .stderr() 60 | .hook('init', { id: 'commits' }) 61 | .do(() => { 62 | expect( 63 | configStub.calledOnceWith({ 64 | token: 'abc123', 65 | baseUrl: 'https://api.github.com', 66 | }), 67 | ).to.be.equal(true); 68 | }) 69 | .it('stores config'); 70 | }); 71 | 72 | describe('bad login', () => { 73 | let configStub: sinon.SinonStub<[AuthConfig], Promise>; 74 | beforeEach(() => { 75 | sandbox.stub(AuthFile.prototype, 'getConfig').resolves({}); 76 | configStub = sandbox.stub(AuthFile.prototype, 'setConfig').resolves(); 77 | sandbox.stub(questions, 'ghe').resolves(false); 78 | sandbox.stub(questions, 'username').resolves('testuser'); 79 | sandbox 80 | .stub(questions, 'password') 81 | .onFirstCall() 82 | .resolves('wrong') 83 | .onSecondCall() 84 | .resolves('correct'); 85 | nock('https://api.github.com', { 86 | reqheaders: { 87 | Authorization: auth => 88 | Buffer.from(auth.split(' ')[1], 'base64') 89 | .toString() 90 | .endsWith(':wrong'), 91 | }, 92 | }) 93 | .post('/authorizations') 94 | .reply(401, {}); 95 | 96 | nock('https://api.github.com', { 97 | reqheaders: { 98 | Authorization: auth => 99 | Buffer.from(auth.split(' ')[1], 'base64') 100 | .toString() 101 | .endsWith(':correct'), 102 | }, 103 | }) 104 | .post('/authorizations') 105 | .reply(200, { token: 'fgh345' }); 106 | }); 107 | 108 | test 109 | .stderr() 110 | .hook('init', { id: 'commits' }) 111 | .do(ctx => { 112 | expect(ctx.stderr).to.match(/bad login/i); 113 | expect( 114 | configStub.calledOnceWith({ 115 | token: 'fgh345', 116 | baseUrl: 'https://api.github.com', 117 | }), 118 | ).to.be.equal(true); 119 | }) 120 | .it('stores config'); 121 | }); 122 | 123 | describe('needs configured (github, 2fa)', () => { 124 | let configStub: sinon.SinonStub<[AuthConfig], Promise>; 125 | beforeEach(() => { 126 | sandbox.stub(AuthFile.prototype, 'getConfig').resolves({}); 127 | configStub = sandbox.stub(AuthFile.prototype, 'setConfig').resolves(); 128 | sandbox.stub(questions, 'ghe').resolves(false); 129 | sandbox.stub(questions, 'username').resolves('testuser'); 130 | sandbox.stub(questions, 'password').resolves('password'); 131 | sandbox.stub(questions, 'otp').resolves('12345'); 132 | 133 | nock('https://api.github.com') 134 | .post('/authorizations') 135 | .reply( 136 | 401, 137 | {}, 138 | { 139 | 'X-GitHub-OTP': 'required; app', 140 | }, 141 | ); 142 | nock('https://api.github.com', { 143 | reqheaders: { 144 | 'X-GitHub-OTP': '12345', 145 | }, 146 | }) 147 | .post('/authorizations') 148 | .reply(200, { 149 | token: 'abc123', 150 | }); 151 | }); 152 | 153 | test 154 | .stderr() 155 | .stdout() 156 | .hook('init', { id: 'commits' }) 157 | .do(() => { 158 | expect( 159 | configStub.calledOnceWith({ 160 | token: 'abc123', 161 | baseUrl: 'https://api.github.com', 162 | }), 163 | ).to.be.equal(true); 164 | }) 165 | .it('stores config'); 166 | }); 167 | 168 | describe('needs configured (ghe, no 2fa)', () => { 169 | let configStub: sinon.SinonStub<[AuthConfig], Promise>; 170 | beforeEach(() => { 171 | sandbox.stub(AuthFile.prototype, 'getConfig').resolves({}); 172 | configStub = sandbox.stub(AuthFile.prototype, 'setConfig').resolves(); 173 | sandbox.stub(questions, 'ghe').resolves(true); 174 | sandbox 175 | .stub(questions, 'apiUrl') 176 | .resolves('https://github.evilcorp.com/api/v3'); 177 | sandbox.stub(questions, 'username').resolves('testuser'); 178 | sandbox.stub(questions, 'password').resolves('password'); 179 | nock('https://github.evilcorp.com/api/v3') 180 | .post('/authorizations') 181 | .reply(200, { 182 | token: '456dfg', 183 | }); 184 | }); 185 | 186 | test 187 | .stderr() 188 | .hook('init', { id: 'commits' }) 189 | .do(() => { 190 | expect( 191 | configStub.calledOnceWith({ 192 | token: '456dfg', 193 | baseUrl: 'https://github.evilcorp.com/api/v3', 194 | }), 195 | ).to.be.equal(true); 196 | }) 197 | .it('stores config'); 198 | }); 199 | }); 200 | -------------------------------------------------------------------------------- /src/base-command.ts: -------------------------------------------------------------------------------- 1 | import Command, { flags } from '@oclif/command'; 2 | import { flags as parserFlags } from '@oclif/parser'; 3 | import cli, { Table } from 'cli-ux'; 4 | import { StatusCodeError } from 'request-promise-native/errors'; 5 | 6 | import search, { ApiResponse } from './api'; 7 | import AuthFile from './auth-file'; 8 | import opener from './opener'; 9 | import paginator from './pagination'; 10 | 11 | export type FormatOptions = { 12 | piped: boolean; 13 | text: boolean; 14 | open: boolean; 15 | json: boolean; 16 | }; 17 | 18 | export type TableResult = { 19 | rows: Array; 20 | columns: Table.table.Columns; 21 | options?: Table.table.Options; 22 | }; 23 | 24 | type IOptionFlag = parserFlags.IOptionFlag; 25 | type IBooleanFlag = parserFlags.IBooleanFlag; 26 | 27 | /** 28 | * Adds a hidden NOT qualifier flag to all option flags (--language & --not-language) provided and returns the combined result 29 | */ 30 | function negateOptionFlags< 31 | T extends Record | IBooleanFlag> 32 | >( 33 | flags: T, 34 | nonNegatable: (keyof T)[], 35 | ): T & Record | IBooleanFlag> { 36 | return Object.entries(flags).reduce< 37 | Record | IBooleanFlag> 38 | >((acc, [key, flag]) => { 39 | acc[key] = flag; 40 | if (flag.type === 'option' && !nonNegatable.includes(key)) { 41 | acc[`not-${key}`] = { ...flag, char: undefined, hidden: true }; 42 | } 43 | return acc; 44 | }, {}) as T & Record | IBooleanFlag>; 45 | } 46 | 47 | export default abstract class BaseCommand extends Command { 48 | static args = [{ name: 'query' }]; 49 | 50 | static flags = { 51 | 'api-token': flags.string({ 52 | description: 'The github api token. Defaults to configured api token', 53 | }), 54 | 'api-base-url': flags.string({ 55 | description: 56 | "The github api token. Defaults to configured GHE url or 'https://api.github.com'", 57 | }), 58 | open: flags.boolean({ 59 | char: 'o', 60 | description: 'Open the first result in your browser.', 61 | }), 62 | json: flags.boolean({ 63 | char: 'j', 64 | description: 'Return json. Can be piped to jq.', 65 | }), 66 | order: flags.enum({ 67 | description: 68 | 'The sort order if sort parameter is provided. Default: desc', 69 | options: ['asc', 'desc'], 70 | }), 71 | }; 72 | 73 | async catch(err: Error) { 74 | if (err instanceof StatusCodeError) { 75 | const lines: Array = []; 76 | lines.push(err.error.message); 77 | (err.error.errors || []).forEach((_err: Error) => 78 | lines.push(_err.message), 79 | ); 80 | this.warn(lines.join('\n')); 81 | } else { 82 | this.warn(err.message); 83 | } 84 | } 85 | 86 | abstract format(data: ApiResponse, opts?: FormatOptions): TableResult; 87 | 88 | async run() { 89 | const { args, flags } = this.parse(this.constructor as typeof Command); 90 | 91 | const qs: Array = []; 92 | if (args.query) { 93 | qs.push(args.query); 94 | } 95 | 96 | let { 'api-token': apiToken, 'api-base-url': baseUrl } = flags; 97 | 98 | const { sort, order, open, json, text, ...options } = flags; 99 | 100 | this.debug('options: %o', options); 101 | Object.entries(options).forEach(([k, v]) => { 102 | if (k.startsWith('not-')) { 103 | const negatedKey = k.replace(/^not-/, '-'); 104 | qs.push(`${negatedKey}:${v}`); 105 | } else { 106 | qs.push(`${k}:${v}`); 107 | } 108 | }); 109 | 110 | if (qs.length === 0) { 111 | this._help(); 112 | } 113 | const authFile = new AuthFile(this.config); 114 | const authConfig = await authFile.getConfig(); 115 | this.debug('auth config: %o', authConfig); 116 | if (!apiToken && authConfig) { 117 | apiToken = authConfig.token; 118 | } 119 | 120 | if (!baseUrl && authConfig) { 121 | baseUrl = authConfig.baseUrl; 122 | } else if (!baseUrl) { 123 | baseUrl = 'https://api.github.com'; 124 | } 125 | 126 | const type = `${this.id}`; 127 | 128 | const print = async (resp: ApiResponse, opts: FormatOptions) => { 129 | if (resp.items.length === 0) { 130 | this.warn('no results found'); 131 | } else if (opts.open) { 132 | await opener.open(resp.items[0].html_url); 133 | } else if (opts.json) { 134 | this.log(JSON.stringify(resp.items)); 135 | } else { 136 | const { rows, columns, options } = this.format(resp, opts); 137 | cli.table(rows, columns, { ...options, 'no-header': opts.piped }); 138 | } 139 | }; 140 | 141 | const next = async (results: ApiResponse, opts: FormatOptions) => { 142 | if (!opts.json && results.links.next) { 143 | if (opts.piped) { 144 | // if we get past 5 we tend to trigger abuse detection 145 | // so we back off if we fall below that 146 | if (results.rate.remaining < 5) { 147 | const waitFor = 148 | Math.max(results.rate.reset.valueOf() - new Date().valueOf(), 0) + 149 | 100; 150 | this.debug(`waiting for ${waitFor} before next api call`); 151 | await new Promise((resolve) => setTimeout(resolve, waitFor)); 152 | } 153 | } else { 154 | await paginator.next(); 155 | } 156 | const resp = await results.links.next(); 157 | await print(resp, opts); 158 | await next(resp, opts); 159 | } 160 | }; 161 | 162 | const opts: FormatOptions = { 163 | piped: !process.stdout.isTTY, 164 | open, 165 | json, 166 | text, 167 | }; 168 | 169 | this.debug('searching %s qs: %o sort: %s order: %s', type, qs, sort, order); 170 | 171 | const resp: any = await search(type, qs.join(' '), { 172 | apiToken, 173 | baseUrl, 174 | sort, 175 | order, 176 | userAgent: this.config.userAgent, 177 | textMatch: text, 178 | }); 179 | await print(resp, opts); 180 | return next(resp, opts); 181 | } 182 | } 183 | 184 | export function buildFlags< 185 | T extends Record | IBooleanFlag> 186 | >( 187 | flags: T, 188 | nonNegatable: (keyof T)[] = [], 189 | ): T & 190 | typeof BaseCommand['flags'] & 191 | Record | IBooleanFlag> { 192 | return { 193 | ...negateOptionFlags(flags, nonNegatable), 194 | ...BaseCommand.flags, 195 | }; 196 | } 197 | -------------------------------------------------------------------------------- /test/__fixtures__/commands_repositories_runs_repo_user_feinoujc.json: -------------------------------------------------------------------------------- 1 | { 2 | "total_count": 1, 3 | "incomplete_results": false, 4 | "items": [ 5 | { 6 | "id": 122796034, 7 | "node_id": "MDEwOlJlcG9zaXRvcnkxMjI3OTYwMzQ=", 8 | "name": "gh-search-cli", 9 | "full_name": "feinoujc/gh-search-cli", 10 | "private": false, 11 | "owner": { 12 | "login": "feinoujc", 13 | "id": 1733707, 14 | "node_id": "MDQ6VXNlcjE3MzM3MDc=", 15 | "avatar_url": "https://avatars2.githubusercontent.com/u/1733707?v=4", 16 | "gravatar_id": "", 17 | "url": "https://api.github.com/users/feinoujc", 18 | "html_url": "https://github.com/feinoujc", 19 | "followers_url": "https://api.github.com/users/feinoujc/followers", 20 | "following_url": "https://api.github.com/users/feinoujc/following{/other_user}", 21 | "gists_url": "https://api.github.com/users/feinoujc/gists{/gist_id}", 22 | "starred_url": "https://api.github.com/users/feinoujc/starred{/owner}{/repo}", 23 | "subscriptions_url": "https://api.github.com/users/feinoujc/subscriptions", 24 | "organizations_url": "https://api.github.com/users/feinoujc/orgs", 25 | "repos_url": "https://api.github.com/users/feinoujc/repos", 26 | "events_url": "https://api.github.com/users/feinoujc/events{/privacy}", 27 | "received_events_url": "https://api.github.com/users/feinoujc/received_events", 28 | "type": "User", 29 | "site_admin": false 30 | }, 31 | "html_url": "https://github.com/feinoujc/gh-search-cli", 32 | "description": "searches github via command line. Supports repo, issues, code and commit searches and listing notifications", 33 | "fork": false, 34 | "url": "https://api.github.com/repos/feinoujc/gh-search-cli", 35 | "forks_url": "https://api.github.com/repos/feinoujc/gh-search-cli/forks", 36 | "keys_url": "https://api.github.com/repos/feinoujc/gh-search-cli/keys{/key_id}", 37 | "collaborators_url": "https://api.github.com/repos/feinoujc/gh-search-cli/collaborators{/collaborator}", 38 | "teams_url": "https://api.github.com/repos/feinoujc/gh-search-cli/teams", 39 | "hooks_url": "https://api.github.com/repos/feinoujc/gh-search-cli/hooks", 40 | "issue_events_url": "https://api.github.com/repos/feinoujc/gh-search-cli/issues/events{/number}", 41 | "events_url": "https://api.github.com/repos/feinoujc/gh-search-cli/events", 42 | "assignees_url": "https://api.github.com/repos/feinoujc/gh-search-cli/assignees{/user}", 43 | "branches_url": "https://api.github.com/repos/feinoujc/gh-search-cli/branches{/branch}", 44 | "tags_url": "https://api.github.com/repos/feinoujc/gh-search-cli/tags", 45 | "blobs_url": "https://api.github.com/repos/feinoujc/gh-search-cli/git/blobs{/sha}", 46 | "git_tags_url": "https://api.github.com/repos/feinoujc/gh-search-cli/git/tags{/sha}", 47 | "git_refs_url": "https://api.github.com/repos/feinoujc/gh-search-cli/git/refs{/sha}", 48 | "trees_url": "https://api.github.com/repos/feinoujc/gh-search-cli/git/trees{/sha}", 49 | "statuses_url": "https://api.github.com/repos/feinoujc/gh-search-cli/statuses/{sha}", 50 | "languages_url": "https://api.github.com/repos/feinoujc/gh-search-cli/languages", 51 | "stargazers_url": "https://api.github.com/repos/feinoujc/gh-search-cli/stargazers", 52 | "contributors_url": "https://api.github.com/repos/feinoujc/gh-search-cli/contributors", 53 | "subscribers_url": "https://api.github.com/repos/feinoujc/gh-search-cli/subscribers", 54 | "subscription_url": "https://api.github.com/repos/feinoujc/gh-search-cli/subscription", 55 | "commits_url": "https://api.github.com/repos/feinoujc/gh-search-cli/commits{/sha}", 56 | "git_commits_url": "https://api.github.com/repos/feinoujc/gh-search-cli/git/commits{/sha}", 57 | "comments_url": "https://api.github.com/repos/feinoujc/gh-search-cli/comments{/number}", 58 | "issue_comment_url": "https://api.github.com/repos/feinoujc/gh-search-cli/issues/comments{/number}", 59 | "contents_url": "https://api.github.com/repos/feinoujc/gh-search-cli/contents/{+path}", 60 | "compare_url": "https://api.github.com/repos/feinoujc/gh-search-cli/compare/{base}...{head}", 61 | "merges_url": "https://api.github.com/repos/feinoujc/gh-search-cli/merges", 62 | "archive_url": "https://api.github.com/repos/feinoujc/gh-search-cli/{archive_format}{/ref}", 63 | "downloads_url": "https://api.github.com/repos/feinoujc/gh-search-cli/downloads", 64 | "issues_url": "https://api.github.com/repos/feinoujc/gh-search-cli/issues{/number}", 65 | "pulls_url": "https://api.github.com/repos/feinoujc/gh-search-cli/pulls{/number}", 66 | "milestones_url": "https://api.github.com/repos/feinoujc/gh-search-cli/milestones{/number}", 67 | "notifications_url": "https://api.github.com/repos/feinoujc/gh-search-cli/notifications{?since,all,participating}", 68 | "labels_url": "https://api.github.com/repos/feinoujc/gh-search-cli/labels{/name}", 69 | "releases_url": "https://api.github.com/repos/feinoujc/gh-search-cli/releases{/id}", 70 | "deployments_url": "https://api.github.com/repos/feinoujc/gh-search-cli/deployments", 71 | "created_at": "2018-02-25T01:39:16Z", 72 | "updated_at": "2019-09-24T13:10:28Z", 73 | "pushed_at": "2019-09-25T02:53:10Z", 74 | "git_url": "git://github.com/feinoujc/gh-search-cli.git", 75 | "ssh_url": "git@github.com:feinoujc/gh-search-cli.git", 76 | "clone_url": "https://github.com/feinoujc/gh-search-cli.git", 77 | "svn_url": "https://github.com/feinoujc/gh-search-cli", 78 | "homepage": "", 79 | "size": 791, 80 | "stargazers_count": 26, 81 | "watchers_count": 26, 82 | "language": "TypeScript", 83 | "has_issues": true, 84 | "has_projects": true, 85 | "has_downloads": true, 86 | "has_wiki": true, 87 | "has_pages": false, 88 | "forks_count": 4, 89 | "mirror_url": null, 90 | "archived": false, 91 | "disabled": false, 92 | "open_issues_count": 10, 93 | "license": { 94 | "key": "mit", 95 | "name": "MIT License", 96 | "spdx_id": "MIT", 97 | "url": "https://api.github.com/licenses/mit", 98 | "node_id": "MDc6TGljZW5zZTEz" 99 | }, 100 | "forks": 4, 101 | "open_issues": 10, 102 | "watchers": 26, 103 | "default_branch": "master", 104 | "permissions": { 105 | "admin": true, 106 | "push": true, 107 | "pull": true 108 | }, 109 | "score": 78.04065 110 | } 111 | ] 112 | } 113 | -------------------------------------------------------------------------------- /src/commands/issues.ts: -------------------------------------------------------------------------------- 1 | import { flags } from '@oclif/command'; 2 | import * as chalk from 'chalk'; 3 | 4 | import { ApiResponse } from '../api'; 5 | import Command, { buildFlags, TableResult } from '../base-command'; 6 | 7 | function sanitizeTicket(ticket: string) { 8 | return ticket.replace(/[^A-Z\d-]+/gi, ''); 9 | } 10 | 11 | const jiraRegex = /^(\[?[A-Z]{2,10}-\d{1,5}\]?[\s|:]*)?(.*)$/im; 12 | 13 | export default class Issues extends Command { 14 | static description = 15 | 'search github issues. https://developer.github.com/v3/search/#search-issues'; 16 | 17 | static examples = [ 18 | `$ ghs issues --is open --involves my-github-username 19 | `, 20 | ]; 21 | 22 | static flags = buildFlags( 23 | { 24 | type: flags.enum({ 25 | char: 't', 26 | description: 27 | 'With this qualifier you can restrict the search to issues (issue) or pull request (pr) only.', 28 | options: ['issue', 'pr'], 29 | }), 30 | in: flags.string({ 31 | description: 32 | 'Qualifies which fields are searched. With this qualifier you can restrict the searchto just the title (title), body (body), comments (comments), or any combination of these.', 33 | }), 34 | author: flags.string({ 35 | description: 36 | 'Finds issues or pull requests created by a certain user. Use @me for your username.', 37 | }), 38 | assignee: flags.string({ 39 | description: 40 | 'Finds issues or pull requeststhat are assigned to a certain user. Use @me for your username.', 41 | }), 42 | mentions: flags.string({ 43 | description: 44 | 'Finds issues or pull requests that mention a certain user. Use @me for your username.', 45 | }), 46 | commenter: flags.string({ 47 | description: 48 | 'Finds issues or pull requests that a certain user commented on. Use @me for your username.', 49 | }), 50 | involves: flags.string({ 51 | description: 52 | 'Finds issues or pull requests that were either created by a certain user, assigned to that user, mention that user, or were commented on by that user. Use @me for your username.', 53 | }), 54 | team: flags.string({ 55 | description: 56 | "For organizations you're a member of, finds issues or pull requests that @mention a team within the organization.", 57 | }), 58 | state: flags.enum({ 59 | description: 60 | "Filter issues or pull requests based on whether they're open or closed.", 61 | options: ['open', 'closed'], 62 | }), 63 | label: flags.string({ 64 | description: 'Filters issues or pull requests based on their labels.', 65 | }), 66 | milestone: flags.string({ 67 | description: 68 | 'Finds issues or pull requests that are a part of a milestone within a repository.', 69 | }), 70 | no: flags.string({ 71 | description: 72 | 'Filters items missing certain metadata, such as label, milestone, or assignee', 73 | }), 74 | SHA: flags.string({ 75 | description: 76 | 'If you know the specific SHA hash of a commit, you can use it to search for pull requests that contain that SHA. The SHA syntax must be at least seven characters.', 77 | }), 78 | interactions: flags.string({ 79 | description: 80 | 'You can filter issues and pull requests by the number of interactions with the interactions qualifier along with greater than, less than, and range qualifiers. The interactions count is the number of reactions and comments on an issue or pull request.', 81 | }), 82 | reactions: flags.string({ 83 | description: 84 | 'You can filter issues and pull requests by the number of reactions using the reactions qualifier along with greater than, less than, and range qualifiers.', 85 | }), 86 | review: flags.enum({ 87 | options: ['none', 'required', 'approved', 'changes_requested'], 88 | description: 89 | 'You can filter pull requests based on their review status', 90 | }), 91 | 'reviewed-by': flags.string({ 92 | description: 'Filter pull requests by reviewer.', 93 | }), 94 | 'review-requested': flags.string({ 95 | description: 'Filter pull requests by requested reviewer.', 96 | }), 97 | 'team-review-requested': flags.string({ 98 | description: 'Filter pull requests by requested reviewer.', 99 | }), 100 | language: flags.string({ 101 | char: 'l', 102 | description: 103 | 'Searches for issues or pull requests within repositories that match a certain language.', 104 | }), 105 | is: flags.string({ 106 | description: 107 | 'Searches for items within repositories that match a certain state, such as open, closed, or merged', 108 | }), 109 | created: flags.string({ 110 | char: 'c', 111 | description: 112 | 'Filters issues or pull requests based on date of creation,or when they were last updated.', 113 | }), 114 | updated: flags.string({ 115 | char: 'u', 116 | description: 117 | 'Filters issues or pull requests based on date of creation, or when they were last updated.', 118 | }), 119 | merged: flags.string({ 120 | char: 'm', 121 | description: 122 | 'Filters pull requests based on the date when they were merged.', 123 | }), 124 | status: flags.string({ 125 | char: 's', 126 | description: 'Filters pull requests based on the commit status.', 127 | }), 128 | base: flags.string({ 129 | description: 130 | 'Filters pull requests based on the branch that they came from.', 131 | }), 132 | head: flags.string({ 133 | description: 134 | 'Filters pull requests based on the branch that they are modifying.', 135 | }), 136 | closed: flags.string({ 137 | description: 138 | 'Filters issues or pull requests based on the date when they were closed.', 139 | }), 140 | comments: flags.string({ 141 | description: 142 | 'Filters issues or pull requests based on the quantity of comments.', 143 | }), 144 | user: flags.string({ 145 | char: 'u', 146 | description: 147 | 'Limits searches to a specific user. Use @me for your username.', 148 | }), 149 | repo: flags.string({ 150 | char: 'r', 151 | description: 'Limits searches to a specific repository.', 152 | }), 153 | org: flags.string({ 154 | description: 'Limits searches to a specific org.', 155 | }), 156 | project: flags.string({ 157 | description: 158 | 'Limits searches to a specific project board in a repository or organization.', 159 | }), 160 | archived: flags.boolean({ 161 | allowNo: true, 162 | description: 163 | 'Filters issues or pull requests based on whether they are in an archived repository.', 164 | }), 165 | sort: flags.enum({ 166 | char: 's', 167 | description: 168 | 'The sort field. Default: results are sorted by best match.', 169 | options: ['comments', 'created', 'updated'], 170 | }), 171 | }, 172 | ['sort'], 173 | ); 174 | 175 | static args = [...Command.args]; 176 | 177 | format(data: ApiResponse): TableResult { 178 | const rows = data.items.reduce((acc, item) => { 179 | const [, ticketMatch, labelMatch] = item.title.match(jiraRegex); 180 | const ticket = chalk.bold.yellow(sanitizeTicket(ticketMatch || '')); 181 | const label = chalk.bold(labelMatch || ''); 182 | const url = item.html_url; 183 | acc.push({ title: `${ticket} ${label}`.trim(), url }); 184 | return acc; 185 | }, []); 186 | 187 | return { 188 | rows, 189 | columns: { 190 | title: {}, 191 | url: {}, 192 | }, 193 | }; 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /test/__fixtures__/commands_notifications_default.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "590705216", 4 | "unread": true, 5 | "reason": "mention", 6 | "updated_at": "2019-09-24T20:15:19Z", 7 | "last_read_at": "2019-09-24T20:17:28Z", 8 | "subject": { 9 | "title": "[@pollyjs/core] Improve on previous types and update to match v2.6.2", 10 | "url": "https://api.github.com/repos/DefinitelyTyped/DefinitelyTyped/pulls/38554", 11 | "latest_comment_url": "https://api.github.com/repos/DefinitelyTyped/DefinitelyTyped/issues/comments/534729067", 12 | "type": "PullRequest" 13 | }, 14 | "repository": { 15 | "id": 6093316, 16 | "node_id": "MDEwOlJlcG9zaXRvcnk2MDkzMzE2", 17 | "name": "DefinitelyTyped", 18 | "full_name": "DefinitelyTyped/DefinitelyTyped", 19 | "private": false, 20 | "owner": { 21 | "login": "DefinitelyTyped", 22 | "id": 3637556, 23 | "node_id": "MDEyOk9yZ2FuaXphdGlvbjM2Mzc1NTY=", 24 | "avatar_url": "https://avatars1.githubusercontent.com/u/3637556?v=4", 25 | "gravatar_id": "", 26 | "url": "https://api.github.com/users/DefinitelyTyped", 27 | "html_url": "https://github.com/DefinitelyTyped", 28 | "followers_url": "https://api.github.com/users/DefinitelyTyped/followers", 29 | "following_url": "https://api.github.com/users/DefinitelyTyped/following{/other_user}", 30 | "gists_url": "https://api.github.com/users/DefinitelyTyped/gists{/gist_id}", 31 | "starred_url": "https://api.github.com/users/DefinitelyTyped/starred{/owner}{/repo}", 32 | "subscriptions_url": "https://api.github.com/users/DefinitelyTyped/subscriptions", 33 | "organizations_url": "https://api.github.com/users/DefinitelyTyped/orgs", 34 | "repos_url": "https://api.github.com/users/DefinitelyTyped/repos", 35 | "events_url": "https://api.github.com/users/DefinitelyTyped/events{/privacy}", 36 | "received_events_url": "https://api.github.com/users/DefinitelyTyped/received_events", 37 | "type": "Organization", 38 | "site_admin": false 39 | }, 40 | "html_url": "https://github.com/DefinitelyTyped/DefinitelyTyped", 41 | "description": "The repository for high quality TypeScript type definitions.", 42 | "fork": false, 43 | "url": "https://api.github.com/repos/DefinitelyTyped/DefinitelyTyped", 44 | "forks_url": "https://api.github.com/repos/DefinitelyTyped/DefinitelyTyped/forks", 45 | "keys_url": "https://api.github.com/repos/DefinitelyTyped/DefinitelyTyped/keys{/key_id}", 46 | "collaborators_url": "https://api.github.com/repos/DefinitelyTyped/DefinitelyTyped/collaborators{/collaborator}", 47 | "teams_url": "https://api.github.com/repos/DefinitelyTyped/DefinitelyTyped/teams", 48 | "hooks_url": "https://api.github.com/repos/DefinitelyTyped/DefinitelyTyped/hooks", 49 | "issue_events_url": "https://api.github.com/repos/DefinitelyTyped/DefinitelyTyped/issues/events{/number}", 50 | "events_url": "https://api.github.com/repos/DefinitelyTyped/DefinitelyTyped/events", 51 | "assignees_url": "https://api.github.com/repos/DefinitelyTyped/DefinitelyTyped/assignees{/user}", 52 | "branches_url": "https://api.github.com/repos/DefinitelyTyped/DefinitelyTyped/branches{/branch}", 53 | "tags_url": "https://api.github.com/repos/DefinitelyTyped/DefinitelyTyped/tags", 54 | "blobs_url": "https://api.github.com/repos/DefinitelyTyped/DefinitelyTyped/git/blobs{/sha}", 55 | "git_tags_url": "https://api.github.com/repos/DefinitelyTyped/DefinitelyTyped/git/tags{/sha}", 56 | "git_refs_url": "https://api.github.com/repos/DefinitelyTyped/DefinitelyTyped/git/refs{/sha}", 57 | "trees_url": "https://api.github.com/repos/DefinitelyTyped/DefinitelyTyped/git/trees{/sha}", 58 | "statuses_url": "https://api.github.com/repos/DefinitelyTyped/DefinitelyTyped/statuses/{sha}", 59 | "languages_url": "https://api.github.com/repos/DefinitelyTyped/DefinitelyTyped/languages", 60 | "stargazers_url": "https://api.github.com/repos/DefinitelyTyped/DefinitelyTyped/stargazers", 61 | "contributors_url": "https://api.github.com/repos/DefinitelyTyped/DefinitelyTyped/contributors", 62 | "subscribers_url": "https://api.github.com/repos/DefinitelyTyped/DefinitelyTyped/subscribers", 63 | "subscription_url": "https://api.github.com/repos/DefinitelyTyped/DefinitelyTyped/subscription", 64 | "commits_url": "https://api.github.com/repos/DefinitelyTyped/DefinitelyTyped/commits{/sha}", 65 | "git_commits_url": "https://api.github.com/repos/DefinitelyTyped/DefinitelyTyped/git/commits{/sha}", 66 | "comments_url": "https://api.github.com/repos/DefinitelyTyped/DefinitelyTyped/comments{/number}", 67 | "issue_comment_url": "https://api.github.com/repos/DefinitelyTyped/DefinitelyTyped/issues/comments{/number}", 68 | "contents_url": "https://api.github.com/repos/DefinitelyTyped/DefinitelyTyped/contents/{+path}", 69 | "compare_url": "https://api.github.com/repos/DefinitelyTyped/DefinitelyTyped/compare/{base}...{head}", 70 | "merges_url": "https://api.github.com/repos/DefinitelyTyped/DefinitelyTyped/merges", 71 | "archive_url": "https://api.github.com/repos/DefinitelyTyped/DefinitelyTyped/{archive_format}{/ref}", 72 | "downloads_url": "https://api.github.com/repos/DefinitelyTyped/DefinitelyTyped/downloads", 73 | "issues_url": "https://api.github.com/repos/DefinitelyTyped/DefinitelyTyped/issues{/number}", 74 | "pulls_url": "https://api.github.com/repos/DefinitelyTyped/DefinitelyTyped/pulls{/number}", 75 | "milestones_url": "https://api.github.com/repos/DefinitelyTyped/DefinitelyTyped/milestones{/number}", 76 | "notifications_url": "https://api.github.com/repos/DefinitelyTyped/DefinitelyTyped/notifications{?since,all,participating}", 77 | "labels_url": "https://api.github.com/repos/DefinitelyTyped/DefinitelyTyped/labels{/name}", 78 | "releases_url": "https://api.github.com/repos/DefinitelyTyped/DefinitelyTyped/releases{/id}", 79 | "deployments_url": "https://api.github.com/repos/DefinitelyTyped/DefinitelyTyped/deployments" 80 | }, 81 | "url": "https://api.github.com/notifications/threads/590705216", 82 | "subscription_url": "https://api.github.com/notifications/threads/590705216/subscription" 83 | }, 84 | { 85 | "id": "590176567", 86 | "unread": true, 87 | "reason": "subscribed", 88 | "updated_at": "2019-09-23T17:02:25Z", 89 | "last_read_at": "2019-09-23T17:43:08Z", 90 | "subject": { 91 | "title": "v2.3.1", 92 | "url": "https://api.github.com/repos/typescript-eslint/typescript-eslint/releases/20186922", 93 | "latest_comment_url": "https://api.github.com/repos/typescript-eslint/typescript-eslint/releases/20186922", 94 | "type": "Release" 95 | }, 96 | "repository": { 97 | "id": 165536154, 98 | "node_id": "MDEwOlJlcG9zaXRvcnkxNjU1MzYxNTQ=", 99 | "name": "typescript-eslint", 100 | "full_name": "typescript-eslint/typescript-eslint", 101 | "private": false, 102 | "owner": { 103 | "login": "typescript-eslint", 104 | "id": 46634674, 105 | "node_id": "MDEyOk9yZ2FuaXphdGlvbjQ2NjM0Njc0", 106 | "avatar_url": "https://avatars2.githubusercontent.com/u/46634674?v=4", 107 | "gravatar_id": "", 108 | "url": "https://api.github.com/users/typescript-eslint", 109 | "html_url": "https://github.com/typescript-eslint", 110 | "followers_url": "https://api.github.com/users/typescript-eslint/followers", 111 | "following_url": "https://api.github.com/users/typescript-eslint/following{/other_user}", 112 | "gists_url": "https://api.github.com/users/typescript-eslint/gists{/gist_id}", 113 | "starred_url": "https://api.github.com/users/typescript-eslint/starred{/owner}{/repo}", 114 | "subscriptions_url": "https://api.github.com/users/typescript-eslint/subscriptions", 115 | "organizations_url": "https://api.github.com/users/typescript-eslint/orgs", 116 | "repos_url": "https://api.github.com/users/typescript-eslint/repos", 117 | "events_url": "https://api.github.com/users/typescript-eslint/events{/privacy}", 118 | "received_events_url": "https://api.github.com/users/typescript-eslint/received_events", 119 | "type": "Organization", 120 | "site_admin": false 121 | }, 122 | "html_url": "https://github.com/typescript-eslint/typescript-eslint", 123 | "description": ":sparkles: Monorepo for all the tooling which enables ESLint to support TypeScript", 124 | "fork": false, 125 | "url": "https://api.github.com/repos/typescript-eslint/typescript-eslint", 126 | "forks_url": "https://api.github.com/repos/typescript-eslint/typescript-eslint/forks", 127 | "keys_url": "https://api.github.com/repos/typescript-eslint/typescript-eslint/keys{/key_id}", 128 | "collaborators_url": "https://api.github.com/repos/typescript-eslint/typescript-eslint/collaborators{/collaborator}", 129 | "teams_url": "https://api.github.com/repos/typescript-eslint/typescript-eslint/teams", 130 | "hooks_url": "https://api.github.com/repos/typescript-eslint/typescript-eslint/hooks", 131 | "issue_events_url": "https://api.github.com/repos/typescript-eslint/typescript-eslint/issues/events{/number}", 132 | "events_url": "https://api.github.com/repos/typescript-eslint/typescript-eslint/events", 133 | "assignees_url": "https://api.github.com/repos/typescript-eslint/typescript-eslint/assignees{/user}", 134 | "branches_url": "https://api.github.com/repos/typescript-eslint/typescript-eslint/branches{/branch}", 135 | "tags_url": "https://api.github.com/repos/typescript-eslint/typescript-eslint/tags", 136 | "blobs_url": "https://api.github.com/repos/typescript-eslint/typescript-eslint/git/blobs{/sha}", 137 | "git_tags_url": "https://api.github.com/repos/typescript-eslint/typescript-eslint/git/tags{/sha}", 138 | "git_refs_url": "https://api.github.com/repos/typescript-eslint/typescript-eslint/git/refs{/sha}", 139 | "trees_url": "https://api.github.com/repos/typescript-eslint/typescript-eslint/git/trees{/sha}", 140 | "statuses_url": "https://api.github.com/repos/typescript-eslint/typescript-eslint/statuses/{sha}", 141 | "languages_url": "https://api.github.com/repos/typescript-eslint/typescript-eslint/languages", 142 | "stargazers_url": "https://api.github.com/repos/typescript-eslint/typescript-eslint/stargazers", 143 | "contributors_url": "https://api.github.com/repos/typescript-eslint/typescript-eslint/contributors", 144 | "subscribers_url": "https://api.github.com/repos/typescript-eslint/typescript-eslint/subscribers", 145 | "subscription_url": "https://api.github.com/repos/typescript-eslint/typescript-eslint/subscription", 146 | "commits_url": "https://api.github.com/repos/typescript-eslint/typescript-eslint/commits{/sha}", 147 | "git_commits_url": "https://api.github.com/repos/typescript-eslint/typescript-eslint/git/commits{/sha}", 148 | "comments_url": "https://api.github.com/repos/typescript-eslint/typescript-eslint/comments{/number}", 149 | "issue_comment_url": "https://api.github.com/repos/typescript-eslint/typescript-eslint/issues/comments{/number}", 150 | "contents_url": "https://api.github.com/repos/typescript-eslint/typescript-eslint/contents/{+path}", 151 | "compare_url": "https://api.github.com/repos/typescript-eslint/typescript-eslint/compare/{base}...{head}", 152 | "merges_url": "https://api.github.com/repos/typescript-eslint/typescript-eslint/merges", 153 | "archive_url": "https://api.github.com/repos/typescript-eslint/typescript-eslint/{archive_format}{/ref}", 154 | "downloads_url": "https://api.github.com/repos/typescript-eslint/typescript-eslint/downloads", 155 | "issues_url": "https://api.github.com/repos/typescript-eslint/typescript-eslint/issues{/number}", 156 | "pulls_url": "https://api.github.com/repos/typescript-eslint/typescript-eslint/pulls{/number}", 157 | "milestones_url": "https://api.github.com/repos/typescript-eslint/typescript-eslint/milestones{/number}", 158 | "notifications_url": "https://api.github.com/repos/typescript-eslint/typescript-eslint/notifications{?since,all,participating}", 159 | "labels_url": "https://api.github.com/repos/typescript-eslint/typescript-eslint/labels{/name}", 160 | "releases_url": "https://api.github.com/repos/typescript-eslint/typescript-eslint/releases{/id}", 161 | "deployments_url": "https://api.github.com/repos/typescript-eslint/typescript-eslint/deployments" 162 | }, 163 | "url": "https://api.github.com/notifications/threads/590176567", 164 | "subscription_url": "https://api.github.com/notifications/threads/590176567/subscription" 165 | } 166 | ] 167 | -------------------------------------------------------------------------------- /test/commands/commands.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from '@oclif/test'; 2 | import * as sinon from 'sinon'; 3 | 4 | import AuthFile from '../../src/auth-file'; 5 | import _opener from '../../src/opener'; 6 | import _paginator from '../../src/pagination'; 7 | import { random } from '../helpers/utils'; 8 | 9 | describe('ghs commands', () => { 10 | const sandbox = sinon.createSandbox(); 11 | beforeEach(() => { 12 | sandbox.stub(_paginator, 'next').resolves(); 13 | sandbox.stub(AuthFile.prototype, 'getConfig').resolves({ 14 | token: 'test_token', 15 | baseUrl: 'https://api.github.com', 16 | }); 17 | }); 18 | 19 | afterEach(() => { 20 | sandbox.restore(); 21 | }); 22 | 23 | describe('repositories', () => { 24 | const args = ['repo']; 25 | 26 | test 27 | .nock('https://api.github.com', api => 28 | api 29 | .get('/search/repositories') 30 | .query({ q: 'oclif' }) 31 | .reply( 32 | 200, 33 | require('../__fixtures__/commands_repositories_runs_oclif'), 34 | ), 35 | ) 36 | .stdout() 37 | .stderr() 38 | .command([...args, 'oclif']) 39 | .it('runs repo oclif', ctx => { 40 | expect(ctx.stdout).to.contain('oclif'); 41 | }); 42 | 43 | test 44 | .nock('https://api.github.com', api => 45 | api 46 | .get('/search/repositories') 47 | .query({ q: 'gh-search-cli user:feinoujc' }) 48 | .reply( 49 | 200, 50 | require('../__fixtures__/commands_repositories_runs_repo_user_feinoujc'), 51 | ), 52 | ) 53 | .stdout() 54 | .stdout() 55 | .command([...args, '--user', 'feinoujc', 'gh-search-cli']) 56 | .it('runs repo --user feinoujc', ctx => { 57 | expect(ctx.stdout).to.contain('feinoujc'); 58 | }); 59 | 60 | test 61 | .nock('https://api.github.com', api => 62 | api 63 | .get('/search/repositories') 64 | .query({ q: 'user:oclif' }) 65 | .reply( 66 | 200, 67 | require('../__fixtures__/commands_repositories_runs_repo_user_oclif_page1'), 68 | { 69 | Link: 70 | '; rel="next", ; rel="last"', 71 | }, 72 | ) 73 | .get('/search/repositories') 74 | .query({ q: 'user:oclif', page: 2 }) 75 | .reply( 76 | 200, 77 | require('../__fixtures__/commands_repositories_runs_repo_user_oclif_page2'), 78 | { 79 | Link: 80 | '; rel="prev", ; rel="first"', 81 | }, 82 | ), 83 | ) 84 | .stdout() 85 | .command([...args, '--user', 'oclif']) 86 | .it('runs repo --user oclif w/paging', ctx => { 87 | expect(ctx.stdout).to.contain('oclif/command'); 88 | expect(ctx.stdout).to.contain('oclif/oclif'); 89 | }); 90 | 91 | test 92 | .nock('https://api.github.com', api => 93 | api 94 | .get('/search/repositories') 95 | .query({ q: 'user:oclif' }) 96 | .reply( 97 | 200, 98 | require('../__fixtures__/commands_repositories_runs_repo_user_oclif_page1'), 99 | { 100 | 'X-Ratelimit-Limit': '30', 101 | 'X-Ratelimit-Remaining': '3', 102 | 'X-Ratelimit-Reset': String(new Date().valueOf() / 1000), 103 | Link: 104 | '; rel="next", ; rel="last"', 105 | }, 106 | ) 107 | .get('/search/repositories') 108 | .query({ q: 'user:oclif', page: 2 }) 109 | .reply( 110 | 200, 111 | require('../__fixtures__/commands_repositories_runs_repo_user_oclif_page2'), 112 | { 113 | Link: 114 | '; rel="prev", ; rel="first"', 115 | }, 116 | ), 117 | ) 118 | .stdout() 119 | .command([...args, '--user', 'oclif']) 120 | .it('runs repo --user oclif w/rate limiting', ctx => { 121 | expect(ctx.stdout).to.contain('oclif/command'); 122 | expect(ctx.stdout).to.contain('oclif/oclif'); 123 | }); 124 | 125 | describe('opener flag', () => { 126 | let opener: any; 127 | beforeEach(() => { 128 | opener = sandbox.stub(_opener, 'open').resolves(); 129 | }); 130 | 131 | test 132 | .nock('https://api.github.com', api => 133 | api 134 | .get('/search/repositories') 135 | .query({ q: 'gh-search-cli user:feinoujc' }) 136 | .reply( 137 | 200, 138 | require('../__fixtures__/commands_repositories_runs_repo_user_feinoujc'), 139 | ), 140 | ) 141 | .stdout() 142 | .command([...args, '--user', 'feinoujc', '-o', 'gh-search-cli']) 143 | .it('opens first result', ctx => { 144 | expect(ctx.stdout).to.be.equal(''); 145 | expect(opener.callCount).to.be.equal(1); 146 | const [[url]] = opener.args; 147 | expect(url).to.be.equal('https://github.com/feinoujc/gh-search-cli'); 148 | }); 149 | }); 150 | 151 | describe('json flag', () => { 152 | test 153 | .nock('https://api.github.com', api => 154 | api 155 | .get('/search/repositories') 156 | .query({ q: 'gh-search-cli user:feinoujc' }) 157 | .reply( 158 | 200, 159 | require('../__fixtures__/commands_repositories_runs_repo_user_feinoujc'), 160 | ), 161 | ) 162 | .stdout() 163 | .command([...args, '--user', 'feinoujc', '--json', 'gh-search-cli']) 164 | .it('writes json', ctx => { 165 | const parsed = JSON.parse(ctx.stdout) as Array; 166 | expect(parsed.length).to.be.equal(1); 167 | }); 168 | }); 169 | 170 | describe('errors', () => { 171 | const rand = random(); 172 | test 173 | .nock('https://api.github.com', api => 174 | api 175 | .get('/search/repositories') 176 | .query({ q: `gh-search-cli user:${rand}` }) 177 | .reply( 178 | 422, 179 | require('../__fixtures__/commands_repositories_errors_unknown_user'), 180 | ), 181 | ) 182 | .stderr() 183 | .command([...args, '--user', rand, 'gh-search-cli']) 184 | .it('writes out api errors', ctx => { 185 | expect(ctx.stderr).to.match(/resources do not exist/i); 186 | }); 187 | 188 | test 189 | .stderr() 190 | .command([ 191 | 'repositories', 192 | '--api-token', 193 | rand, 194 | '--api-base-url', 195 | `ftp://api.${rand}.com`, 196 | 'gh-search-cli', 197 | ]) 198 | .it('writes out http errors', ctx => { 199 | expect(ctx.stderr).to.match(/invalid protocol/i); 200 | }); 201 | 202 | test 203 | .stderr() 204 | .stdout() 205 | .command([...args]) 206 | .it('fails on empty command'); 207 | }); 208 | }); 209 | 210 | describe('issues', () => { 211 | const args = ['issues']; 212 | 213 | test 214 | .nock('https://api.github.com', api => 215 | api 216 | .get('/search/issues') 217 | .query({ q: 'ahejlsberg' }) 218 | .reply( 219 | 200, 220 | require('../__fixtures__/commands_issues_runs_ahejlsberg'), 221 | ), 222 | ) 223 | .stdout() 224 | .command([...args, 'ahejlsberg']) 225 | .it('runs issues ahejlsberg', ctx => { 226 | expect(ctx.stdout).to.contain( 227 | 'https://github.com/microsoft/TypeScript/issues', 228 | ); 229 | }); 230 | }); 231 | describe('commits', () => { 232 | const args = ['commits']; 233 | 234 | test 235 | .nock('https://api.github.com', api => 236 | api 237 | .get('/search/commits') 238 | .query({ q: 'parser repo:oclif/oclif' }) 239 | .reply( 240 | 200, 241 | require('../__fixtures__/commands_commits_runs_runs_oclif_repo_parser'), 242 | ), 243 | ) 244 | .stdout() 245 | .command([...args, '--repo', 'oclif/oclif', 'parser']) 246 | .it('runs commits --repo oclif/oclif parser', ctx => { 247 | expect(ctx.stdout).to.contain('oclif'); 248 | expect(ctx.stdout).to.contain('https://github.com/oclif/oclif'); 249 | }); 250 | 251 | test 252 | .nock('https://api.github.com', api => 253 | api 254 | .get('/search/commits') 255 | .query({ q: 'repo:oclif/oclif committer:jdxcode' }) 256 | .reply( 257 | 200, 258 | require('../__fixtures__/commands_commits_runs_runs_oclif_repo_parser'), 259 | ), 260 | ) 261 | .stdout() 262 | .command([...args, '--repo', 'oclif/oclif', '--committer', 'jdxcode']) 263 | .it('runs commits --repo oclif/oclif committer', ctx => { 264 | expect(ctx.stdout).to.contain('oclif'); 265 | expect(ctx.stdout).to.contain('https://github.com/oclif/oclif'); 266 | }); 267 | 268 | test 269 | .nock('https://api.github.com', api => 270 | api 271 | .get('/search/commits') 272 | .query({ q: 'repo:oclif/oclif -committer:jdxcode' }) 273 | .reply( 274 | 200, 275 | require('../__fixtures__/commands_commits_runs_runs_oclif_repo_parser'), 276 | ), 277 | ) 278 | .stdout() 279 | .command([...args, '--repo', 'oclif/oclif', '--not-committer', 'jdxcode']) 280 | .it('runs commits --repo oclif/oclif --not-committer', ctx => { 281 | expect(ctx.stdout).to.contain('oclif'); 282 | expect(ctx.stdout).to.contain('https://github.com/oclif/oclif'); 283 | }); 284 | }); 285 | 286 | describe('code', () => { 287 | const args = ['code']; 288 | 289 | test 290 | .nock('https://api.github.com', api => 291 | api 292 | .get('/search/code') 293 | .query({ q: 'parser repo:oclif/oclif' }) 294 | .reply( 295 | 200, 296 | require('../__fixtures__/commands_code_runs_runs_oclif_repo_parser'), 297 | ), 298 | ) 299 | .stdout() 300 | .command([...args, '--repo', 'oclif/oclif', 'parser']) 301 | .it('runs code --repo oclif/oclif parser', ctx => { 302 | expect(ctx.stdout).to.contain('oclif'); 303 | expect(ctx.stdout).to.contain('https://github.com/oclif/oclif'); 304 | }); 305 | 306 | test 307 | .nock('https://api.github.com', api => 308 | api 309 | .get('/search/code') 310 | .query({ q: 'parser repo:oclif/oclif' }) 311 | .reply( 312 | 200, 313 | require('../__fixtures__/commands_code_runs_runs_oclif_repo_parser_full_text'), 314 | ), 315 | ) 316 | .stdout() 317 | .command([...args, '--repo', 'oclif/oclif', 'parser', '--text']) 318 | .it('runs code --repo oclif/oclif parser --text', ctx => { 319 | expect(ctx.stdout).to.contain('oclif'); 320 | expect(ctx.stdout).to.contain('https://github.com/oclif/oclif'); 321 | expect(ctx.stdout).to.contain('parser'); 322 | }); 323 | 324 | test 325 | .nock('https://api.github.com', api => 326 | api 327 | .get('/search/code') 328 | .query({ q: 'parser org:oclif' }) 329 | .reply( 330 | 200, 331 | require('../__fixtures__/commands_code_runs_runs_oclif_repo_parser_full_text'), 332 | ), 333 | ) 334 | .stdout() 335 | .command([...args, '--org', 'oclif', 'parser', '--text']) 336 | .it('runs code --org oclif parser --text', ctx => { 337 | expect(ctx.stdout).to.contain('oclif'); 338 | expect(ctx.stdout).to.contain('https://github.com/oclif/oclif'); 339 | expect(ctx.stdout).to.contain('parser'); 340 | }); 341 | }); 342 | 343 | describe('notifications', () => { 344 | test 345 | .nock('https://api.github.com', api => 346 | api 347 | .get('/notifications') 348 | .query({ all: 'false', participating: 'false' }) 349 | .reply(200, require('../__fixtures__/commands_notifications_default')) 350 | .get( 351 | '/repos/DefinitelyTyped/DefinitelyTyped/issues/comments/534729067', 352 | ) 353 | .reply( 354 | 200, 355 | require('../__fixtures__/commands_notifications_issue_comment'), 356 | ) 357 | .get('/repos/typescript-eslint/typescript-eslint/releases/20186922') 358 | .reply( 359 | 200, 360 | require('../__fixtures__/commands_notifications_release'), 361 | ), 362 | ) 363 | .stdout() 364 | .command(['notifications']) 365 | .it('fetches notifications', ctx => { 366 | expect(ctx.stdout).to.contain('pollyjs'); 367 | }); 368 | 369 | test 370 | .nock('https://api.github.com', api => 371 | api 372 | .get('/notifications') 373 | .query({ all: 'true', participating: 'false' }) 374 | .reply(200, require('../__fixtures__/commands_notifications_default')) 375 | .get( 376 | '/repos/DefinitelyTyped/DefinitelyTyped/issues/comments/534729067', 377 | ) 378 | .reply( 379 | 200, 380 | require('../__fixtures__/commands_notifications_issue_comment'), 381 | ) 382 | .get('/repos/typescript-eslint/typescript-eslint/releases/20186922') 383 | .reply( 384 | 200, 385 | require('../__fixtures__/commands_notifications_release'), 386 | ), 387 | ) 388 | .stdout() 389 | .command(['notifications', '--all']) 390 | .it('fetches notifications --all', ctx => { 391 | expect(ctx.stdout).to.contain('pollyjs'); 392 | }); 393 | 394 | test 395 | .nock('https://api.github.com', api => 396 | api 397 | .get('/repos/feinoujc/gh-search-cli/notifications') 398 | .query({ all: false, participating: false }) 399 | .reply(200, require('../__fixtures__/commands_notifications_default')) 400 | .get( 401 | '/repos/DefinitelyTyped/DefinitelyTyped/issues/comments/534729067', 402 | ) 403 | .reply( 404 | 200, 405 | require('../__fixtures__/commands_notifications_issue_comment'), 406 | ) 407 | .get('/repos/typescript-eslint/typescript-eslint/releases/20186922') 408 | .reply( 409 | 200, 410 | require('../__fixtures__/commands_notifications_release'), 411 | ), 412 | ) 413 | .stdout() 414 | .command([ 415 | 'notifications', 416 | '--owner', 417 | 'feinoujc', 418 | '--repo', 419 | 'gh-search-cli', 420 | ]) 421 | .it('fetches notifications --owner --repo', ctx => { 422 | expect(ctx.stdout).to.contain('pollyjs'); 423 | }); 424 | }); 425 | }); 426 | -------------------------------------------------------------------------------- /test/__fixtures__/commands_code_runs_runs_oclif_repo_parser.json: -------------------------------------------------------------------------------- 1 | { 2 | "total_count": 3, 3 | "incomplete_results": false, 4 | "items": [ 5 | { 6 | "name": "README.md", 7 | "path": "README.md", 8 | "sha": "e88a4edb6b1d90f651e4b831499851762fcb4ddb", 9 | "url": "https://api.github.com/repositories/117152902/contents/README.md?ref=78d57df0afbaceebdc8a3156bf38c4352b0eb189", 10 | "git_url": "https://api.github.com/repositories/117152902/git/blobs/e88a4edb6b1d90f651e4b831499851762fcb4ddb", 11 | "html_url": "https://github.com/oclif/oclif/blob/78d57df0afbaceebdc8a3156bf38c4352b0eb189/README.md", 12 | "repository": { 13 | "id": 117152902, 14 | "node_id": "MDEwOlJlcG9zaXRvcnkxMTcxNTI5MDI=", 15 | "name": "oclif", 16 | "full_name": "oclif/oclif", 17 | "private": false, 18 | "owner": { 19 | "login": "oclif", 20 | "id": 35349060, 21 | "node_id": "MDEyOk9yZ2FuaXphdGlvbjM1MzQ5MDYw", 22 | "avatar_url": "https://avatars3.githubusercontent.com/u/35349060?v=4", 23 | "gravatar_id": "", 24 | "url": "https://api.github.com/users/oclif", 25 | "html_url": "https://github.com/oclif", 26 | "followers_url": "https://api.github.com/users/oclif/followers", 27 | "following_url": "https://api.github.com/users/oclif/following{/other_user}", 28 | "gists_url": "https://api.github.com/users/oclif/gists{/gist_id}", 29 | "starred_url": "https://api.github.com/users/oclif/starred{/owner}{/repo}", 30 | "subscriptions_url": "https://api.github.com/users/oclif/subscriptions", 31 | "organizations_url": "https://api.github.com/users/oclif/orgs", 32 | "repos_url": "https://api.github.com/users/oclif/repos", 33 | "events_url": "https://api.github.com/users/oclif/events{/privacy}", 34 | "received_events_url": "https://api.github.com/users/oclif/received_events", 35 | "type": "Organization", 36 | "site_admin": false 37 | }, 38 | "html_url": "https://github.com/oclif/oclif", 39 | "description": "Node.js Open CLI Framework. Built with 💜 by Heroku.", 40 | "fork": false, 41 | "url": "https://api.github.com/repos/oclif/oclif", 42 | "forks_url": "https://api.github.com/repos/oclif/oclif/forks", 43 | "keys_url": "https://api.github.com/repos/oclif/oclif/keys{/key_id}", 44 | "collaborators_url": "https://api.github.com/repos/oclif/oclif/collaborators{/collaborator}", 45 | "teams_url": "https://api.github.com/repos/oclif/oclif/teams", 46 | "hooks_url": "https://api.github.com/repos/oclif/oclif/hooks", 47 | "issue_events_url": "https://api.github.com/repos/oclif/oclif/issues/events{/number}", 48 | "events_url": "https://api.github.com/repos/oclif/oclif/events", 49 | "assignees_url": "https://api.github.com/repos/oclif/oclif/assignees{/user}", 50 | "branches_url": "https://api.github.com/repos/oclif/oclif/branches{/branch}", 51 | "tags_url": "https://api.github.com/repos/oclif/oclif/tags", 52 | "blobs_url": "https://api.github.com/repos/oclif/oclif/git/blobs{/sha}", 53 | "git_tags_url": "https://api.github.com/repos/oclif/oclif/git/tags{/sha}", 54 | "git_refs_url": "https://api.github.com/repos/oclif/oclif/git/refs{/sha}", 55 | "trees_url": "https://api.github.com/repos/oclif/oclif/git/trees{/sha}", 56 | "statuses_url": "https://api.github.com/repos/oclif/oclif/statuses/{sha}", 57 | "languages_url": "https://api.github.com/repos/oclif/oclif/languages", 58 | "stargazers_url": "https://api.github.com/repos/oclif/oclif/stargazers", 59 | "contributors_url": "https://api.github.com/repos/oclif/oclif/contributors", 60 | "subscribers_url": "https://api.github.com/repos/oclif/oclif/subscribers", 61 | "subscription_url": "https://api.github.com/repos/oclif/oclif/subscription", 62 | "commits_url": "https://api.github.com/repos/oclif/oclif/commits{/sha}", 63 | "git_commits_url": "https://api.github.com/repos/oclif/oclif/git/commits{/sha}", 64 | "comments_url": "https://api.github.com/repos/oclif/oclif/comments{/number}", 65 | "issue_comment_url": "https://api.github.com/repos/oclif/oclif/issues/comments{/number}", 66 | "contents_url": "https://api.github.com/repos/oclif/oclif/contents/{+path}", 67 | "compare_url": "https://api.github.com/repos/oclif/oclif/compare/{base}...{head}", 68 | "merges_url": "https://api.github.com/repos/oclif/oclif/merges", 69 | "archive_url": "https://api.github.com/repos/oclif/oclif/{archive_format}{/ref}", 70 | "downloads_url": "https://api.github.com/repos/oclif/oclif/downloads", 71 | "issues_url": "https://api.github.com/repos/oclif/oclif/issues{/number}", 72 | "pulls_url": "https://api.github.com/repos/oclif/oclif/pulls{/number}", 73 | "milestones_url": "https://api.github.com/repos/oclif/oclif/milestones{/number}", 74 | "notifications_url": "https://api.github.com/repos/oclif/oclif/notifications{?since,all,participating}", 75 | "labels_url": "https://api.github.com/repos/oclif/oclif/labels{/name}", 76 | "releases_url": "https://api.github.com/repos/oclif/oclif/releases{/id}", 77 | "deployments_url": "https://api.github.com/repos/oclif/oclif/deployments" 78 | }, 79 | "score": 4.854224 80 | }, 81 | { 82 | "name": "yarn.lock", 83 | "path": "yarn.lock", 84 | "sha": "e5d3e6d493c91ca37e43a756c54247bdc5d4474c", 85 | "url": "https://api.github.com/repositories/117152902/contents/yarn.lock?ref=4f11822eea759f6c91c1cbef7fc19acbdc9f7df9", 86 | "git_url": "https://api.github.com/repositories/117152902/git/blobs/e5d3e6d493c91ca37e43a756c54247bdc5d4474c", 87 | "html_url": "https://github.com/oclif/oclif/blob/4f11822eea759f6c91c1cbef7fc19acbdc9f7df9/yarn.lock", 88 | "repository": { 89 | "id": 117152902, 90 | "node_id": "MDEwOlJlcG9zaXRvcnkxMTcxNTI5MDI=", 91 | "name": "oclif", 92 | "full_name": "oclif/oclif", 93 | "private": false, 94 | "owner": { 95 | "login": "oclif", 96 | "id": 35349060, 97 | "node_id": "MDEyOk9yZ2FuaXphdGlvbjM1MzQ5MDYw", 98 | "avatar_url": "https://avatars3.githubusercontent.com/u/35349060?v=4", 99 | "gravatar_id": "", 100 | "url": "https://api.github.com/users/oclif", 101 | "html_url": "https://github.com/oclif", 102 | "followers_url": "https://api.github.com/users/oclif/followers", 103 | "following_url": "https://api.github.com/users/oclif/following{/other_user}", 104 | "gists_url": "https://api.github.com/users/oclif/gists{/gist_id}", 105 | "starred_url": "https://api.github.com/users/oclif/starred{/owner}{/repo}", 106 | "subscriptions_url": "https://api.github.com/users/oclif/subscriptions", 107 | "organizations_url": "https://api.github.com/users/oclif/orgs", 108 | "repos_url": "https://api.github.com/users/oclif/repos", 109 | "events_url": "https://api.github.com/users/oclif/events{/privacy}", 110 | "received_events_url": "https://api.github.com/users/oclif/received_events", 111 | "type": "Organization", 112 | "site_admin": false 113 | }, 114 | "html_url": "https://github.com/oclif/oclif", 115 | "description": "Node.js Open CLI Framework. Built with 💜 by Heroku.", 116 | "fork": false, 117 | "url": "https://api.github.com/repos/oclif/oclif", 118 | "forks_url": "https://api.github.com/repos/oclif/oclif/forks", 119 | "keys_url": "https://api.github.com/repos/oclif/oclif/keys{/key_id}", 120 | "collaborators_url": "https://api.github.com/repos/oclif/oclif/collaborators{/collaborator}", 121 | "teams_url": "https://api.github.com/repos/oclif/oclif/teams", 122 | "hooks_url": "https://api.github.com/repos/oclif/oclif/hooks", 123 | "issue_events_url": "https://api.github.com/repos/oclif/oclif/issues/events{/number}", 124 | "events_url": "https://api.github.com/repos/oclif/oclif/events", 125 | "assignees_url": "https://api.github.com/repos/oclif/oclif/assignees{/user}", 126 | "branches_url": "https://api.github.com/repos/oclif/oclif/branches{/branch}", 127 | "tags_url": "https://api.github.com/repos/oclif/oclif/tags", 128 | "blobs_url": "https://api.github.com/repos/oclif/oclif/git/blobs{/sha}", 129 | "git_tags_url": "https://api.github.com/repos/oclif/oclif/git/tags{/sha}", 130 | "git_refs_url": "https://api.github.com/repos/oclif/oclif/git/refs{/sha}", 131 | "trees_url": "https://api.github.com/repos/oclif/oclif/git/trees{/sha}", 132 | "statuses_url": "https://api.github.com/repos/oclif/oclif/statuses/{sha}", 133 | "languages_url": "https://api.github.com/repos/oclif/oclif/languages", 134 | "stargazers_url": "https://api.github.com/repos/oclif/oclif/stargazers", 135 | "contributors_url": "https://api.github.com/repos/oclif/oclif/contributors", 136 | "subscribers_url": "https://api.github.com/repos/oclif/oclif/subscribers", 137 | "subscription_url": "https://api.github.com/repos/oclif/oclif/subscription", 138 | "commits_url": "https://api.github.com/repos/oclif/oclif/commits{/sha}", 139 | "git_commits_url": "https://api.github.com/repos/oclif/oclif/git/commits{/sha}", 140 | "comments_url": "https://api.github.com/repos/oclif/oclif/comments{/number}", 141 | "issue_comment_url": "https://api.github.com/repos/oclif/oclif/issues/comments{/number}", 142 | "contents_url": "https://api.github.com/repos/oclif/oclif/contents/{+path}", 143 | "compare_url": "https://api.github.com/repos/oclif/oclif/compare/{base}...{head}", 144 | "merges_url": "https://api.github.com/repos/oclif/oclif/merges", 145 | "archive_url": "https://api.github.com/repos/oclif/oclif/{archive_format}{/ref}", 146 | "downloads_url": "https://api.github.com/repos/oclif/oclif/downloads", 147 | "issues_url": "https://api.github.com/repos/oclif/oclif/issues{/number}", 148 | "pulls_url": "https://api.github.com/repos/oclif/oclif/pulls{/number}", 149 | "milestones_url": "https://api.github.com/repos/oclif/oclif/milestones{/number}", 150 | "notifications_url": "https://api.github.com/repos/oclif/oclif/notifications{?since,all,participating}", 151 | "labels_url": "https://api.github.com/repos/oclif/oclif/labels{/name}", 152 | "releases_url": "https://api.github.com/repos/oclif/oclif/releases{/id}", 153 | "deployments_url": "https://api.github.com/repos/oclif/oclif/deployments" 154 | }, 155 | "score": 2.9979632 156 | }, 157 | { 158 | "name": "CHANGELOG.md", 159 | "path": "CHANGELOG.md", 160 | "sha": "32aca1866d6a970a328658dd287808c8e6e3e70c", 161 | "url": "https://api.github.com/repositories/117152902/contents/CHANGELOG.md?ref=e841d827e825c7271fdf83222cfd544ff0f0c14e", 162 | "git_url": "https://api.github.com/repositories/117152902/git/blobs/32aca1866d6a970a328658dd287808c8e6e3e70c", 163 | "html_url": "https://github.com/oclif/oclif/blob/e841d827e825c7271fdf83222cfd544ff0f0c14e/CHANGELOG.md", 164 | "repository": { 165 | "id": 117152902, 166 | "node_id": "MDEwOlJlcG9zaXRvcnkxMTcxNTI5MDI=", 167 | "name": "oclif", 168 | "full_name": "oclif/oclif", 169 | "private": false, 170 | "owner": { 171 | "login": "oclif", 172 | "id": 35349060, 173 | "node_id": "MDEyOk9yZ2FuaXphdGlvbjM1MzQ5MDYw", 174 | "avatar_url": "https://avatars3.githubusercontent.com/u/35349060?v=4", 175 | "gravatar_id": "", 176 | "url": "https://api.github.com/users/oclif", 177 | "html_url": "https://github.com/oclif", 178 | "followers_url": "https://api.github.com/users/oclif/followers", 179 | "following_url": "https://api.github.com/users/oclif/following{/other_user}", 180 | "gists_url": "https://api.github.com/users/oclif/gists{/gist_id}", 181 | "starred_url": "https://api.github.com/users/oclif/starred{/owner}{/repo}", 182 | "subscriptions_url": "https://api.github.com/users/oclif/subscriptions", 183 | "organizations_url": "https://api.github.com/users/oclif/orgs", 184 | "repos_url": "https://api.github.com/users/oclif/repos", 185 | "events_url": "https://api.github.com/users/oclif/events{/privacy}", 186 | "received_events_url": "https://api.github.com/users/oclif/received_events", 187 | "type": "Organization", 188 | "site_admin": false 189 | }, 190 | "html_url": "https://github.com/oclif/oclif", 191 | "description": "Node.js Open CLI Framework. Built with 💜 by Heroku.", 192 | "fork": false, 193 | "url": "https://api.github.com/repos/oclif/oclif", 194 | "forks_url": "https://api.github.com/repos/oclif/oclif/forks", 195 | "keys_url": "https://api.github.com/repos/oclif/oclif/keys{/key_id}", 196 | "collaborators_url": "https://api.github.com/repos/oclif/oclif/collaborators{/collaborator}", 197 | "teams_url": "https://api.github.com/repos/oclif/oclif/teams", 198 | "hooks_url": "https://api.github.com/repos/oclif/oclif/hooks", 199 | "issue_events_url": "https://api.github.com/repos/oclif/oclif/issues/events{/number}", 200 | "events_url": "https://api.github.com/repos/oclif/oclif/events", 201 | "assignees_url": "https://api.github.com/repos/oclif/oclif/assignees{/user}", 202 | "branches_url": "https://api.github.com/repos/oclif/oclif/branches{/branch}", 203 | "tags_url": "https://api.github.com/repos/oclif/oclif/tags", 204 | "blobs_url": "https://api.github.com/repos/oclif/oclif/git/blobs{/sha}", 205 | "git_tags_url": "https://api.github.com/repos/oclif/oclif/git/tags{/sha}", 206 | "git_refs_url": "https://api.github.com/repos/oclif/oclif/git/refs{/sha}", 207 | "trees_url": "https://api.github.com/repos/oclif/oclif/git/trees{/sha}", 208 | "statuses_url": "https://api.github.com/repos/oclif/oclif/statuses/{sha}", 209 | "languages_url": "https://api.github.com/repos/oclif/oclif/languages", 210 | "stargazers_url": "https://api.github.com/repos/oclif/oclif/stargazers", 211 | "contributors_url": "https://api.github.com/repos/oclif/oclif/contributors", 212 | "subscribers_url": "https://api.github.com/repos/oclif/oclif/subscribers", 213 | "subscription_url": "https://api.github.com/repos/oclif/oclif/subscription", 214 | "commits_url": "https://api.github.com/repos/oclif/oclif/commits{/sha}", 215 | "git_commits_url": "https://api.github.com/repos/oclif/oclif/git/commits{/sha}", 216 | "comments_url": "https://api.github.com/repos/oclif/oclif/comments{/number}", 217 | "issue_comment_url": "https://api.github.com/repos/oclif/oclif/issues/comments{/number}", 218 | "contents_url": "https://api.github.com/repos/oclif/oclif/contents/{+path}", 219 | "compare_url": "https://api.github.com/repos/oclif/oclif/compare/{base}...{head}", 220 | "merges_url": "https://api.github.com/repos/oclif/oclif/merges", 221 | "archive_url": "https://api.github.com/repos/oclif/oclif/{archive_format}{/ref}", 222 | "downloads_url": "https://api.github.com/repos/oclif/oclif/downloads", 223 | "issues_url": "https://api.github.com/repos/oclif/oclif/issues{/number}", 224 | "pulls_url": "https://api.github.com/repos/oclif/oclif/pulls{/number}", 225 | "milestones_url": "https://api.github.com/repos/oclif/oclif/milestones{/number}", 226 | "notifications_url": "https://api.github.com/repos/oclif/oclif/notifications{?since,all,participating}", 227 | "labels_url": "https://api.github.com/repos/oclif/oclif/labels{/name}", 228 | "releases_url": "https://api.github.com/repos/oclif/oclif/releases{/id}", 229 | "deployments_url": "https://api.github.com/repos/oclif/oclif/deployments" 230 | }, 231 | "score": 2.2165987 232 | } 233 | ] 234 | } 235 | -------------------------------------------------------------------------------- /test/__fixtures__/commands_code_runs_runs_oclif_repo_parser_full_text.json: -------------------------------------------------------------------------------- 1 | { 2 | "total_count": 3, 3 | "incomplete_results": false, 4 | "items": [ 5 | { 6 | "name": "README.md", 7 | "path": "README.md", 8 | "sha": "e88a4edb6b1d90f651e4b831499851762fcb4ddb", 9 | "url": "https://api.github.com/repositories/117152902/contents/README.md?ref=78d57df0afbaceebdc8a3156bf38c4352b0eb189", 10 | "git_url": "https://api.github.com/repositories/117152902/git/blobs/e88a4edb6b1d90f651e4b831499851762fcb4ddb", 11 | "html_url": "https://github.com/oclif/oclif/blob/78d57df0afbaceebdc8a3156bf38c4352b0eb189/README.md", 12 | "repository": { 13 | "id": 117152902, 14 | "node_id": "MDEwOlJlcG9zaXRvcnkxMTcxNTI5MDI=", 15 | "name": "oclif", 16 | "full_name": "oclif/oclif", 17 | "private": false, 18 | "owner": { 19 | "login": "oclif", 20 | "id": 35349060, 21 | "node_id": "MDEyOk9yZ2FuaXphdGlvbjM1MzQ5MDYw", 22 | "avatar_url": "https://avatars3.githubusercontent.com/u/35349060?v=4", 23 | "gravatar_id": "", 24 | "url": "https://api.github.com/users/oclif", 25 | "html_url": "https://github.com/oclif", 26 | "followers_url": "https://api.github.com/users/oclif/followers", 27 | "following_url": "https://api.github.com/users/oclif/following{/other_user}", 28 | "gists_url": "https://api.github.com/users/oclif/gists{/gist_id}", 29 | "starred_url": "https://api.github.com/users/oclif/starred{/owner}{/repo}", 30 | "subscriptions_url": "https://api.github.com/users/oclif/subscriptions", 31 | "organizations_url": "https://api.github.com/users/oclif/orgs", 32 | "repos_url": "https://api.github.com/users/oclif/repos", 33 | "events_url": "https://api.github.com/users/oclif/events{/privacy}", 34 | "received_events_url": "https://api.github.com/users/oclif/received_events", 35 | "type": "Organization", 36 | "site_admin": false 37 | }, 38 | "html_url": "https://github.com/oclif/oclif", 39 | "description": "Node.js Open CLI Framework. Built with 💜 by Heroku.", 40 | "fork": false, 41 | "url": "https://api.github.com/repos/oclif/oclif", 42 | "forks_url": "https://api.github.com/repos/oclif/oclif/forks", 43 | "keys_url": "https://api.github.com/repos/oclif/oclif/keys{/key_id}", 44 | "collaborators_url": "https://api.github.com/repos/oclif/oclif/collaborators{/collaborator}", 45 | "teams_url": "https://api.github.com/repos/oclif/oclif/teams", 46 | "hooks_url": "https://api.github.com/repos/oclif/oclif/hooks", 47 | "issue_events_url": "https://api.github.com/repos/oclif/oclif/issues/events{/number}", 48 | "events_url": "https://api.github.com/repos/oclif/oclif/events", 49 | "assignees_url": "https://api.github.com/repos/oclif/oclif/assignees{/user}", 50 | "branches_url": "https://api.github.com/repos/oclif/oclif/branches{/branch}", 51 | "tags_url": "https://api.github.com/repos/oclif/oclif/tags", 52 | "blobs_url": "https://api.github.com/repos/oclif/oclif/git/blobs{/sha}", 53 | "git_tags_url": "https://api.github.com/repos/oclif/oclif/git/tags{/sha}", 54 | "git_refs_url": "https://api.github.com/repos/oclif/oclif/git/refs{/sha}", 55 | "trees_url": "https://api.github.com/repos/oclif/oclif/git/trees{/sha}", 56 | "statuses_url": "https://api.github.com/repos/oclif/oclif/statuses/{sha}", 57 | "languages_url": "https://api.github.com/repos/oclif/oclif/languages", 58 | "stargazers_url": "https://api.github.com/repos/oclif/oclif/stargazers", 59 | "contributors_url": "https://api.github.com/repos/oclif/oclif/contributors", 60 | "subscribers_url": "https://api.github.com/repos/oclif/oclif/subscribers", 61 | "subscription_url": "https://api.github.com/repos/oclif/oclif/subscription", 62 | "commits_url": "https://api.github.com/repos/oclif/oclif/commits{/sha}", 63 | "git_commits_url": "https://api.github.com/repos/oclif/oclif/git/commits{/sha}", 64 | "comments_url": "https://api.github.com/repos/oclif/oclif/comments{/number}", 65 | "issue_comment_url": "https://api.github.com/repos/oclif/oclif/issues/comments{/number}", 66 | "contents_url": "https://api.github.com/repos/oclif/oclif/contents/{+path}", 67 | "compare_url": "https://api.github.com/repos/oclif/oclif/compare/{base}...{head}", 68 | "merges_url": "https://api.github.com/repos/oclif/oclif/merges", 69 | "archive_url": "https://api.github.com/repos/oclif/oclif/{archive_format}{/ref}", 70 | "downloads_url": "https://api.github.com/repos/oclif/oclif/downloads", 71 | "issues_url": "https://api.github.com/repos/oclif/oclif/issues{/number}", 72 | "pulls_url": "https://api.github.com/repos/oclif/oclif/pulls{/number}", 73 | "milestones_url": "https://api.github.com/repos/oclif/oclif/milestones{/number}", 74 | "notifications_url": "https://api.github.com/repos/oclif/oclif/notifications{?since,all,participating}", 75 | "labels_url": "https://api.github.com/repos/oclif/oclif/labels{/name}", 76 | "releases_url": "https://api.github.com/repos/oclif/oclif/releases{/id}", 77 | "deployments_url": "https://api.github.com/repos/oclif/oclif/deployments" 78 | }, 79 | "score": 4.8564177, 80 | "text_matches": [ 81 | { 82 | "object_url": "https://api.github.com/repositories/117152902/contents//README.md?ref=78d57df0afbaceebdc8a3156bf38c4352b0eb189", 83 | "object_type": "FileContent", 84 | "property": "content", 85 | "fragment": " parser. We've built a custom one from years of experimentation that we feel consistently handles", 86 | "matches": [ 87 | { 88 | "text": "parser", 89 | "indices": [1, 7] 90 | } 91 | ] 92 | }, 93 | { 94 | "object_url": "https://api.github.com/repositories/117152902/contents//README.md?ref=78d57df0afbaceebdc8a3156bf38c4352b0eb189", 95 | "object_type": "FileContent", 96 | "property": "content", 97 | "fragment": " replaced inside oclif if needed—including the arg/flag parser.\n* **Autocomplete** - Automatically", 98 | "matches": [ 99 | { 100 | "text": "parser", 101 | "indices": [56, 62] 102 | } 103 | ] 104 | } 105 | ] 106 | }, 107 | { 108 | "name": "yarn.lock", 109 | "path": "yarn.lock", 110 | "sha": "e5d3e6d493c91ca37e43a756c54247bdc5d4474c", 111 | "url": "https://api.github.com/repositories/117152902/contents/yarn.lock?ref=4f11822eea759f6c91c1cbef7fc19acbdc9f7df9", 112 | "git_url": "https://api.github.com/repositories/117152902/git/blobs/e5d3e6d493c91ca37e43a756c54247bdc5d4474c", 113 | "html_url": "https://github.com/oclif/oclif/blob/4f11822eea759f6c91c1cbef7fc19acbdc9f7df9/yarn.lock", 114 | "repository": { 115 | "id": 117152902, 116 | "node_id": "MDEwOlJlcG9zaXRvcnkxMTcxNTI5MDI=", 117 | "name": "oclif", 118 | "full_name": "oclif/oclif", 119 | "private": false, 120 | "owner": { 121 | "login": "oclif", 122 | "id": 35349060, 123 | "node_id": "MDEyOk9yZ2FuaXphdGlvbjM1MzQ5MDYw", 124 | "avatar_url": "https://avatars3.githubusercontent.com/u/35349060?v=4", 125 | "gravatar_id": "", 126 | "url": "https://api.github.com/users/oclif", 127 | "html_url": "https://github.com/oclif", 128 | "followers_url": "https://api.github.com/users/oclif/followers", 129 | "following_url": "https://api.github.com/users/oclif/following{/other_user}", 130 | "gists_url": "https://api.github.com/users/oclif/gists{/gist_id}", 131 | "starred_url": "https://api.github.com/users/oclif/starred{/owner}{/repo}", 132 | "subscriptions_url": "https://api.github.com/users/oclif/subscriptions", 133 | "organizations_url": "https://api.github.com/users/oclif/orgs", 134 | "repos_url": "https://api.github.com/users/oclif/repos", 135 | "events_url": "https://api.github.com/users/oclif/events{/privacy}", 136 | "received_events_url": "https://api.github.com/users/oclif/received_events", 137 | "type": "Organization", 138 | "site_admin": false 139 | }, 140 | "html_url": "https://github.com/oclif/oclif", 141 | "description": "Node.js Open CLI Framework. Built with 💜 by Heroku.", 142 | "fork": false, 143 | "url": "https://api.github.com/repos/oclif/oclif", 144 | "forks_url": "https://api.github.com/repos/oclif/oclif/forks", 145 | "keys_url": "https://api.github.com/repos/oclif/oclif/keys{/key_id}", 146 | "collaborators_url": "https://api.github.com/repos/oclif/oclif/collaborators{/collaborator}", 147 | "teams_url": "https://api.github.com/repos/oclif/oclif/teams", 148 | "hooks_url": "https://api.github.com/repos/oclif/oclif/hooks", 149 | "issue_events_url": "https://api.github.com/repos/oclif/oclif/issues/events{/number}", 150 | "events_url": "https://api.github.com/repos/oclif/oclif/events", 151 | "assignees_url": "https://api.github.com/repos/oclif/oclif/assignees{/user}", 152 | "branches_url": "https://api.github.com/repos/oclif/oclif/branches{/branch}", 153 | "tags_url": "https://api.github.com/repos/oclif/oclif/tags", 154 | "blobs_url": "https://api.github.com/repos/oclif/oclif/git/blobs{/sha}", 155 | "git_tags_url": "https://api.github.com/repos/oclif/oclif/git/tags{/sha}", 156 | "git_refs_url": "https://api.github.com/repos/oclif/oclif/git/refs{/sha}", 157 | "trees_url": "https://api.github.com/repos/oclif/oclif/git/trees{/sha}", 158 | "statuses_url": "https://api.github.com/repos/oclif/oclif/statuses/{sha}", 159 | "languages_url": "https://api.github.com/repos/oclif/oclif/languages", 160 | "stargazers_url": "https://api.github.com/repos/oclif/oclif/stargazers", 161 | "contributors_url": "https://api.github.com/repos/oclif/oclif/contributors", 162 | "subscribers_url": "https://api.github.com/repos/oclif/oclif/subscribers", 163 | "subscription_url": "https://api.github.com/repos/oclif/oclif/subscription", 164 | "commits_url": "https://api.github.com/repos/oclif/oclif/commits{/sha}", 165 | "git_commits_url": "https://api.github.com/repos/oclif/oclif/git/commits{/sha}", 166 | "comments_url": "https://api.github.com/repos/oclif/oclif/comments{/number}", 167 | "issue_comment_url": "https://api.github.com/repos/oclif/oclif/issues/comments{/number}", 168 | "contents_url": "https://api.github.com/repos/oclif/oclif/contents/{+path}", 169 | "compare_url": "https://api.github.com/repos/oclif/oclif/compare/{base}...{head}", 170 | "merges_url": "https://api.github.com/repos/oclif/oclif/merges", 171 | "archive_url": "https://api.github.com/repos/oclif/oclif/{archive_format}{/ref}", 172 | "downloads_url": "https://api.github.com/repos/oclif/oclif/downloads", 173 | "issues_url": "https://api.github.com/repos/oclif/oclif/issues{/number}", 174 | "pulls_url": "https://api.github.com/repos/oclif/oclif/pulls{/number}", 175 | "milestones_url": "https://api.github.com/repos/oclif/oclif/milestones{/number}", 176 | "notifications_url": "https://api.github.com/repos/oclif/oclif/notifications{?since,all,participating}", 177 | "labels_url": "https://api.github.com/repos/oclif/oclif/labels{/name}", 178 | "releases_url": "https://api.github.com/repos/oclif/oclif/releases{/id}", 179 | "deployments_url": "https://api.github.com/repos/oclif/oclif/deployments" 180 | }, 181 | "score": 2.9993384, 182 | "text_matches": [ 183 | { 184 | "object_url": "https://api.github.com/repositories/117152902/contents//yarn.lock?ref=4f11822eea759f6c91c1cbef7fc19acbdc9f7df9", 185 | "object_type": "FileContent", 186 | "property": "content", 187 | "fragment": "/RP62Bg9BgmvLAdn1dJ8YuxxNrhFCv+DmTyukyCYGLle+eNQ==\n dependencies:\n \"@oclif/errors\" \"^1.2.2\"\n \"@oclif/parser", 188 | "matches": [ 189 | { 190 | "text": "parser", 191 | "indices": [108, 114] 192 | } 193 | ] 194 | }, 195 | { 196 | "object_url": "https://api.github.com/repositories/117152902/contents//yarn.lock?ref=4f11822eea759f6c91c1cbef7fc19acbdc9f7df9", 197 | "object_type": "FileContent", 198 | "property": "content", 199 | "fragment": "+MIGFPrWQO1KLMiQfoTpcLnUwloN4brrVUHw==\n\n\"@oclif/parser@^3.7.2\":\n version \"3.7.2\"\n resolved \"https://registry.yarnpkg", 200 | "matches": [ 201 | { 202 | "text": "parser", 203 | "indices": [48, 54] 204 | } 205 | ] 206 | } 207 | ] 208 | }, 209 | { 210 | "name": "CHANGELOG.md", 211 | "path": "CHANGELOG.md", 212 | "sha": "32aca1866d6a970a328658dd287808c8e6e3e70c", 213 | "url": "https://api.github.com/repositories/117152902/contents/CHANGELOG.md?ref=e841d827e825c7271fdf83222cfd544ff0f0c14e", 214 | "git_url": "https://api.github.com/repositories/117152902/git/blobs/32aca1866d6a970a328658dd287808c8e6e3e70c", 215 | "html_url": "https://github.com/oclif/oclif/blob/e841d827e825c7271fdf83222cfd544ff0f0c14e/CHANGELOG.md", 216 | "repository": { 217 | "id": 117152902, 218 | "node_id": "MDEwOlJlcG9zaXRvcnkxMTcxNTI5MDI=", 219 | "name": "oclif", 220 | "full_name": "oclif/oclif", 221 | "private": false, 222 | "owner": { 223 | "login": "oclif", 224 | "id": 35349060, 225 | "node_id": "MDEyOk9yZ2FuaXphdGlvbjM1MzQ5MDYw", 226 | "avatar_url": "https://avatars3.githubusercontent.com/u/35349060?v=4", 227 | "gravatar_id": "", 228 | "url": "https://api.github.com/users/oclif", 229 | "html_url": "https://github.com/oclif", 230 | "followers_url": "https://api.github.com/users/oclif/followers", 231 | "following_url": "https://api.github.com/users/oclif/following{/other_user}", 232 | "gists_url": "https://api.github.com/users/oclif/gists{/gist_id}", 233 | "starred_url": "https://api.github.com/users/oclif/starred{/owner}{/repo}", 234 | "subscriptions_url": "https://api.github.com/users/oclif/subscriptions", 235 | "organizations_url": "https://api.github.com/users/oclif/orgs", 236 | "repos_url": "https://api.github.com/users/oclif/repos", 237 | "events_url": "https://api.github.com/users/oclif/events{/privacy}", 238 | "received_events_url": "https://api.github.com/users/oclif/received_events", 239 | "type": "Organization", 240 | "site_admin": false 241 | }, 242 | "html_url": "https://github.com/oclif/oclif", 243 | "description": "Node.js Open CLI Framework. Built with 💜 by Heroku.", 244 | "fork": false, 245 | "url": "https://api.github.com/repos/oclif/oclif", 246 | "forks_url": "https://api.github.com/repos/oclif/oclif/forks", 247 | "keys_url": "https://api.github.com/repos/oclif/oclif/keys{/key_id}", 248 | "collaborators_url": "https://api.github.com/repos/oclif/oclif/collaborators{/collaborator}", 249 | "teams_url": "https://api.github.com/repos/oclif/oclif/teams", 250 | "hooks_url": "https://api.github.com/repos/oclif/oclif/hooks", 251 | "issue_events_url": "https://api.github.com/repos/oclif/oclif/issues/events{/number}", 252 | "events_url": "https://api.github.com/repos/oclif/oclif/events", 253 | "assignees_url": "https://api.github.com/repos/oclif/oclif/assignees{/user}", 254 | "branches_url": "https://api.github.com/repos/oclif/oclif/branches{/branch}", 255 | "tags_url": "https://api.github.com/repos/oclif/oclif/tags", 256 | "blobs_url": "https://api.github.com/repos/oclif/oclif/git/blobs{/sha}", 257 | "git_tags_url": "https://api.github.com/repos/oclif/oclif/git/tags{/sha}", 258 | "git_refs_url": "https://api.github.com/repos/oclif/oclif/git/refs{/sha}", 259 | "trees_url": "https://api.github.com/repos/oclif/oclif/git/trees{/sha}", 260 | "statuses_url": "https://api.github.com/repos/oclif/oclif/statuses/{sha}", 261 | "languages_url": "https://api.github.com/repos/oclif/oclif/languages", 262 | "stargazers_url": "https://api.github.com/repos/oclif/oclif/stargazers", 263 | "contributors_url": "https://api.github.com/repos/oclif/oclif/contributors", 264 | "subscribers_url": "https://api.github.com/repos/oclif/oclif/subscribers", 265 | "subscription_url": "https://api.github.com/repos/oclif/oclif/subscription", 266 | "commits_url": "https://api.github.com/repos/oclif/oclif/commits{/sha}", 267 | "git_commits_url": "https://api.github.com/repos/oclif/oclif/git/commits{/sha}", 268 | "comments_url": "https://api.github.com/repos/oclif/oclif/comments{/number}", 269 | "issue_comment_url": "https://api.github.com/repos/oclif/oclif/issues/comments{/number}", 270 | "contents_url": "https://api.github.com/repos/oclif/oclif/contents/{+path}", 271 | "compare_url": "https://api.github.com/repos/oclif/oclif/compare/{base}...{head}", 272 | "merges_url": "https://api.github.com/repos/oclif/oclif/merges", 273 | "archive_url": "https://api.github.com/repos/oclif/oclif/{archive_format}{/ref}", 274 | "downloads_url": "https://api.github.com/repos/oclif/oclif/downloads", 275 | "issues_url": "https://api.github.com/repos/oclif/oclif/issues{/number}", 276 | "pulls_url": "https://api.github.com/repos/oclif/oclif/pulls{/number}", 277 | "milestones_url": "https://api.github.com/repos/oclif/oclif/milestones{/number}", 278 | "notifications_url": "https://api.github.com/repos/oclif/oclif/notifications{?since,all,participating}", 279 | "labels_url": "https://api.github.com/repos/oclif/oclif/labels{/name}", 280 | "releases_url": "https://api.github.com/repos/oclif/oclif/releases{/id}", 281 | "deployments_url": "https://api.github.com/repos/oclif/oclif/deployments" 282 | }, 283 | "score": 2.21762, 284 | "text_matches": [ 285 | { 286 | "object_url": "https://api.github.com/repositories/117152902/contents//CHANGELOG.md?ref=e841d827e825c7271fdf83222cfd544ff0f0c14e", 287 | "object_type": "FileContent", 288 | "property": "content", 289 | "fragment": " scripts ([fc9bba9](https://github.com/oclif/cli/commit/fc9bba9))\n* parser is now exported from command", 290 | "matches": [ 291 | { 292 | "text": "parser", 293 | "indices": [68, 74] 294 | } 295 | ] 296 | }, 297 | { 298 | "object_url": "https://api.github.com/repositories/117152902/contents//CHANGELOG.md?ref=e841d827e825c7271fdf83222cfd544ff0f0c14e", 299 | "object_type": "FileContent", 300 | "property": "content", 301 | "fragment": " parser model ([7e7f7d3](https://github.com/oclif/cli/commit/7e7f7d3))\n\n\n## [0.33", 302 | "matches": [ 303 | { 304 | "text": "parser", 305 | "indices": [1, 7] 306 | } 307 | ] 308 | } 309 | ] 310 | } 311 | ] 312 | } 313 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NO LONGER MAINTAINED 2 | 3 | The [GitHub CLI](https://cli.github.com/) has added functionality similar to this library (`gh search issues, ...`), so just use that instead. 4 | 5 | gh-search-cli 6 | ============= 7 | Provides a cli for searching github.com. Supports repositories, code, issues and commits. Can be configured for github enterprise instances as well. Built using [oclif](https://github.com/oclif/oclif) 8 | 9 | [![Version](https://img.shields.io/npm/v/gh-search-cli.svg)](https://npmjs.org/package/gh-search-cli) 10 | [![Codecov](https://codecov.io/gh/feinoujc/gh-search-cli/branch/master/graph/badge.svg)](https://codecov.io/gh/feinoujc/gh-search-cli) 11 | [![License](https://img.shields.io/npm/l/gh-search-cli.svg)](https://github.com/feinoujc/gh-search-cli/blob/master/package.json) 12 | [![Dependabot Status](https://api.dependabot.com/badges/status?host=github&repo=feinoujc/gh-search-cli)](https://dependabot.com) 13 | 14 | 15 | * [Setup](#setup) 16 | * [Usage](#usage) 17 | * [Commands](#commands) 18 | 19 | 20 | # Setup 21 | 22 | The cli requires a personal access token (`notifications` scope is needed for the notifications command). The cli will create a new token on the first run and store it for future use. If you prefer you can use your own token and config the cli yourself (see [ghs config](#ghs-config)) 23 | 24 | _See code: [src/hooks/init/auth.ts](https://github.com/feinoujc/gh-search-cli/blob/v2.1.0/src/hooks/init/auth.ts)_ 25 | 26 | 27 | # Usage 28 | 29 | ```sh-session 30 | $ npm install -g gh-search-cli 31 | $ ghs COMMAND 32 | running command... 33 | $ ghs (-v|--version|version) 34 | gh-search-cli/3.1.0 darwin-x64 node-v12.19.0 35 | $ ghs --help [COMMAND] 36 | USAGE 37 | $ ghs COMMAND 38 | ... 39 | ``` 40 | 41 | # Commands 42 | 43 | * [`ghs code [QUERY]`](#ghs-code-query) 44 | * [`ghs commits [QUERY]`](#ghs-commits-query) 45 | * [`ghs config`](#ghs-config) 46 | * [`ghs help [COMMAND]`](#ghs-help-command) 47 | * [`ghs issues [QUERY]`](#ghs-issues-query) 48 | * [`ghs notifications`](#ghs-notifications) 49 | * [`ghs repositories [QUERY]`](#ghs-repositories-query) 50 | 51 | ## `ghs code [QUERY]` 52 | 53 | search github code. https://developer.github.com/v3/search/#search-code 54 | 55 | ``` 56 | USAGE 57 | $ ghs code [QUERY] 58 | 59 | OPTIONS 60 | -j, --json Return json. Can be piped to jq. 61 | -l, --language=language Searches code based on the language it's written in. 62 | -o, --open Open the first result in your browser. 63 | -o, --org=org Limits searchs to a specific organization 64 | -r, --repo=repo Limits searches to a specific repository. 65 | 66 | -s, --sort=(indexed) The sort field. Can only be indexed, which indicates how recently a file has been indexed 67 | by the GitHub search infrastructure. Default: results are sorted by best match. 68 | 69 | -t, --text Show full text match 70 | 71 | -u, --user=user Limits searches to a specific user. Use @me for your username 72 | 73 | --api-base-url=api-base-url The github api token. Defaults to configured GHE url or 'https://api.github.com' 74 | 75 | --api-token=api-token The github api token. Defaults to configured api token 76 | 77 | --extension=extension Matches files with a certain extension after a dot. 78 | 79 | --filename=filename Matches files by a substring of the filename. 80 | 81 | --in=in Qualifies which fields are searched. With this qualifier you can restrict the search to 82 | the file contents (file), the file path (path), or both. 83 | 84 | --order=(asc|desc) The sort order if sort parameter is provided. Default: desc 85 | 86 | --path=path Specifies the path prefix that the resulting file must be under. 87 | 88 | --size=size Finds files that match a certain size (in bytes). 89 | 90 | EXAMPLE 91 | $ ghs code --extension js "import _ from 'lodash'" 92 | ``` 93 | 94 | _See code: [src/commands/code.ts](https://github.com/feinoujc/gh-search-cli/blob/v3.1.0/src/commands/code.ts)_ 95 | 96 | ## `ghs commits [QUERY]` 97 | 98 | search github commits. https://developer.github.com/v3/search/#search-commits 99 | 100 | ``` 101 | USAGE 102 | $ ghs commits [QUERY] 103 | 104 | OPTIONS 105 | -j, --json Return json. Can be piped to jq. 106 | -o, --open Open the first result in your browser. 107 | 108 | -s, --sort=(author-date|committer-date) The sort field. Can be author-date or committer-date. Default: results are 109 | sorted by best match. 110 | 111 | --api-base-url=api-base-url The github api token. Defaults to configured GHE url or 112 | 'https://api.github.com' 113 | 114 | --api-token=api-token The github api token. Defaults to configured api token 115 | 116 | --author=author Matches commits authored by a user (based on email settings). 117 | 118 | --author-date=author-date Matches commits by author date range. 119 | 120 | --author-email=author-email Matches commits by author email. 121 | 122 | --author-name=author-name Matches commits by author name. 123 | 124 | --committer=committer Matches commits committed by a user (based on email settings). 125 | 126 | --committer-date=committer-date Matches commits by committer date range. 127 | 128 | --committer-email=committer-email Matches commits by committer email. 129 | 130 | --committer-name=committer-name Matches commits by committer name. 131 | 132 | --hash=hash Matches commits by hash. 133 | 134 | --is=(public|private) Matches public or private repositories. 135 | 136 | --[no-]merge --merge filters to merge commits, --no-merge filters out merge commits. 137 | 138 | --order=(asc|desc) The sort order if sort parameter is provided. Default: desc 139 | 140 | --org=org Limits searches to a specific organization. 141 | 142 | --parent=parent Matches commits that have a particular parent. 143 | 144 | --repo=repo Limits searches to a specific repository. 145 | 146 | --tree=tree Matches commits with the specified git tree hash. 147 | 148 | --user=user Limits searches to a specific user. Use @me for your username. 149 | 150 | EXAMPLE 151 | $ ghs commit --repo octocat/Spoon-Knife css 152 | ``` 153 | 154 | _See code: [src/commands/commits.ts](https://github.com/feinoujc/gh-search-cli/blob/v3.1.0/src/commands/commits.ts)_ 155 | 156 | ## `ghs config` 157 | 158 | Configure ghs settings 159 | 160 | ``` 161 | USAGE 162 | $ ghs config 163 | 164 | OPTIONS 165 | --base-url=base-url sets the github base url for github enterprise instances (ex: https://github.company.com/api/v3). 166 | --clear clears the local config file including the auth token. 167 | --token=token sets the github token to use. 168 | 169 | EXAMPLE 170 | $ ghs config --clear 171 | config cleared 172 | ``` 173 | 174 | _See code: [src/commands/config.ts](https://github.com/feinoujc/gh-search-cli/blob/v3.1.0/src/commands/config.ts)_ 175 | 176 | ## `ghs help [COMMAND]` 177 | 178 | display help for ghs 179 | 180 | ``` 181 | USAGE 182 | $ ghs help [COMMAND] 183 | 184 | ARGUMENTS 185 | COMMAND command to show help for 186 | 187 | OPTIONS 188 | --all see all commands in CLI 189 | ``` 190 | 191 | _See code: [@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/v3.2.0/src/commands/help.ts)_ 192 | 193 | ## `ghs issues [QUERY]` 194 | 195 | search github issues. https://developer.github.com/v3/search/#search-issues 196 | 197 | ``` 198 | USAGE 199 | $ ghs issues [QUERY] 200 | 201 | OPTIONS 202 | -c, --created=created Filters issues or pull requests based on date of creation,or when 203 | they were last updated. 204 | 205 | -j, --json Return json. Can be piped to jq. 206 | 207 | -l, --language=language Searches for issues or pull requests within repositories that 208 | match a certain language. 209 | 210 | -m, --merged=merged Filters pull requests based on the date when they were merged. 211 | 212 | -o, --open Open the first result in your browser. 213 | 214 | -r, --repo=repo Limits searches to a specific repository. 215 | 216 | -s, --sort=(comments|created|updated) The sort field. Default: results are sorted by best match. 217 | 218 | -s, --status=status Filters pull requests based on the commit status. 219 | 220 | -t, --type=(issue|pr) With this qualifier you can restrict the search to issues (issue) 221 | or pull request (pr) only. 222 | 223 | -u, --updated=updated Filters issues or pull requests based on date of creation, or 224 | when they were last updated. 225 | 226 | -u, --user=user Limits searches to a specific user. Use @me for your username. 227 | 228 | --SHA=SHA If you know the specific SHA hash of a commit, you can use it to 229 | search for pull requests that contain that SHA. The SHA syntax 230 | must be at least seven characters. 231 | 232 | --api-base-url=api-base-url The github api token. Defaults to configured GHE url or 233 | 'https://api.github.com' 234 | 235 | --api-token=api-token The github api token. Defaults to configured api token 236 | 237 | --[no-]archived Filters issues or pull requests based on whether they are in an 238 | archived repository. 239 | 240 | --assignee=assignee Finds issues or pull requeststhat are assigned to a certain user. 241 | Use @me for your username. 242 | 243 | --author=author Finds issues or pull requests created by a certain user. Use @me 244 | for your username. 245 | 246 | --base=base Filters pull requests based on the branch that they came from. 247 | 248 | --closed=closed Filters issues or pull requests based on the date when they were 249 | closed. 250 | 251 | --commenter=commenter Finds issues or pull requests that a certain user commented on. 252 | Use @me for your username. 253 | 254 | --comments=comments Filters issues or pull requests based on the quantity of 255 | comments. 256 | 257 | --head=head Filters pull requests based on the branch that they are 258 | modifying. 259 | 260 | --in=in Qualifies which fields are searched. With this qualifier you can 261 | restrict the searchto just the title (title), body (body), 262 | comments (comments), or any combination of these. 263 | 264 | --interactions=interactions You can filter issues and pull requests by the number of 265 | interactions with the interactions qualifier along with greater 266 | than, less than, and range qualifiers. The interactions count is 267 | the number of reactions and comments on an issue or pull request. 268 | 269 | --involves=involves Finds issues or pull requests that were either created by a 270 | certain user, assigned to that user, mention that user, or were 271 | commented on by that user. Use @me for your username. 272 | 273 | --is=is Searches for items within repositories that match a certain 274 | state, such as open, closed, or merged 275 | 276 | --label=label Filters issues or pull requests based on their labels. 277 | 278 | --mentions=mentions Finds issues or pull requests that mention a certain user. Use 279 | @me for your username. 280 | 281 | --milestone=milestone Finds issues or pull requests that are a part of a milestone 282 | within a repository. 283 | 284 | --no=no Filters items missing certain metadata, such as label, milestone, 285 | or assignee 286 | 287 | --order=(asc|desc) The sort order if sort parameter is provided. Default: desc 288 | 289 | --org=org Limits searches to a specific org. 290 | 291 | --project=project Limits searches to a specific project board in a repository or 292 | organization. 293 | 294 | --reactions=reactions You can filter issues and pull requests by the number of 295 | reactions using the reactions qualifier along with greater than, 296 | less than, and range qualifiers. 297 | 298 | --review=(none|required|approved|changes_requested) You can filter pull requests based on their review status 299 | 300 | --review-requested=review-requested Filter pull requests by requested reviewer. 301 | 302 | --reviewed-by=reviewed-by Filter pull requests by reviewer. 303 | 304 | --state=(open|closed) Filter issues or pull requests based on whether they're open or 305 | closed. 306 | 307 | --team=team For organizations you're a member of, finds issues or pull 308 | requests that @mention a team within the organization. 309 | 310 | --team-review-requested=team-review-requested Filter pull requests by requested reviewer. 311 | 312 | EXAMPLE 313 | $ ghs issues --is open --involves my-github-username 314 | ``` 315 | 316 | _See code: [src/commands/issues.ts](https://github.com/feinoujc/gh-search-cli/blob/v3.1.0/src/commands/issues.ts)_ 317 | 318 | ## `ghs notifications` 319 | 320 | List notifications 321 | 322 | ``` 323 | USAGE 324 | $ ghs notifications 325 | 326 | OPTIONS 327 | -a, --all If true, show notifications marked as read. Default: false 328 | 329 | -b, --before=before Only show notifications updated before the given time. This is a timestamp in ISO 8601 330 | format: YYYY-MM-DDTHH:MM:SSZ. 331 | 332 | -j, --json Return json. Can be piped to jq. 333 | 334 | -o, --open Open the first result in your browser. 335 | 336 | -p, --participating If true, only shows notifications in which the user is directly participating or 337 | mentioned. Default: false 338 | 339 | -s, --since=since Only show notifications updated after the given time. This is a timestamp in ISO 8601 340 | format: YYYY-MM-DDTHH:MM:SSZ 341 | 342 | --api-base-url=api-base-url The github api token. Defaults to configured GHE url or 'https://api.github.com' 343 | 344 | --api-token=api-token The github api token. Defaults to configured api token 345 | 346 | --owner=owner Filter notifications to a owner, required with --repo flag 347 | 348 | --repo=repo Filter notifications to a repository, required with --owner flag 349 | ``` 350 | 351 | _See code: [src/commands/notifications.ts](https://github.com/feinoujc/gh-search-cli/blob/v3.1.0/src/commands/notifications.ts)_ 352 | 353 | ## `ghs repositories [QUERY]` 354 | 355 | search github repositories. https://developer.github.com/v3/search/#search-repositories 356 | 357 | ``` 358 | USAGE 359 | $ ghs repositories [QUERY] 360 | 361 | OPTIONS 362 | -c, --created=created Filters repositories based on date of creation, or when they were last 363 | updated. 364 | 365 | -f, --[no-]fork Filters whether forked repositories should be included (--fork) or not 366 | (--no-fork). 367 | 368 | -j, --json Return json. Can be piped to jq. 369 | 370 | -l, --language=language Searches repositories based on the language they're written in. 371 | 372 | -o, --open Open the first result in your browser. 373 | 374 | -p, --pushed=pushed Filters repositories based on date of creation, or when they were last 375 | updated. 376 | 377 | -r, --repo=repo Limits searches to a specific repo. 378 | 379 | -s, --sort=(stars|forks|updated) The sort field. Default: results are sorted by best match. 380 | 381 | -u, --user=user Limits searches to a specific user. Use @me for your username. 382 | 383 | --api-base-url=api-base-url The github api token. Defaults to configured GHE url or 384 | 'https://api.github.com' 385 | 386 | --api-token=api-token The github api token. Defaults to configured api token 387 | 388 | --[no-]archived Filters whether archived repositories should be included (--archived) or not 389 | (--no-archived). 390 | 391 | --followers=followers Searches repositories based on the number of followers. 392 | 393 | --forks=forks Filters repositories based on the number of forks. 394 | 395 | --good-first-issues=good-first-issues Search for repositories that have a minimum number of issues labeled 396 | help-wanted. 397 | 398 | --help-wanted-issues=help-wanted-issues Search for repositories that have a minimum number of issues labeled 399 | good-first-issue. 400 | 401 | --in=in Qualifies which fields are searched. With this qualifier you can restrict the 402 | search to just the repository name, description, readme, or any combination 403 | of these. 404 | 405 | --license=license Filters repositories by license or license family, using the license keyword. 406 | 407 | --[no-]mirror Search repositories based on whether or not they're a mirror and are hosted 408 | elsewhere. 409 | 410 | --order=(asc|desc) The sort order if sort parameter is provided. Default: desc 411 | 412 | --size=size Finds repositories that match a certain size (in kilobytes). 413 | 414 | --stars=stars Searches repositories based on the number of stars. 415 | 416 | --topic=topic Filters repositories based on the specified topic. 417 | 418 | --topics=topics Search repositories by the number of topics that have been applied to them. 419 | 420 | ALIASES 421 | $ ghs repo 422 | $ ghs repository 423 | 424 | EXAMPLE 425 | $ ghs repo puppeteer 426 | GoogleChrome/puppeteer (https://github.com/GoogleChrome/puppeteer) 427 | ``` 428 | 429 | _See code: [src/commands/repositories.ts](https://github.com/feinoujc/gh-search-cli/blob/v3.1.0/src/commands/repositories.ts)_ 430 | 431 | -------------------------------------------------------------------------------- /test/__fixtures__/commands_commits_runs_runs_oclif_repo_parser.json: -------------------------------------------------------------------------------- 1 | { 2 | "total_count": 7, 3 | "incomplete_results": false, 4 | "items": [ 5 | { 6 | "url": "https://api.github.com/repos/oclif/oclif/commits/7e7f7d310b212c3105135d962ab232605a6122bc", 7 | "sha": "7e7f7d310b212c3105135d962ab232605a6122bc", 8 | "node_id": "MDY6Q29tbWl0MTE3MTUyOTAyOjdlN2Y3ZDMxMGIyMTJjMzEwNTEzNWQ5NjJhYjIzMjYwNWE2MTIyYmM=", 9 | "html_url": "https://github.com/oclif/oclif/commit/7e7f7d310b212c3105135d962ab232605a6122bc", 10 | "comments_url": "https://api.github.com/repos/oclif/oclif/commits/7e7f7d310b212c3105135d962ab232605a6122bc/comments", 11 | "commit": { 12 | "url": "https://api.github.com/repos/oclif/oclif/git/commits/7e7f7d310b212c3105135d962ab232605a6122bc", 13 | "author": { 14 | "date": "2018-01-31T21:04:58.000-08:00", 15 | "name": "Jeff Dickey", 16 | "email": "216188+jdxcode@users.noreply.github.com" 17 | }, 18 | "committer": { 19 | "date": "2018-01-31T21:04:58.000-08:00", 20 | "name": "Jeff Dickey", 21 | "email": "216188+jdxcode@users.noreply.github.com" 22 | }, 23 | "message": "feat: use new parser model", 24 | "tree": { 25 | "url": "https://api.github.com/repos/oclif/oclif/git/trees/b4edf191154234b14208995c02048ef1cfc08a71", 26 | "sha": "b4edf191154234b14208995c02048ef1cfc08a71" 27 | }, 28 | "comment_count": 0 29 | }, 30 | "author": { 31 | "login": "jdxcode", 32 | "id": 216188, 33 | "node_id": "MDQ6VXNlcjIxNjE4OA==", 34 | "avatar_url": "https://avatars2.githubusercontent.com/u/216188?v=4", 35 | "gravatar_id": "", 36 | "url": "https://api.github.com/users/jdxcode", 37 | "html_url": "https://github.com/jdxcode", 38 | "followers_url": "https://api.github.com/users/jdxcode/followers", 39 | "following_url": "https://api.github.com/users/jdxcode/following{/other_user}", 40 | "gists_url": "https://api.github.com/users/jdxcode/gists{/gist_id}", 41 | "starred_url": "https://api.github.com/users/jdxcode/starred{/owner}{/repo}", 42 | "subscriptions_url": "https://api.github.com/users/jdxcode/subscriptions", 43 | "organizations_url": "https://api.github.com/users/jdxcode/orgs", 44 | "repos_url": "https://api.github.com/users/jdxcode/repos", 45 | "events_url": "https://api.github.com/users/jdxcode/events{/privacy}", 46 | "received_events_url": "https://api.github.com/users/jdxcode/received_events", 47 | "type": "User", 48 | "site_admin": false 49 | }, 50 | "committer": { 51 | "login": "jdxcode", 52 | "id": 216188, 53 | "node_id": "MDQ6VXNlcjIxNjE4OA==", 54 | "avatar_url": "https://avatars2.githubusercontent.com/u/216188?v=4", 55 | "gravatar_id": "", 56 | "url": "https://api.github.com/users/jdxcode", 57 | "html_url": "https://github.com/jdxcode", 58 | "followers_url": "https://api.github.com/users/jdxcode/followers", 59 | "following_url": "https://api.github.com/users/jdxcode/following{/other_user}", 60 | "gists_url": "https://api.github.com/users/jdxcode/gists{/gist_id}", 61 | "starred_url": "https://api.github.com/users/jdxcode/starred{/owner}{/repo}", 62 | "subscriptions_url": "https://api.github.com/users/jdxcode/subscriptions", 63 | "organizations_url": "https://api.github.com/users/jdxcode/orgs", 64 | "repos_url": "https://api.github.com/users/jdxcode/repos", 65 | "events_url": "https://api.github.com/users/jdxcode/events{/privacy}", 66 | "received_events_url": "https://api.github.com/users/jdxcode/received_events", 67 | "type": "User", 68 | "site_admin": false 69 | }, 70 | "parents": [ 71 | { 72 | "url": "https://api.github.com/repos/oclif/oclif/commits/844ab18e55794c4b6ac36168e0648b1e8222044f", 73 | "html_url": "https://github.com/oclif/oclif/commit/844ab18e55794c4b6ac36168e0648b1e8222044f", 74 | "sha": "844ab18e55794c4b6ac36168e0648b1e8222044f" 75 | } 76 | ], 77 | "repository": { 78 | "id": 117152902, 79 | "node_id": "MDEwOlJlcG9zaXRvcnkxMTcxNTI5MDI=", 80 | "name": "oclif", 81 | "full_name": "oclif/oclif", 82 | "private": false, 83 | "owner": { 84 | "login": "oclif", 85 | "id": 35349060, 86 | "node_id": "MDEyOk9yZ2FuaXphdGlvbjM1MzQ5MDYw", 87 | "avatar_url": "https://avatars3.githubusercontent.com/u/35349060?v=4", 88 | "gravatar_id": "", 89 | "url": "https://api.github.com/users/oclif", 90 | "html_url": "https://github.com/oclif", 91 | "followers_url": "https://api.github.com/users/oclif/followers", 92 | "following_url": "https://api.github.com/users/oclif/following{/other_user}", 93 | "gists_url": "https://api.github.com/users/oclif/gists{/gist_id}", 94 | "starred_url": "https://api.github.com/users/oclif/starred{/owner}{/repo}", 95 | "subscriptions_url": "https://api.github.com/users/oclif/subscriptions", 96 | "organizations_url": "https://api.github.com/users/oclif/orgs", 97 | "repos_url": "https://api.github.com/users/oclif/repos", 98 | "events_url": "https://api.github.com/users/oclif/events{/privacy}", 99 | "received_events_url": "https://api.github.com/users/oclif/received_events", 100 | "type": "Organization", 101 | "site_admin": false 102 | }, 103 | "html_url": "https://github.com/oclif/oclif", 104 | "description": "Node.js Open CLI Framework. Built with 💜 by Heroku.", 105 | "fork": false, 106 | "url": "https://api.github.com/repos/oclif/oclif", 107 | "forks_url": "https://api.github.com/repos/oclif/oclif/forks", 108 | "keys_url": "https://api.github.com/repos/oclif/oclif/keys{/key_id}", 109 | "collaborators_url": "https://api.github.com/repos/oclif/oclif/collaborators{/collaborator}", 110 | "teams_url": "https://api.github.com/repos/oclif/oclif/teams", 111 | "hooks_url": "https://api.github.com/repos/oclif/oclif/hooks", 112 | "issue_events_url": "https://api.github.com/repos/oclif/oclif/issues/events{/number}", 113 | "events_url": "https://api.github.com/repos/oclif/oclif/events", 114 | "assignees_url": "https://api.github.com/repos/oclif/oclif/assignees{/user}", 115 | "branches_url": "https://api.github.com/repos/oclif/oclif/branches{/branch}", 116 | "tags_url": "https://api.github.com/repos/oclif/oclif/tags", 117 | "blobs_url": "https://api.github.com/repos/oclif/oclif/git/blobs{/sha}", 118 | "git_tags_url": "https://api.github.com/repos/oclif/oclif/git/tags{/sha}", 119 | "git_refs_url": "https://api.github.com/repos/oclif/oclif/git/refs{/sha}", 120 | "trees_url": "https://api.github.com/repos/oclif/oclif/git/trees{/sha}", 121 | "statuses_url": "https://api.github.com/repos/oclif/oclif/statuses/{sha}", 122 | "languages_url": "https://api.github.com/repos/oclif/oclif/languages", 123 | "stargazers_url": "https://api.github.com/repos/oclif/oclif/stargazers", 124 | "contributors_url": "https://api.github.com/repos/oclif/oclif/contributors", 125 | "subscribers_url": "https://api.github.com/repos/oclif/oclif/subscribers", 126 | "subscription_url": "https://api.github.com/repos/oclif/oclif/subscription", 127 | "commits_url": "https://api.github.com/repos/oclif/oclif/commits{/sha}", 128 | "git_commits_url": "https://api.github.com/repos/oclif/oclif/git/commits{/sha}", 129 | "comments_url": "https://api.github.com/repos/oclif/oclif/comments{/number}", 130 | "issue_comment_url": "https://api.github.com/repos/oclif/oclif/issues/comments{/number}", 131 | "contents_url": "https://api.github.com/repos/oclif/oclif/contents/{+path}", 132 | "compare_url": "https://api.github.com/repos/oclif/oclif/compare/{base}...{head}", 133 | "merges_url": "https://api.github.com/repos/oclif/oclif/merges", 134 | "archive_url": "https://api.github.com/repos/oclif/oclif/{archive_format}{/ref}", 135 | "downloads_url": "https://api.github.com/repos/oclif/oclif/downloads", 136 | "issues_url": "https://api.github.com/repos/oclif/oclif/issues{/number}", 137 | "pulls_url": "https://api.github.com/repos/oclif/oclif/pulls{/number}", 138 | "milestones_url": "https://api.github.com/repos/oclif/oclif/milestones{/number}", 139 | "notifications_url": "https://api.github.com/repos/oclif/oclif/notifications{?since,all,participating}", 140 | "labels_url": "https://api.github.com/repos/oclif/oclif/labels{/name}", 141 | "releases_url": "https://api.github.com/repos/oclif/oclif/releases{/id}", 142 | "deployments_url": "https://api.github.com/repos/oclif/oclif/deployments" 143 | }, 144 | "score": 15.216207 145 | }, 146 | { 147 | "url": "https://api.github.com/repos/oclif/oclif/commits/6f192b1cf7b373e1008bf9512cc0860ed1c8d953", 148 | "sha": "6f192b1cf7b373e1008bf9512cc0860ed1c8d953", 149 | "node_id": "MDY6Q29tbWl0MTE3MTUyOTAyOjZmMTkyYjFjZjdiMzczZTEwMDhiZjk1MTJjYzA4NjBlZDFjOGQ5NTM=", 150 | "html_url": "https://github.com/oclif/oclif/commit/6f192b1cf7b373e1008bf9512cc0860ed1c8d953", 151 | "comments_url": "https://api.github.com/repos/oclif/oclif/commits/6f192b1cf7b373e1008bf9512cc0860ed1c8d953/comments", 152 | "commit": { 153 | "url": "https://api.github.com/repos/oclif/oclif/git/commits/6f192b1cf7b373e1008bf9512cc0860ed1c8d953", 154 | "author": { 155 | "date": "2018-01-28T02:33:36.000-08:00", 156 | "name": "Jeff Dickey", 157 | "email": "216188+jdxcode@users.noreply.github.com" 158 | }, 159 | "committer": { 160 | "date": "2018-01-28T02:33:45.000-08:00", 161 | "name": "Jeff Dickey", 162 | "email": "216188+jdxcode@users.noreply.github.com" 163 | }, 164 | "message": "fix: parser is now exported from command", 165 | "tree": { 166 | "url": "https://api.github.com/repos/oclif/oclif/git/trees/58c0ce021b5218f6deb1c5031af2f536671ddd7a", 167 | "sha": "58c0ce021b5218f6deb1c5031af2f536671ddd7a" 168 | }, 169 | "comment_count": 0 170 | }, 171 | "author": { 172 | "login": "jdxcode", 173 | "id": 216188, 174 | "node_id": "MDQ6VXNlcjIxNjE4OA==", 175 | "avatar_url": "https://avatars2.githubusercontent.com/u/216188?v=4", 176 | "gravatar_id": "", 177 | "url": "https://api.github.com/users/jdxcode", 178 | "html_url": "https://github.com/jdxcode", 179 | "followers_url": "https://api.github.com/users/jdxcode/followers", 180 | "following_url": "https://api.github.com/users/jdxcode/following{/other_user}", 181 | "gists_url": "https://api.github.com/users/jdxcode/gists{/gist_id}", 182 | "starred_url": "https://api.github.com/users/jdxcode/starred{/owner}{/repo}", 183 | "subscriptions_url": "https://api.github.com/users/jdxcode/subscriptions", 184 | "organizations_url": "https://api.github.com/users/jdxcode/orgs", 185 | "repos_url": "https://api.github.com/users/jdxcode/repos", 186 | "events_url": "https://api.github.com/users/jdxcode/events{/privacy}", 187 | "received_events_url": "https://api.github.com/users/jdxcode/received_events", 188 | "type": "User", 189 | "site_admin": false 190 | }, 191 | "committer": { 192 | "login": "jdxcode", 193 | "id": 216188, 194 | "node_id": "MDQ6VXNlcjIxNjE4OA==", 195 | "avatar_url": "https://avatars2.githubusercontent.com/u/216188?v=4", 196 | "gravatar_id": "", 197 | "url": "https://api.github.com/users/jdxcode", 198 | "html_url": "https://github.com/jdxcode", 199 | "followers_url": "https://api.github.com/users/jdxcode/followers", 200 | "following_url": "https://api.github.com/users/jdxcode/following{/other_user}", 201 | "gists_url": "https://api.github.com/users/jdxcode/gists{/gist_id}", 202 | "starred_url": "https://api.github.com/users/jdxcode/starred{/owner}{/repo}", 203 | "subscriptions_url": "https://api.github.com/users/jdxcode/subscriptions", 204 | "organizations_url": "https://api.github.com/users/jdxcode/orgs", 205 | "repos_url": "https://api.github.com/users/jdxcode/repos", 206 | "events_url": "https://api.github.com/users/jdxcode/events{/privacy}", 207 | "received_events_url": "https://api.github.com/users/jdxcode/received_events", 208 | "type": "User", 209 | "site_admin": false 210 | }, 211 | "parents": [ 212 | { 213 | "url": "https://api.github.com/repos/oclif/oclif/commits/ef0904f3e4b848c58510d5377861dbb693410640", 214 | "html_url": "https://github.com/oclif/oclif/commit/ef0904f3e4b848c58510d5377861dbb693410640", 215 | "sha": "ef0904f3e4b848c58510d5377861dbb693410640" 216 | } 217 | ], 218 | "repository": { 219 | "id": 117152902, 220 | "node_id": "MDEwOlJlcG9zaXRvcnkxMTcxNTI5MDI=", 221 | "name": "oclif", 222 | "full_name": "oclif/oclif", 223 | "private": false, 224 | "owner": { 225 | "login": "oclif", 226 | "id": 35349060, 227 | "node_id": "MDEyOk9yZ2FuaXphdGlvbjM1MzQ5MDYw", 228 | "avatar_url": "https://avatars3.githubusercontent.com/u/35349060?v=4", 229 | "gravatar_id": "", 230 | "url": "https://api.github.com/users/oclif", 231 | "html_url": "https://github.com/oclif", 232 | "followers_url": "https://api.github.com/users/oclif/followers", 233 | "following_url": "https://api.github.com/users/oclif/following{/other_user}", 234 | "gists_url": "https://api.github.com/users/oclif/gists{/gist_id}", 235 | "starred_url": "https://api.github.com/users/oclif/starred{/owner}{/repo}", 236 | "subscriptions_url": "https://api.github.com/users/oclif/subscriptions", 237 | "organizations_url": "https://api.github.com/users/oclif/orgs", 238 | "repos_url": "https://api.github.com/users/oclif/repos", 239 | "events_url": "https://api.github.com/users/oclif/events{/privacy}", 240 | "received_events_url": "https://api.github.com/users/oclif/received_events", 241 | "type": "Organization", 242 | "site_admin": false 243 | }, 244 | "html_url": "https://github.com/oclif/oclif", 245 | "description": "Node.js Open CLI Framework. Built with 💜 by Heroku.", 246 | "fork": false, 247 | "url": "https://api.github.com/repos/oclif/oclif", 248 | "forks_url": "https://api.github.com/repos/oclif/oclif/forks", 249 | "keys_url": "https://api.github.com/repos/oclif/oclif/keys{/key_id}", 250 | "collaborators_url": "https://api.github.com/repos/oclif/oclif/collaborators{/collaborator}", 251 | "teams_url": "https://api.github.com/repos/oclif/oclif/teams", 252 | "hooks_url": "https://api.github.com/repos/oclif/oclif/hooks", 253 | "issue_events_url": "https://api.github.com/repos/oclif/oclif/issues/events{/number}", 254 | "events_url": "https://api.github.com/repos/oclif/oclif/events", 255 | "assignees_url": "https://api.github.com/repos/oclif/oclif/assignees{/user}", 256 | "branches_url": "https://api.github.com/repos/oclif/oclif/branches{/branch}", 257 | "tags_url": "https://api.github.com/repos/oclif/oclif/tags", 258 | "blobs_url": "https://api.github.com/repos/oclif/oclif/git/blobs{/sha}", 259 | "git_tags_url": "https://api.github.com/repos/oclif/oclif/git/tags{/sha}", 260 | "git_refs_url": "https://api.github.com/repos/oclif/oclif/git/refs{/sha}", 261 | "trees_url": "https://api.github.com/repos/oclif/oclif/git/trees{/sha}", 262 | "statuses_url": "https://api.github.com/repos/oclif/oclif/statuses/{sha}", 263 | "languages_url": "https://api.github.com/repos/oclif/oclif/languages", 264 | "stargazers_url": "https://api.github.com/repos/oclif/oclif/stargazers", 265 | "contributors_url": "https://api.github.com/repos/oclif/oclif/contributors", 266 | "subscribers_url": "https://api.github.com/repos/oclif/oclif/subscribers", 267 | "subscription_url": "https://api.github.com/repos/oclif/oclif/subscription", 268 | "commits_url": "https://api.github.com/repos/oclif/oclif/commits{/sha}", 269 | "git_commits_url": "https://api.github.com/repos/oclif/oclif/git/commits{/sha}", 270 | "comments_url": "https://api.github.com/repos/oclif/oclif/comments{/number}", 271 | "issue_comment_url": "https://api.github.com/repos/oclif/oclif/issues/comments{/number}", 272 | "contents_url": "https://api.github.com/repos/oclif/oclif/contents/{+path}", 273 | "compare_url": "https://api.github.com/repos/oclif/oclif/compare/{base}...{head}", 274 | "merges_url": "https://api.github.com/repos/oclif/oclif/merges", 275 | "archive_url": "https://api.github.com/repos/oclif/oclif/{archive_format}{/ref}", 276 | "downloads_url": "https://api.github.com/repos/oclif/oclif/downloads", 277 | "issues_url": "https://api.github.com/repos/oclif/oclif/issues{/number}", 278 | "pulls_url": "https://api.github.com/repos/oclif/oclif/pulls{/number}", 279 | "milestones_url": "https://api.github.com/repos/oclif/oclif/milestones{/number}", 280 | "notifications_url": "https://api.github.com/repos/oclif/oclif/notifications{?since,all,participating}", 281 | "labels_url": "https://api.github.com/repos/oclif/oclif/labels{/name}", 282 | "releases_url": "https://api.github.com/repos/oclif/oclif/releases{/id}", 283 | "deployments_url": "https://api.github.com/repos/oclif/oclif/deployments" 284 | }, 285 | "score": 15.066653 286 | }, 287 | { 288 | "url": "https://api.github.com/repos/oclif/oclif/commits/68c2023b5475bf1fa1fb99738b4c8a391f37f953", 289 | "sha": "68c2023b5475bf1fa1fb99738b4c8a391f37f953", 290 | "node_id": "MDY6Q29tbWl0MTE3MTUyOTAyOjY4YzIwMjNiNTQ3NWJmMWZhMWZiOTk3MzhiNGM4YTM5MWYzN2Y5NTM=", 291 | "html_url": "https://github.com/oclif/oclif/commit/68c2023b5475bf1fa1fb99738b4c8a391f37f953", 292 | "comments_url": "https://api.github.com/repos/oclif/oclif/commits/68c2023b5475bf1fa1fb99738b4c8a391f37f953/comments", 293 | "commit": { 294 | "url": "https://api.github.com/repos/oclif/oclif/git/commits/68c2023b5475bf1fa1fb99738b4c8a391f37f953", 295 | "author": { 296 | "date": "2018-01-28T13:44:35.000-08:00", 297 | "name": "Jeff Dickey", 298 | "email": "216188+jdxcode@users.noreply.github.com" 299 | }, 300 | "committer": { 301 | "date": "2018-01-28T13:44:35.000-08:00", 302 | "name": "Jeff Dickey", 303 | "email": "216188+jdxcode@users.noreply.github.com" 304 | }, 305 | "message": "fix: add TS parsing for commands", 306 | "tree": { 307 | "url": "https://api.github.com/repos/oclif/oclif/git/trees/f0e8150d3f2aa5c84c4b2282bcc71f99c06be7b9", 308 | "sha": "f0e8150d3f2aa5c84c4b2282bcc71f99c06be7b9" 309 | }, 310 | "comment_count": 0 311 | }, 312 | "author": { 313 | "login": "jdxcode", 314 | "id": 216188, 315 | "node_id": "MDQ6VXNlcjIxNjE4OA==", 316 | "avatar_url": "https://avatars2.githubusercontent.com/u/216188?v=4", 317 | "gravatar_id": "", 318 | "url": "https://api.github.com/users/jdxcode", 319 | "html_url": "https://github.com/jdxcode", 320 | "followers_url": "https://api.github.com/users/jdxcode/followers", 321 | "following_url": "https://api.github.com/users/jdxcode/following{/other_user}", 322 | "gists_url": "https://api.github.com/users/jdxcode/gists{/gist_id}", 323 | "starred_url": "https://api.github.com/users/jdxcode/starred{/owner}{/repo}", 324 | "subscriptions_url": "https://api.github.com/users/jdxcode/subscriptions", 325 | "organizations_url": "https://api.github.com/users/jdxcode/orgs", 326 | "repos_url": "https://api.github.com/users/jdxcode/repos", 327 | "events_url": "https://api.github.com/users/jdxcode/events{/privacy}", 328 | "received_events_url": "https://api.github.com/users/jdxcode/received_events", 329 | "type": "User", 330 | "site_admin": false 331 | }, 332 | "committer": { 333 | "login": "jdxcode", 334 | "id": 216188, 335 | "node_id": "MDQ6VXNlcjIxNjE4OA==", 336 | "avatar_url": "https://avatars2.githubusercontent.com/u/216188?v=4", 337 | "gravatar_id": "", 338 | "url": "https://api.github.com/users/jdxcode", 339 | "html_url": "https://github.com/jdxcode", 340 | "followers_url": "https://api.github.com/users/jdxcode/followers", 341 | "following_url": "https://api.github.com/users/jdxcode/following{/other_user}", 342 | "gists_url": "https://api.github.com/users/jdxcode/gists{/gist_id}", 343 | "starred_url": "https://api.github.com/users/jdxcode/starred{/owner}{/repo}", 344 | "subscriptions_url": "https://api.github.com/users/jdxcode/subscriptions", 345 | "organizations_url": "https://api.github.com/users/jdxcode/orgs", 346 | "repos_url": "https://api.github.com/users/jdxcode/repos", 347 | "events_url": "https://api.github.com/users/jdxcode/events{/privacy}", 348 | "received_events_url": "https://api.github.com/users/jdxcode/received_events", 349 | "type": "User", 350 | "site_admin": false 351 | }, 352 | "parents": [ 353 | { 354 | "url": "https://api.github.com/repos/oclif/oclif/commits/adf7aad16e7a6185c89b2c81185401e45a7351e0", 355 | "html_url": "https://github.com/oclif/oclif/commit/adf7aad16e7a6185c89b2c81185401e45a7351e0", 356 | "sha": "adf7aad16e7a6185c89b2c81185401e45a7351e0" 357 | } 358 | ], 359 | "repository": { 360 | "id": 117152902, 361 | "node_id": "MDEwOlJlcG9zaXRvcnkxMTcxNTI5MDI=", 362 | "name": "oclif", 363 | "full_name": "oclif/oclif", 364 | "private": false, 365 | "owner": { 366 | "login": "oclif", 367 | "id": 35349060, 368 | "node_id": "MDEyOk9yZ2FuaXphdGlvbjM1MzQ5MDYw", 369 | "avatar_url": "https://avatars3.githubusercontent.com/u/35349060?v=4", 370 | "gravatar_id": "", 371 | "url": "https://api.github.com/users/oclif", 372 | "html_url": "https://github.com/oclif", 373 | "followers_url": "https://api.github.com/users/oclif/followers", 374 | "following_url": "https://api.github.com/users/oclif/following{/other_user}", 375 | "gists_url": "https://api.github.com/users/oclif/gists{/gist_id}", 376 | "starred_url": "https://api.github.com/users/oclif/starred{/owner}{/repo}", 377 | "subscriptions_url": "https://api.github.com/users/oclif/subscriptions", 378 | "organizations_url": "https://api.github.com/users/oclif/orgs", 379 | "repos_url": "https://api.github.com/users/oclif/repos", 380 | "events_url": "https://api.github.com/users/oclif/events{/privacy}", 381 | "received_events_url": "https://api.github.com/users/oclif/received_events", 382 | "type": "Organization", 383 | "site_admin": false 384 | }, 385 | "html_url": "https://github.com/oclif/oclif", 386 | "description": "Node.js Open CLI Framework. Built with 💜 by Heroku.", 387 | "fork": false, 388 | "url": "https://api.github.com/repos/oclif/oclif", 389 | "forks_url": "https://api.github.com/repos/oclif/oclif/forks", 390 | "keys_url": "https://api.github.com/repos/oclif/oclif/keys{/key_id}", 391 | "collaborators_url": "https://api.github.com/repos/oclif/oclif/collaborators{/collaborator}", 392 | "teams_url": "https://api.github.com/repos/oclif/oclif/teams", 393 | "hooks_url": "https://api.github.com/repos/oclif/oclif/hooks", 394 | "issue_events_url": "https://api.github.com/repos/oclif/oclif/issues/events{/number}", 395 | "events_url": "https://api.github.com/repos/oclif/oclif/events", 396 | "assignees_url": "https://api.github.com/repos/oclif/oclif/assignees{/user}", 397 | "branches_url": "https://api.github.com/repos/oclif/oclif/branches{/branch}", 398 | "tags_url": "https://api.github.com/repos/oclif/oclif/tags", 399 | "blobs_url": "https://api.github.com/repos/oclif/oclif/git/blobs{/sha}", 400 | "git_tags_url": "https://api.github.com/repos/oclif/oclif/git/tags{/sha}", 401 | "git_refs_url": "https://api.github.com/repos/oclif/oclif/git/refs{/sha}", 402 | "trees_url": "https://api.github.com/repos/oclif/oclif/git/trees{/sha}", 403 | "statuses_url": "https://api.github.com/repos/oclif/oclif/statuses/{sha}", 404 | "languages_url": "https://api.github.com/repos/oclif/oclif/languages", 405 | "stargazers_url": "https://api.github.com/repos/oclif/oclif/stargazers", 406 | "contributors_url": "https://api.github.com/repos/oclif/oclif/contributors", 407 | "subscribers_url": "https://api.github.com/repos/oclif/oclif/subscribers", 408 | "subscription_url": "https://api.github.com/repos/oclif/oclif/subscription", 409 | "commits_url": "https://api.github.com/repos/oclif/oclif/commits{/sha}", 410 | "git_commits_url": "https://api.github.com/repos/oclif/oclif/git/commits{/sha}", 411 | "comments_url": "https://api.github.com/repos/oclif/oclif/comments{/number}", 412 | "issue_comment_url": "https://api.github.com/repos/oclif/oclif/issues/comments{/number}", 413 | "contents_url": "https://api.github.com/repos/oclif/oclif/contents/{+path}", 414 | "compare_url": "https://api.github.com/repos/oclif/oclif/compare/{base}...{head}", 415 | "merges_url": "https://api.github.com/repos/oclif/oclif/merges", 416 | "archive_url": "https://api.github.com/repos/oclif/oclif/{archive_format}{/ref}", 417 | "downloads_url": "https://api.github.com/repos/oclif/oclif/downloads", 418 | "issues_url": "https://api.github.com/repos/oclif/oclif/issues{/number}", 419 | "pulls_url": "https://api.github.com/repos/oclif/oclif/pulls{/number}", 420 | "milestones_url": "https://api.github.com/repos/oclif/oclif/milestones{/number}", 421 | "notifications_url": "https://api.github.com/repos/oclif/oclif/notifications{?since,all,participating}", 422 | "labels_url": "https://api.github.com/repos/oclif/oclif/labels{/name}", 423 | "releases_url": "https://api.github.com/repos/oclif/oclif/releases{/id}", 424 | "deployments_url": "https://api.github.com/repos/oclif/oclif/deployments" 425 | }, 426 | "score": 13.810236 427 | }, 428 | { 429 | "url": "https://api.github.com/repos/oclif/oclif/commits/d18f0cb5e20f7b5de8fd49c2bc6755df8f5ce33c", 430 | "sha": "d18f0cb5e20f7b5de8fd49c2bc6755df8f5ce33c", 431 | "node_id": "MDY6Q29tbWl0MTE3MTUyOTAyOmQxOGYwY2I1ZTIwZjdiNWRlOGZkNDljMmJjNjc1NWRmOGY1Y2UzM2M=", 432 | "html_url": "https://github.com/oclif/oclif/commit/d18f0cb5e20f7b5de8fd49c2bc6755df8f5ce33c", 433 | "comments_url": "https://api.github.com/repos/oclif/oclif/commits/d18f0cb5e20f7b5de8fd49c2bc6755df8f5ce33c/comments", 434 | "commit": { 435 | "url": "https://api.github.com/repos/oclif/oclif/git/commits/d18f0cb5e20f7b5de8fd49c2bc6755df8f5ce33c", 436 | "author": { 437 | "date": "2018-01-28T10:38:00.000Z", 438 | "name": "dxcli-bot", 439 | "email": "35625753+dxcli-bot@users.noreply.github.com" 440 | }, 441 | "committer": { 442 | "date": "2018-01-28T10:38:00.000Z", 443 | "name": "dxcli-bot", 444 | "email": "35625753+dxcli-bot@users.noreply.github.com" 445 | }, 446 | "message": "chore(release): 0.26.11 [skip ci]\n\n\n## [0.26.11](https://github.com/dxcli/create-dxcli/compare/0a289a10ba1d162b296678aba732140938038534...v0.26.11) (2018-01-28)\n\n### Bug Fixes\n\n* parser is now exported from command ([6f192b1](https://github.com/dxcli/create-dxcli/commit/6f192b1))", 447 | "tree": { 448 | "url": "https://api.github.com/repos/oclif/oclif/git/trees/322b9846e73bf06b0fec31197b89efa04fbb4462", 449 | "sha": "322b9846e73bf06b0fec31197b89efa04fbb4462" 450 | }, 451 | "comment_count": 0 452 | }, 453 | "author": { 454 | "login": "oclif-bot", 455 | "id": 35625753, 456 | "node_id": "MDQ6VXNlcjM1NjI1NzUz", 457 | "avatar_url": "https://avatars2.githubusercontent.com/u/35625753?v=4", 458 | "gravatar_id": "", 459 | "url": "https://api.github.com/users/oclif-bot", 460 | "html_url": "https://github.com/oclif-bot", 461 | "followers_url": "https://api.github.com/users/oclif-bot/followers", 462 | "following_url": "https://api.github.com/users/oclif-bot/following{/other_user}", 463 | "gists_url": "https://api.github.com/users/oclif-bot/gists{/gist_id}", 464 | "starred_url": "https://api.github.com/users/oclif-bot/starred{/owner}{/repo}", 465 | "subscriptions_url": "https://api.github.com/users/oclif-bot/subscriptions", 466 | "organizations_url": "https://api.github.com/users/oclif-bot/orgs", 467 | "repos_url": "https://api.github.com/users/oclif-bot/repos", 468 | "events_url": "https://api.github.com/users/oclif-bot/events{/privacy}", 469 | "received_events_url": "https://api.github.com/users/oclif-bot/received_events", 470 | "type": "User", 471 | "site_admin": false 472 | }, 473 | "committer": { 474 | "login": "oclif-bot", 475 | "id": 35625753, 476 | "node_id": "MDQ6VXNlcjM1NjI1NzUz", 477 | "avatar_url": "https://avatars2.githubusercontent.com/u/35625753?v=4", 478 | "gravatar_id": "", 479 | "url": "https://api.github.com/users/oclif-bot", 480 | "html_url": "https://github.com/oclif-bot", 481 | "followers_url": "https://api.github.com/users/oclif-bot/followers", 482 | "following_url": "https://api.github.com/users/oclif-bot/following{/other_user}", 483 | "gists_url": "https://api.github.com/users/oclif-bot/gists{/gist_id}", 484 | "starred_url": "https://api.github.com/users/oclif-bot/starred{/owner}{/repo}", 485 | "subscriptions_url": "https://api.github.com/users/oclif-bot/subscriptions", 486 | "organizations_url": "https://api.github.com/users/oclif-bot/orgs", 487 | "repos_url": "https://api.github.com/users/oclif-bot/repos", 488 | "events_url": "https://api.github.com/users/oclif-bot/events{/privacy}", 489 | "received_events_url": "https://api.github.com/users/oclif-bot/received_events", 490 | "type": "User", 491 | "site_admin": false 492 | }, 493 | "parents": [ 494 | { 495 | "url": "https://api.github.com/repos/oclif/oclif/commits/6f192b1cf7b373e1008bf9512cc0860ed1c8d953", 496 | "html_url": "https://github.com/oclif/oclif/commit/6f192b1cf7b373e1008bf9512cc0860ed1c8d953", 497 | "sha": "6f192b1cf7b373e1008bf9512cc0860ed1c8d953" 498 | } 499 | ], 500 | "repository": { 501 | "id": 117152902, 502 | "node_id": "MDEwOlJlcG9zaXRvcnkxMTcxNTI5MDI=", 503 | "name": "oclif", 504 | "full_name": "oclif/oclif", 505 | "private": false, 506 | "owner": { 507 | "login": "oclif", 508 | "id": 35349060, 509 | "node_id": "MDEyOk9yZ2FuaXphdGlvbjM1MzQ5MDYw", 510 | "avatar_url": "https://avatars3.githubusercontent.com/u/35349060?v=4", 511 | "gravatar_id": "", 512 | "url": "https://api.github.com/users/oclif", 513 | "html_url": "https://github.com/oclif", 514 | "followers_url": "https://api.github.com/users/oclif/followers", 515 | "following_url": "https://api.github.com/users/oclif/following{/other_user}", 516 | "gists_url": "https://api.github.com/users/oclif/gists{/gist_id}", 517 | "starred_url": "https://api.github.com/users/oclif/starred{/owner}{/repo}", 518 | "subscriptions_url": "https://api.github.com/users/oclif/subscriptions", 519 | "organizations_url": "https://api.github.com/users/oclif/orgs", 520 | "repos_url": "https://api.github.com/users/oclif/repos", 521 | "events_url": "https://api.github.com/users/oclif/events{/privacy}", 522 | "received_events_url": "https://api.github.com/users/oclif/received_events", 523 | "type": "Organization", 524 | "site_admin": false 525 | }, 526 | "html_url": "https://github.com/oclif/oclif", 527 | "description": "Node.js Open CLI Framework. Built with 💜 by Heroku.", 528 | "fork": false, 529 | "url": "https://api.github.com/repos/oclif/oclif", 530 | "forks_url": "https://api.github.com/repos/oclif/oclif/forks", 531 | "keys_url": "https://api.github.com/repos/oclif/oclif/keys{/key_id}", 532 | "collaborators_url": "https://api.github.com/repos/oclif/oclif/collaborators{/collaborator}", 533 | "teams_url": "https://api.github.com/repos/oclif/oclif/teams", 534 | "hooks_url": "https://api.github.com/repos/oclif/oclif/hooks", 535 | "issue_events_url": "https://api.github.com/repos/oclif/oclif/issues/events{/number}", 536 | "events_url": "https://api.github.com/repos/oclif/oclif/events", 537 | "assignees_url": "https://api.github.com/repos/oclif/oclif/assignees{/user}", 538 | "branches_url": "https://api.github.com/repos/oclif/oclif/branches{/branch}", 539 | "tags_url": "https://api.github.com/repos/oclif/oclif/tags", 540 | "blobs_url": "https://api.github.com/repos/oclif/oclif/git/blobs{/sha}", 541 | "git_tags_url": "https://api.github.com/repos/oclif/oclif/git/tags{/sha}", 542 | "git_refs_url": "https://api.github.com/repos/oclif/oclif/git/refs{/sha}", 543 | "trees_url": "https://api.github.com/repos/oclif/oclif/git/trees{/sha}", 544 | "statuses_url": "https://api.github.com/repos/oclif/oclif/statuses/{sha}", 545 | "languages_url": "https://api.github.com/repos/oclif/oclif/languages", 546 | "stargazers_url": "https://api.github.com/repos/oclif/oclif/stargazers", 547 | "contributors_url": "https://api.github.com/repos/oclif/oclif/contributors", 548 | "subscribers_url": "https://api.github.com/repos/oclif/oclif/subscribers", 549 | "subscription_url": "https://api.github.com/repos/oclif/oclif/subscription", 550 | "commits_url": "https://api.github.com/repos/oclif/oclif/commits{/sha}", 551 | "git_commits_url": "https://api.github.com/repos/oclif/oclif/git/commits{/sha}", 552 | "comments_url": "https://api.github.com/repos/oclif/oclif/comments{/number}", 553 | "issue_comment_url": "https://api.github.com/repos/oclif/oclif/issues/comments{/number}", 554 | "contents_url": "https://api.github.com/repos/oclif/oclif/contents/{+path}", 555 | "compare_url": "https://api.github.com/repos/oclif/oclif/compare/{base}...{head}", 556 | "merges_url": "https://api.github.com/repos/oclif/oclif/merges", 557 | "archive_url": "https://api.github.com/repos/oclif/oclif/{archive_format}{/ref}", 558 | "downloads_url": "https://api.github.com/repos/oclif/oclif/downloads", 559 | "issues_url": "https://api.github.com/repos/oclif/oclif/issues{/number}", 560 | "pulls_url": "https://api.github.com/repos/oclif/oclif/pulls{/number}", 561 | "milestones_url": "https://api.github.com/repos/oclif/oclif/milestones{/number}", 562 | "notifications_url": "https://api.github.com/repos/oclif/oclif/notifications{?since,all,participating}", 563 | "labels_url": "https://api.github.com/repos/oclif/oclif/labels{/name}", 564 | "releases_url": "https://api.github.com/repos/oclif/oclif/releases{/id}", 565 | "deployments_url": "https://api.github.com/repos/oclif/oclif/deployments" 566 | }, 567 | "score": 12.779759 568 | }, 569 | { 570 | "url": "https://api.github.com/repos/oclif/oclif/commits/48674cebae1b8870e569f4acd1e190cc85adfa0e", 571 | "sha": "48674cebae1b8870e569f4acd1e190cc85adfa0e", 572 | "node_id": "MDY6Q29tbWl0MTE3MTUyOTAyOjQ4Njc0Y2ViYWUxYjg4NzBlNTY5ZjRhY2QxZTE5MGNjODVhZGZhMGU=", 573 | "html_url": "https://github.com/oclif/oclif/commit/48674cebae1b8870e569f4acd1e190cc85adfa0e", 574 | "comments_url": "https://api.github.com/repos/oclif/oclif/commits/48674cebae1b8870e569f4acd1e190cc85adfa0e/comments", 575 | "commit": { 576 | "url": "https://api.github.com/repos/oclif/oclif/git/commits/48674cebae1b8870e569f4acd1e190cc85adfa0e", 577 | "author": { 578 | "date": "2018-02-01T05:53:41.000Z", 579 | "name": "anycli-bot", 580 | "email": "35625753+dxcli-bot@users.noreply.github.com" 581 | }, 582 | "committer": { 583 | "date": "2018-02-01T05:53:41.000Z", 584 | "name": "anycli-bot", 585 | "email": "35625753+dxcli-bot@users.noreply.github.com" 586 | }, 587 | "message": "chore(release): 0.29.0 [skip ci]\n\n\n# [0.29.0](https://github.com/anycli/anycli/compare/844ab18e55794c4b6ac36168e0648b1e8222044f...v0.29.0) (2018-02-01)\n\n### Bug Fixes\n\n* consolidate commands and tests ([b90320a](https://github.com/anycli/anycli/commit/b90320a))\n* fixed command generator ([8c5c179](https://github.com/anycli/anycli/commit/8c5c179))\n\n### Features\n\n* use new parser model ([7e7f7d3](https://github.com/anycli/anycli/commit/7e7f7d3))", 588 | "tree": { 589 | "url": "https://api.github.com/repos/oclif/oclif/git/trees/c795fa391f1080d8b087d3b944f5fa8c88ee0556", 590 | "sha": "c795fa391f1080d8b087d3b944f5fa8c88ee0556" 591 | }, 592 | "comment_count": 0 593 | }, 594 | "author": { 595 | "login": "oclif-bot", 596 | "id": 35625753, 597 | "node_id": "MDQ6VXNlcjM1NjI1NzUz", 598 | "avatar_url": "https://avatars2.githubusercontent.com/u/35625753?v=4", 599 | "gravatar_id": "", 600 | "url": "https://api.github.com/users/oclif-bot", 601 | "html_url": "https://github.com/oclif-bot", 602 | "followers_url": "https://api.github.com/users/oclif-bot/followers", 603 | "following_url": "https://api.github.com/users/oclif-bot/following{/other_user}", 604 | "gists_url": "https://api.github.com/users/oclif-bot/gists{/gist_id}", 605 | "starred_url": "https://api.github.com/users/oclif-bot/starred{/owner}{/repo}", 606 | "subscriptions_url": "https://api.github.com/users/oclif-bot/subscriptions", 607 | "organizations_url": "https://api.github.com/users/oclif-bot/orgs", 608 | "repos_url": "https://api.github.com/users/oclif-bot/repos", 609 | "events_url": "https://api.github.com/users/oclif-bot/events{/privacy}", 610 | "received_events_url": "https://api.github.com/users/oclif-bot/received_events", 611 | "type": "User", 612 | "site_admin": false 613 | }, 614 | "committer": { 615 | "login": "oclif-bot", 616 | "id": 35625753, 617 | "node_id": "MDQ6VXNlcjM1NjI1NzUz", 618 | "avatar_url": "https://avatars2.githubusercontent.com/u/35625753?v=4", 619 | "gravatar_id": "", 620 | "url": "https://api.github.com/users/oclif-bot", 621 | "html_url": "https://github.com/oclif-bot", 622 | "followers_url": "https://api.github.com/users/oclif-bot/followers", 623 | "following_url": "https://api.github.com/users/oclif-bot/following{/other_user}", 624 | "gists_url": "https://api.github.com/users/oclif-bot/gists{/gist_id}", 625 | "starred_url": "https://api.github.com/users/oclif-bot/starred{/owner}{/repo}", 626 | "subscriptions_url": "https://api.github.com/users/oclif-bot/subscriptions", 627 | "organizations_url": "https://api.github.com/users/oclif-bot/orgs", 628 | "repos_url": "https://api.github.com/users/oclif-bot/repos", 629 | "events_url": "https://api.github.com/users/oclif-bot/events{/privacy}", 630 | "received_events_url": "https://api.github.com/users/oclif-bot/received_events", 631 | "type": "User", 632 | "site_admin": false 633 | }, 634 | "parents": [ 635 | { 636 | "url": "https://api.github.com/repos/oclif/oclif/commits/b90320a5dd3ef9e5808995c38d7d16d86b32ea03", 637 | "html_url": "https://github.com/oclif/oclif/commit/b90320a5dd3ef9e5808995c38d7d16d86b32ea03", 638 | "sha": "b90320a5dd3ef9e5808995c38d7d16d86b32ea03" 639 | } 640 | ], 641 | "repository": { 642 | "id": 117152902, 643 | "node_id": "MDEwOlJlcG9zaXRvcnkxMTcxNTI5MDI=", 644 | "name": "oclif", 645 | "full_name": "oclif/oclif", 646 | "private": false, 647 | "owner": { 648 | "login": "oclif", 649 | "id": 35349060, 650 | "node_id": "MDEyOk9yZ2FuaXphdGlvbjM1MzQ5MDYw", 651 | "avatar_url": "https://avatars3.githubusercontent.com/u/35349060?v=4", 652 | "gravatar_id": "", 653 | "url": "https://api.github.com/users/oclif", 654 | "html_url": "https://github.com/oclif", 655 | "followers_url": "https://api.github.com/users/oclif/followers", 656 | "following_url": "https://api.github.com/users/oclif/following{/other_user}", 657 | "gists_url": "https://api.github.com/users/oclif/gists{/gist_id}", 658 | "starred_url": "https://api.github.com/users/oclif/starred{/owner}{/repo}", 659 | "subscriptions_url": "https://api.github.com/users/oclif/subscriptions", 660 | "organizations_url": "https://api.github.com/users/oclif/orgs", 661 | "repos_url": "https://api.github.com/users/oclif/repos", 662 | "events_url": "https://api.github.com/users/oclif/events{/privacy}", 663 | "received_events_url": "https://api.github.com/users/oclif/received_events", 664 | "type": "Organization", 665 | "site_admin": false 666 | }, 667 | "html_url": "https://github.com/oclif/oclif", 668 | "description": "Node.js Open CLI Framework. Built with 💜 by Heroku.", 669 | "fork": false, 670 | "url": "https://api.github.com/repos/oclif/oclif", 671 | "forks_url": "https://api.github.com/repos/oclif/oclif/forks", 672 | "keys_url": "https://api.github.com/repos/oclif/oclif/keys{/key_id}", 673 | "collaborators_url": "https://api.github.com/repos/oclif/oclif/collaborators{/collaborator}", 674 | "teams_url": "https://api.github.com/repos/oclif/oclif/teams", 675 | "hooks_url": "https://api.github.com/repos/oclif/oclif/hooks", 676 | "issue_events_url": "https://api.github.com/repos/oclif/oclif/issues/events{/number}", 677 | "events_url": "https://api.github.com/repos/oclif/oclif/events", 678 | "assignees_url": "https://api.github.com/repos/oclif/oclif/assignees{/user}", 679 | "branches_url": "https://api.github.com/repos/oclif/oclif/branches{/branch}", 680 | "tags_url": "https://api.github.com/repos/oclif/oclif/tags", 681 | "blobs_url": "https://api.github.com/repos/oclif/oclif/git/blobs{/sha}", 682 | "git_tags_url": "https://api.github.com/repos/oclif/oclif/git/tags{/sha}", 683 | "git_refs_url": "https://api.github.com/repos/oclif/oclif/git/refs{/sha}", 684 | "trees_url": "https://api.github.com/repos/oclif/oclif/git/trees{/sha}", 685 | "statuses_url": "https://api.github.com/repos/oclif/oclif/statuses/{sha}", 686 | "languages_url": "https://api.github.com/repos/oclif/oclif/languages", 687 | "stargazers_url": "https://api.github.com/repos/oclif/oclif/stargazers", 688 | "contributors_url": "https://api.github.com/repos/oclif/oclif/contributors", 689 | "subscribers_url": "https://api.github.com/repos/oclif/oclif/subscribers", 690 | "subscription_url": "https://api.github.com/repos/oclif/oclif/subscription", 691 | "commits_url": "https://api.github.com/repos/oclif/oclif/commits{/sha}", 692 | "git_commits_url": "https://api.github.com/repos/oclif/oclif/git/commits{/sha}", 693 | "comments_url": "https://api.github.com/repos/oclif/oclif/comments{/number}", 694 | "issue_comment_url": "https://api.github.com/repos/oclif/oclif/issues/comments{/number}", 695 | "contents_url": "https://api.github.com/repos/oclif/oclif/contents/{+path}", 696 | "compare_url": "https://api.github.com/repos/oclif/oclif/compare/{base}...{head}", 697 | "merges_url": "https://api.github.com/repos/oclif/oclif/merges", 698 | "archive_url": "https://api.github.com/repos/oclif/oclif/{archive_format}{/ref}", 699 | "downloads_url": "https://api.github.com/repos/oclif/oclif/downloads", 700 | "issues_url": "https://api.github.com/repos/oclif/oclif/issues{/number}", 701 | "pulls_url": "https://api.github.com/repos/oclif/oclif/pulls{/number}", 702 | "milestones_url": "https://api.github.com/repos/oclif/oclif/milestones{/number}", 703 | "notifications_url": "https://api.github.com/repos/oclif/oclif/notifications{?since,all,participating}", 704 | "labels_url": "https://api.github.com/repos/oclif/oclif/labels{/name}", 705 | "releases_url": "https://api.github.com/repos/oclif/oclif/releases{/id}", 706 | "deployments_url": "https://api.github.com/repos/oclif/oclif/deployments" 707 | }, 708 | "score": 12.292561 709 | }, 710 | { 711 | "url": "https://api.github.com/repos/oclif/oclif/commits/655b139a72b1d3e211a02b233ac1037122143b32", 712 | "sha": "655b139a72b1d3e211a02b233ac1037122143b32", 713 | "node_id": "MDY6Q29tbWl0MTE3MTUyOTAyOjY1NWIxMzlhNzJiMWQzZTIxMWEwMmIyMzNhYzEwMzcxMjIxNDNiMzI=", 714 | "html_url": "https://github.com/oclif/oclif/commit/655b139a72b1d3e211a02b233ac1037122143b32", 715 | "comments_url": "https://api.github.com/repos/oclif/oclif/commits/655b139a72b1d3e211a02b233ac1037122143b32/comments", 716 | "commit": { 717 | "url": "https://api.github.com/repos/oclif/oclif/git/commits/655b139a72b1d3e211a02b233ac1037122143b32", 718 | "author": { 719 | "date": "2018-01-28T23:51:57.000Z", 720 | "name": "dxcli-bot", 721 | "email": "35625753+dxcli-bot@users.noreply.github.com" 722 | }, 723 | "committer": { 724 | "date": "2018-01-28T23:51:57.000Z", 725 | "name": "dxcli-bot", 726 | "email": "35625753+dxcli-bot@users.noreply.github.com" 727 | }, 728 | "message": "chore(release): 0.28.0 [skip ci]\n\n\n# [0.28.0](https://github.com/dxcli/create-dxcli/compare/v0.27.1...v0.28.0) (2018-01-28)\n\n### Bug Fixes\n\n* add TS parsing for commands ([68c2023](https://github.com/dxcli/create-dxcli/commit/68c2023))\n* bump cli-ux ([adf7aad](https://github.com/dxcli/create-dxcli/commit/adf7aad))\n* bump cli-ux ([f4f0f3f](https://github.com/dxcli/create-dxcli/commit/f4f0f3f))\n* fixing single and adding windows cmd scripts ([3659d86](https://github.com/dxcli/create-dxcli/commit/3659d86))\n* improve TS loading ([8d42d1e](https://github.com/dxcli/create-dxcli/commit/8d42d1e))\n* simplified bin scripts ([8b36b61](https://github.com/dxcli/create-dxcli/commit/8b36b61))\n* updated deps ([c6e576a](https://github.com/dxcli/create-dxcli/commit/c6e576a))\n\n### Features\n\n* add mocha script ([4efb9ab](https://github.com/dxcli/create-dxcli/commit/4efb9ab))\n* added command generator ([f6fef1f](https://github.com/dxcli/create-dxcli/commit/f6fef1f))", 729 | "tree": { 730 | "url": "https://api.github.com/repos/oclif/oclif/git/trees/e904350515bfbae2f6dc6c597b7f57c87913af33", 731 | "sha": "e904350515bfbae2f6dc6c597b7f57c87913af33" 732 | }, 733 | "comment_count": 0 734 | }, 735 | "author": { 736 | "login": "oclif-bot", 737 | "id": 35625753, 738 | "node_id": "MDQ6VXNlcjM1NjI1NzUz", 739 | "avatar_url": "https://avatars2.githubusercontent.com/u/35625753?v=4", 740 | "gravatar_id": "", 741 | "url": "https://api.github.com/users/oclif-bot", 742 | "html_url": "https://github.com/oclif-bot", 743 | "followers_url": "https://api.github.com/users/oclif-bot/followers", 744 | "following_url": "https://api.github.com/users/oclif-bot/following{/other_user}", 745 | "gists_url": "https://api.github.com/users/oclif-bot/gists{/gist_id}", 746 | "starred_url": "https://api.github.com/users/oclif-bot/starred{/owner}{/repo}", 747 | "subscriptions_url": "https://api.github.com/users/oclif-bot/subscriptions", 748 | "organizations_url": "https://api.github.com/users/oclif-bot/orgs", 749 | "repos_url": "https://api.github.com/users/oclif-bot/repos", 750 | "events_url": "https://api.github.com/users/oclif-bot/events{/privacy}", 751 | "received_events_url": "https://api.github.com/users/oclif-bot/received_events", 752 | "type": "User", 753 | "site_admin": false 754 | }, 755 | "committer": { 756 | "login": "oclif-bot", 757 | "id": 35625753, 758 | "node_id": "MDQ6VXNlcjM1NjI1NzUz", 759 | "avatar_url": "https://avatars2.githubusercontent.com/u/35625753?v=4", 760 | "gravatar_id": "", 761 | "url": "https://api.github.com/users/oclif-bot", 762 | "html_url": "https://github.com/oclif-bot", 763 | "followers_url": "https://api.github.com/users/oclif-bot/followers", 764 | "following_url": "https://api.github.com/users/oclif-bot/following{/other_user}", 765 | "gists_url": "https://api.github.com/users/oclif-bot/gists{/gist_id}", 766 | "starred_url": "https://api.github.com/users/oclif-bot/starred{/owner}{/repo}", 767 | "subscriptions_url": "https://api.github.com/users/oclif-bot/subscriptions", 768 | "organizations_url": "https://api.github.com/users/oclif-bot/orgs", 769 | "repos_url": "https://api.github.com/users/oclif-bot/repos", 770 | "events_url": "https://api.github.com/users/oclif-bot/events{/privacy}", 771 | "received_events_url": "https://api.github.com/users/oclif-bot/received_events", 772 | "type": "User", 773 | "site_admin": false 774 | }, 775 | "parents": [ 776 | { 777 | "url": "https://api.github.com/repos/oclif/oclif/commits/8b36b615cd0c96adc92e8f6dc4765f5dac5c2778", 778 | "html_url": "https://github.com/oclif/oclif/commit/8b36b615cd0c96adc92e8f6dc4765f5dac5c2778", 779 | "sha": "8b36b615cd0c96adc92e8f6dc4765f5dac5c2778" 780 | } 781 | ], 782 | "repository": { 783 | "id": 117152902, 784 | "node_id": "MDEwOlJlcG9zaXRvcnkxMTcxNTI5MDI=", 785 | "name": "oclif", 786 | "full_name": "oclif/oclif", 787 | "private": false, 788 | "owner": { 789 | "login": "oclif", 790 | "id": 35349060, 791 | "node_id": "MDEyOk9yZ2FuaXphdGlvbjM1MzQ5MDYw", 792 | "avatar_url": "https://avatars3.githubusercontent.com/u/35349060?v=4", 793 | "gravatar_id": "", 794 | "url": "https://api.github.com/users/oclif", 795 | "html_url": "https://github.com/oclif", 796 | "followers_url": "https://api.github.com/users/oclif/followers", 797 | "following_url": "https://api.github.com/users/oclif/following{/other_user}", 798 | "gists_url": "https://api.github.com/users/oclif/gists{/gist_id}", 799 | "starred_url": "https://api.github.com/users/oclif/starred{/owner}{/repo}", 800 | "subscriptions_url": "https://api.github.com/users/oclif/subscriptions", 801 | "organizations_url": "https://api.github.com/users/oclif/orgs", 802 | "repos_url": "https://api.github.com/users/oclif/repos", 803 | "events_url": "https://api.github.com/users/oclif/events{/privacy}", 804 | "received_events_url": "https://api.github.com/users/oclif/received_events", 805 | "type": "Organization", 806 | "site_admin": false 807 | }, 808 | "html_url": "https://github.com/oclif/oclif", 809 | "description": "Node.js Open CLI Framework. Built with 💜 by Heroku.", 810 | "fork": false, 811 | "url": "https://api.github.com/repos/oclif/oclif", 812 | "forks_url": "https://api.github.com/repos/oclif/oclif/forks", 813 | "keys_url": "https://api.github.com/repos/oclif/oclif/keys{/key_id}", 814 | "collaborators_url": "https://api.github.com/repos/oclif/oclif/collaborators{/collaborator}", 815 | "teams_url": "https://api.github.com/repos/oclif/oclif/teams", 816 | "hooks_url": "https://api.github.com/repos/oclif/oclif/hooks", 817 | "issue_events_url": "https://api.github.com/repos/oclif/oclif/issues/events{/number}", 818 | "events_url": "https://api.github.com/repos/oclif/oclif/events", 819 | "assignees_url": "https://api.github.com/repos/oclif/oclif/assignees{/user}", 820 | "branches_url": "https://api.github.com/repos/oclif/oclif/branches{/branch}", 821 | "tags_url": "https://api.github.com/repos/oclif/oclif/tags", 822 | "blobs_url": "https://api.github.com/repos/oclif/oclif/git/blobs{/sha}", 823 | "git_tags_url": "https://api.github.com/repos/oclif/oclif/git/tags{/sha}", 824 | "git_refs_url": "https://api.github.com/repos/oclif/oclif/git/refs{/sha}", 825 | "trees_url": "https://api.github.com/repos/oclif/oclif/git/trees{/sha}", 826 | "statuses_url": "https://api.github.com/repos/oclif/oclif/statuses/{sha}", 827 | "languages_url": "https://api.github.com/repos/oclif/oclif/languages", 828 | "stargazers_url": "https://api.github.com/repos/oclif/oclif/stargazers", 829 | "contributors_url": "https://api.github.com/repos/oclif/oclif/contributors", 830 | "subscribers_url": "https://api.github.com/repos/oclif/oclif/subscribers", 831 | "subscription_url": "https://api.github.com/repos/oclif/oclif/subscription", 832 | "commits_url": "https://api.github.com/repos/oclif/oclif/commits{/sha}", 833 | "git_commits_url": "https://api.github.com/repos/oclif/oclif/git/commits{/sha}", 834 | "comments_url": "https://api.github.com/repos/oclif/oclif/comments{/number}", 835 | "issue_comment_url": "https://api.github.com/repos/oclif/oclif/issues/comments{/number}", 836 | "contents_url": "https://api.github.com/repos/oclif/oclif/contents/{+path}", 837 | "compare_url": "https://api.github.com/repos/oclif/oclif/compare/{base}...{head}", 838 | "merges_url": "https://api.github.com/repos/oclif/oclif/merges", 839 | "archive_url": "https://api.github.com/repos/oclif/oclif/{archive_format}{/ref}", 840 | "downloads_url": "https://api.github.com/repos/oclif/oclif/downloads", 841 | "issues_url": "https://api.github.com/repos/oclif/oclif/issues{/number}", 842 | "pulls_url": "https://api.github.com/repos/oclif/oclif/pulls{/number}", 843 | "milestones_url": "https://api.github.com/repos/oclif/oclif/milestones{/number}", 844 | "notifications_url": "https://api.github.com/repos/oclif/oclif/notifications{?since,all,participating}", 845 | "labels_url": "https://api.github.com/repos/oclif/oclif/labels{/name}", 846 | "releases_url": "https://api.github.com/repos/oclif/oclif/releases{/id}", 847 | "deployments_url": "https://api.github.com/repos/oclif/oclif/deployments" 848 | }, 849 | "score": 9.137721 850 | }, 851 | { 852 | "url": "https://api.github.com/repos/oclif/oclif/commits/fc232bde5a597db011097c3d53701814c75cd48f", 853 | "sha": "fc232bde5a597db011097c3d53701814c75cd48f", 854 | "node_id": "MDY6Q29tbWl0MTE3MTUyOTAyOmZjMjMyYmRlNWE1OTdkYjAxMTA5N2MzZDUzNzAxODE0Yzc1Y2Q0OGY=", 855 | "html_url": "https://github.com/oclif/oclif/commit/fc232bde5a597db011097c3d53701814c75cd48f", 856 | "comments_url": "https://api.github.com/repos/oclif/oclif/commits/fc232bde5a597db011097c3d53701814c75cd48f/comments", 857 | "commit": { 858 | "url": "https://api.github.com/repos/oclif/oclif/git/commits/fc232bde5a597db011097c3d53701814c75cd48f", 859 | "author": { 860 | "date": "2018-02-13T03:30:38.000Z", 861 | "name": "oclif-bot", 862 | "email": "498c3f754e2453f89e1dce1990846f4063faefd4" 863 | }, 864 | "committer": { 865 | "date": "2018-02-13T03:30:38.000Z", 866 | "name": "oclif-bot", 867 | "email": "498c3f754e2453f89e1dce1990846f4063faefd4" 868 | }, 869 | "message": "chore(release): 1.0.0 [skip ci]\n\n\n# 1.0.0 (2018-02-13)\n\n### Bug Fixes\n\n* add [@dxcli](https://github.com/dxcli)/dev-semantic-release ([ff9c542](https://github.com/oclif/cli/commit/ff9c542))\n* add another retry ([6dfe207](https://github.com/oclif/cli/commit/6dfe207))\n* add another workflow for caching ([5fe5d11](https://github.com/oclif/cli/commit/5fe5d11))\n* add bin scripts to files ([a95f10c](https://github.com/oclif/cli/commit/a95f10c))\n* add catch handler ([b8848a4](https://github.com/oclif/cli/commit/b8848a4))\n* add commitlint ([f60d9f2](https://github.com/oclif/cli/commit/f60d9f2))\n* add config to deps ([354683d](https://github.com/oclif/cli/commit/354683d))\n* add dev deps ([98ea830](https://github.com/oclif/cli/commit/98ea830))\n* add dev deps for single ([efc07dc](https://github.com/oclif/cli/commit/efc07dc))\n* add docs and readme to semantic-release ([75b4a74](https://github.com/oclif/cli/commit/75b4a74))\n* add errors to deps ([fd0e9ed](https://github.com/oclif/cli/commit/fd0e9ed))\n* add globby ([bf2bfab](https://github.com/oclif/cli/commit/bf2bfab))\n* add manifest to gitignore ([692ca32](https://github.com/oclif/cli/commit/692ca32))\n* add mocha-junit-reporter ([a1a4a08](https://github.com/oclif/cli/commit/a1a4a08))\n* add mutex so tests can run in parallel ([b03b605](https://github.com/oclif/cli/commit/b03b605))\n* add nyc ([a25c802](https://github.com/oclif/cli/commit/a25c802))\n* add nyc config ([21f25d0](https://github.com/oclif/cli/commit/21f25d0))\n* add nyc config ([73b448b](https://github.com/oclif/cli/commit/73b448b))\n* add nyc to appveyor ([b0216f1](https://github.com/oclif/cli/commit/b0216f1))\n* add other commands ([394b5f0](https://github.com/oclif/cli/commit/394b5f0))\n* add peerDependencies ([7be5fd1](https://github.com/oclif/cli/commit/7be5fd1))\n* add READMEs ([f78cf18](https://github.com/oclif/cli/commit/f78cf18))\n* add semantic-release locally instead ([c8bc421](https://github.com/oclif/cli/commit/c8bc421))\n* add semantic-release step if not mocha ([143532c](https://github.com/oclif/cli/commit/143532c))\n* add smoke tests to appveyor ([f33a88d](https://github.com/oclif/cli/commit/f33a88d))\n* add templates to pack ([ea06630](https://github.com/oclif/cli/commit/ea06630))\n* add TS parsing for commands ([68c2023](https://github.com/oclif/cli/commit/68c2023))\n* add yarn circle step ([52610cd](https://github.com/oclif/cli/commit/52610cd))\n* added bin script to pjson ([190cb30](https://github.com/oclif/cli/commit/190cb30))\n* added clean step ([da22f7f](https://github.com/oclif/cli/commit/da22f7f))\n* added docs ([b5d798e](https://github.com/oclif/cli/commit/b5d798e))\n* added some smoke tests ([24664f1](https://github.com/oclif/cli/commit/24664f1))\n* anycli rename ([50823f4](https://github.com/oclif/cli/commit/50823f4))\n* attach workspace when releasing ([daa43ea](https://github.com/oclif/cli/commit/daa43ea))\n* better repo handling ([4b1add8](https://github.com/oclif/cli/commit/4b1add8))\n* bump cache ([9773ed7](https://github.com/oclif/cli/commit/9773ed7))\n* bump cli-ux ([f4f0f3f](https://github.com/oclif/cli/commit/f4f0f3f))\n* bump cli-ux ([adf7aad](https://github.com/oclif/cli/commit/adf7aad))\n* bump dev-cli ([39e5b5f](https://github.com/oclif/cli/commit/39e5b5f))\n* bump dev-cli ([6d4764f](https://github.com/oclif/cli/commit/6d4764f))\n* bump semantic-release ([48b6b4c](https://github.com/oclif/cli/commit/48b6b4c))\n* cache after release ([a7e0d14](https://github.com/oclif/cli/commit/a7e0d14))\n* circle cache improvements ([7e9c3d0](https://github.com/oclif/cli/commit/7e9c3d0))\n* consolidate circle jobs ([9f3c2f2](https://github.com/oclif/cli/commit/9f3c2f2))\n* consolidate circle jobs ([d72884d](https://github.com/oclif/cli/commit/d72884d))\n* consolidate commands and tests ([b90320a](https://github.com/oclif/cli/commit/b90320a))\n* default to not having options ([72dd9d9](https://github.com/oclif/cli/commit/72dd9d9))\n* delete all files from existing example ([0081edd](https://github.com/oclif/cli/commit/0081edd))\n* dev dep ([46dd0a3](https://github.com/oclif/cli/commit/46dd0a3))\n* disable yarn check for now ([5637b46](https://github.com/oclif/cli/commit/5637b46))\n* do not install dev deps on release ([8ba2b5f](https://github.com/oclif/cli/commit/8ba2b5f))\n* do not set anycli if empty ([3c7bab9](https://github.com/oclif/cli/commit/3c7bab9))\n* dont try to get version of plugin ([ad3544b](https://github.com/oclif/cli/commit/ad3544b))\n* ensure /lib is always gitignored for ts ([07c7e38](https://github.com/oclif/cli/commit/07c7e38))\n* explicitly add [@dxcli](https://github.com/dxcli)/nyc-config ([9944ead](https://github.com/oclif/cli/commit/9944ead))\n* export lib ([ad28e28](https://github.com/oclif/cli/commit/ad28e28))\n* fetch semantic release when needed ([db289ca](https://github.com/oclif/cli/commit/db289ca))\n* fetch semantic release when needed ([7340723](https://github.com/oclif/cli/commit/7340723))\n* fix commitlint ([9aa2212](https://github.com/oclif/cli/commit/9aa2212))\n* fix nyc on circle ([8257b7a](https://github.com/oclif/cli/commit/8257b7a))\n* fix releases ([435a96d](https://github.com/oclif/cli/commit/435a96d))\n* fix yarn execs ([93b3f16](https://github.com/oclif/cli/commit/93b3f16))\n* fixed bin name ([5995e7f](https://github.com/oclif/cli/commit/5995e7f))\n* fixed circle config ([9a8166e](https://github.com/oclif/cli/commit/9a8166e))\n* fixed command generator ([8c5c179](https://github.com/oclif/cli/commit/8c5c179))\n* fixed directory in release script ([a4550b7](https://github.com/oclif/cli/commit/a4550b7))\n* fixed global add ([6e5fb0e](https://github.com/oclif/cli/commit/6e5fb0e))\n* fixed greenkeeper script ([d904f25](https://github.com/oclif/cli/commit/d904f25))\n* fixed greenkeeper script ([b8be73b](https://github.com/oclif/cli/commit/b8be73b))\n* fixed indentation ([0ee469a](https://github.com/oclif/cli/commit/0ee469a))\n* fixed issues with package-scripts ([571facb](https://github.com/oclif/cli/commit/571facb))\n* fixed linters ([5ac2d4c](https://github.com/oclif/cli/commit/5ac2d4c))\n* fixed manifest location ([36f79a8](https://github.com/oclif/cli/commit/36f79a8))\n* fixed manifest location ([14802aa](https://github.com/oclif/cli/commit/14802aa))\n* fixed mocha in js ([dbc44bf](https://github.com/oclif/cli/commit/dbc44bf))\n* fixed plugin circle config ([85b5640](https://github.com/oclif/cli/commit/85b5640))\n* fixed references to deps ([353cac2](https://github.com/oclif/cli/commit/353cac2))\n* fixed restore cache step ([20de5dc](https://github.com/oclif/cli/commit/20de5dc))\n* fixed single bin script ([a8a4b33](https://github.com/oclif/cli/commit/a8a4b33))\n* fixed typescript output ([b3bde94](https://github.com/oclif/cli/commit/b3bde94))\n* fixed yarn mutex ([43335e9](https://github.com/oclif/cli/commit/43335e9))\n* fixed yarn step in circle config ([8bdadc3](https://github.com/oclif/cli/commit/8bdadc3))\n* fixed yarn step in circle config ([e6e19c4](https://github.com/oclif/cli/commit/e6e19c4))\n* fixing example publishing ([d03efe7](https://github.com/oclif/cli/commit/d03efe7))\n* fixing example publishing ([6087f9c](https://github.com/oclif/cli/commit/6087f9c))\n* fixing example publishing ([a333610](https://github.com/oclif/cli/commit/a333610))\n* fixing single ([354f46b](https://github.com/oclif/cli/commit/354f46b))\n* fixing single and adding windows cmd scripts ([3659d86](https://github.com/oclif/cli/commit/3659d86))\n* generator improvements ([50c303f](https://github.com/oclif/cli/commit/50c303f))\n* git username ([11b4b58](https://github.com/oclif/cli/commit/11b4b58))\n* globby not needed for single ([8d34141](https://github.com/oclif/cli/commit/8d34141))\n* ignore kebab case on package-scripts ([e3ca364](https://github.com/oclif/cli/commit/e3ca364))\n* improve experience when overwiting existing base ([f79dfef](https://github.com/oclif/cli/commit/f79dfef))\n* improve TS loading ([8d42d1e](https://github.com/oclif/cli/commit/8d42d1e))\n* lower loglevel ([5286151](https://github.com/oclif/cli/commit/5286151))\n* mkdir ([dc8a2d1](https://github.com/oclif/cli/commit/dc8a2d1))\n* mkdir ([09d86ae](https://github.com/oclif/cli/commit/09d86ae))\n* mkdirp reports ([ab4b02f](https://github.com/oclif/cli/commit/ab4b02f))\n* move generator into this package ([d379e63](https://github.com/oclif/cli/commit/d379e63))\n* move help to devPlugins for pluginsg ([157f1d6](https://github.com/oclif/cli/commit/157f1d6))\n* move setup_git into greenkeeper ([b6c87d9](https://github.com/oclif/cli/commit/b6c87d9))\n* no longer need engine ([bd41ae3](https://github.com/oclif/cli/commit/bd41ae3))\n* only build on release ([fecec26](https://github.com/oclif/cli/commit/fecec26))\n* only build on release ([0f5e7db](https://github.com/oclif/cli/commit/0f5e7db))\n* only build on release ([dba0a63](https://github.com/oclif/cli/commit/dba0a63))\n* only push eslint if it is not empty ([ef9a649](https://github.com/oclif/cli/commit/ef9a649))\n* only sort if array ([6557c82](https://github.com/oclif/cli/commit/6557c82))\n* only use concurrently when needed ([bd567e1](https://github.com/oclif/cli/commit/bd567e1))\n* overwrite scripts ([fc9bba9](https://github.com/oclif/cli/commit/fc9bba9))\n* parser is now exported from command ([6f192b1](https://github.com/oclif/cli/commit/6f192b1))\n* reduce dependencies needed in dev ([2a0bbad](https://github.com/oclif/cli/commit/2a0bbad))\n* release example plugins ([4500eed](https://github.com/oclif/cli/commit/4500eed))\n* release examples ([6a1c3df](https://github.com/oclif/cli/commit/6a1c3df))\n* releases ([fbdc9c2](https://github.com/oclif/cli/commit/fbdc9c2))\n* releases ([ba33440](https://github.com/oclif/cli/commit/ba33440))\n* remove fromScratch ([65d35c5](https://github.com/oclif/cli/commit/65d35c5))\n* remove release task from nps ([38e15dd](https://github.com/oclif/cli/commit/38e15dd))\n* rename dev packages ([e85a5cf](https://github.com/oclif/cli/commit/e85a5cf))\n* rename dev-test to test ([2a2bf9b](https://github.com/oclif/cli/commit/2a2bf9b))\n* rename greenkeeper -> yarn ([78afe96](https://github.com/oclif/cli/commit/78afe96))\n* rename package for now ([8e2f0c8](https://github.com/oclif/cli/commit/8e2f0c8))\n* rename repo ([37ca4cc](https://github.com/oclif/cli/commit/37ca4cc))\n* rename to oclif ([cb1ab35](https://github.com/oclif/cli/commit/cb1ab35))\n* restore cache if exact match ([0f27493](https://github.com/oclif/cli/commit/0f27493))\n* run build before tests ([05765fa](https://github.com/oclif/cli/commit/05765fa))\n* run nps build before release ([0a289a1](https://github.com/oclif/cli/commit/0a289a1))\n* scope templates ([2f03b43](https://github.com/oclif/cli/commit/2f03b43))\n* semantic-release bin ([8183ae5](https://github.com/oclif/cli/commit/8183ae5))\n* semantic-release bin ([6addee5](https://github.com/oclif/cli/commit/6addee5))\n* semantic-release bin ([ba77257](https://github.com/oclif/cli/commit/ba77257))\n* semantic-release bin ([2fcef58](https://github.com/oclif/cli/commit/2fcef58))\n* semantic-release bin ([813d91b](https://github.com/oclif/cli/commit/813d91b))\n* send args to yarn ([3d851ba](https://github.com/oclif/cli/commit/3d851ba))\n* share cache in tests ([1a33371](https://github.com/oclif/cli/commit/1a33371))\n* simplified bin scripts ([8b36b61](https://github.com/oclif/cli/commit/8b36b61))\n* slight performance boost in appveyor ([314398b](https://github.com/oclif/cli/commit/314398b))\n* smoke tests first ([e324c14](https://github.com/oclif/cli/commit/e324c14))\n* sort plugins if exists ([b729296](https://github.com/oclif/cli/commit/b729296))\n* sort plugins if exists ([8d8fe71](https://github.com/oclif/cli/commit/8d8fe71))\n* tests should use chai ([16fdb7b](https://github.com/oclif/cli/commit/16fdb7b))\n* trap git push errors ([af327b3](https://github.com/oclif/cli/commit/af327b3))\n* unescape commit message ([86c4556](https://github.com/oclif/cli/commit/86c4556))\n* updated config ([40d2500](https://github.com/oclif/cli/commit/40d2500))\n* updated config ([76c5225](https://github.com/oclif/cli/commit/76c5225))\n* updated deps ([7d6f276](https://github.com/oclif/cli/commit/7d6f276))\n* updated deps ([c6e576a](https://github.com/oclif/cli/commit/c6e576a))\n* updated deps ([f2d3b5d](https://github.com/oclif/cli/commit/f2d3b5d))\n* updated deps ([9c5e204](https://github.com/oclif/cli/commit/9c5e204))\n* updated deps ([05d0bf0](https://github.com/oclif/cli/commit/05d0bf0))\n* updated dev-cli ([587e09a](https://github.com/oclif/cli/commit/587e09a))\n* updated generator ([608d365](https://github.com/oclif/cli/commit/608d365))\n* updated generator ([906425e](https://github.com/oclif/cli/commit/906425e))\n* updated generator ([08e5c6b](https://github.com/oclif/cli/commit/08e5c6b))\n* updated generator ([4426bd9](https://github.com/oclif/cli/commit/4426bd9))\n* updates from config/command ([b2d0733](https://github.com/oclif/cli/commit/b2d0733))\n* use -o for manifest building ([2171042](https://github.com/oclif/cli/commit/2171042))\n* use [@anycli](https://github.com/anycli)/errors ([82530fd](https://github.com/oclif/cli/commit/82530fd))\n* use [@anycli](https://github.com/anycli)/errors ([348d7e8](https://github.com/oclif/cli/commit/348d7e8))\n* use [@anycli](https://github.com/anycli)/errors ([90afa69](https://github.com/oclif/cli/commit/90afa69))\n* use git add when not using semantic release ([b66c1e9](https://github.com/oclif/cli/commit/b66c1e9))\n* use latest generator-dxcli ([5bd5145](https://github.com/oclif/cli/commit/5bd5145))\n* use name in concurrently ([1384b55](https://github.com/oclif/cli/commit/1384b55))\n* use new circle config ([2c9d9c7](https://github.com/oclif/cli/commit/2c9d9c7))\n* use new linter rules ([2fbd4f9](https://github.com/oclif/cli/commit/2fbd4f9))\n* use new plugins ([7c58310](https://github.com/oclif/cli/commit/7c58310))\n* use new test format ([67fe975](https://github.com/oclif/cli/commit/67fe975))\n* use simpler command/config setup ([b5b7377](https://github.com/oclif/cli/commit/b5b7377))\n* use strings instead of variable ([8e8b9a7](https://github.com/oclif/cli/commit/8e8b9a7))\n* use stylist tslint formatter ([9542a1f](https://github.com/oclif/cli/commit/9542a1f))\n* use tmp dir on CI ([c98a8e4](https://github.com/oclif/cli/commit/c98a8e4))\n* use yarn, not yarn install ([ff61a7f](https://github.com/oclif/cli/commit/ff61a7f))\n\n### Features\n\n* add bin script for local plugin dev ([552df43](https://github.com/oclif/cli/commit/552df43))\n* add cache step to circleci ([fcef9fa](https://github.com/oclif/cli/commit/fcef9fa))\n* add cache step to circleci ([3efa4c5](https://github.com/oclif/cli/commit/3efa4c5))\n* add manifest to package ([2fc517c](https://github.com/oclif/cli/commit/2fc517c))\n* add mocha script ([4efb9ab](https://github.com/oclif/cli/commit/4efb9ab))\n* added --help and --version ([07e5a96](https://github.com/oclif/cli/commit/07e5a96))\n* added command generator ([f6fef1f](https://github.com/oclif/cli/commit/f6fef1f))\n* added generator ([6e96584](https://github.com/oclif/cli/commit/6e96584))\n* added help and not-found plugins to multi ([844ab18](https://github.com/oclif/cli/commit/844ab18))\n* generator-dxcli@1.10.5 ([bcea220](https://github.com/oclif/cli/commit/bcea220))\n* generator-dxcli@1.11.0 ([58b907c](https://github.com/oclif/cli/commit/58b907c))\n* generator-dxcli@1.11.1 ([847459d](https://github.com/oclif/cli/commit/847459d))\n* generator-dxcli@1.11.2 ([3eb57a4](https://github.com/oclif/cli/commit/3eb57a4))\n* generator-dxcli@1.11.3 ([63f333d](https://github.com/oclif/cli/commit/63f333d))\n* generator-dxcli@1.11.4 ([9aed3c0](https://github.com/oclif/cli/commit/9aed3c0))\n* generator-dxcli@1.11.5 ([04e4ce1](https://github.com/oclif/cli/commit/04e4ce1))\n* generator-dxcli@1.12.0 ([6dca853](https://github.com/oclif/cli/commit/6dca853))\n* generator-dxcli@1.12.1 ([87c3b50](https://github.com/oclif/cli/commit/87c3b50))\n* generator-dxcli@1.12.2 ([3665a3e](https://github.com/oclif/cli/commit/3665a3e))\n* generator-dxcli@1.12.3 ([b7bcf58](https://github.com/oclif/cli/commit/b7bcf58))\n* generator-dxcli@1.13.0 ([0240fe8](https://github.com/oclif/cli/commit/0240fe8))\n* generator-dxcli@1.14.0 ([792ba5a](https://github.com/oclif/cli/commit/792ba5a))\n* generator-dxcli@1.14.1 ([a29e7a1](https://github.com/oclif/cli/commit/a29e7a1))\n* generator-dxcli@1.14.2 ([e6c0d87](https://github.com/oclif/cli/commit/e6c0d87))\n* generator-dxcli@1.14.3 ([b1b8ac3](https://github.com/oclif/cli/commit/b1b8ac3))\n* generator-dxcli@1.14.4 ([d2a61f9](https://github.com/oclif/cli/commit/d2a61f9))\n* generator-dxcli@1.15.0 ([fdd7270](https://github.com/oclif/cli/commit/fdd7270))\n* generator-dxcli@1.15.2 ([8b1d993](https://github.com/oclif/cli/commit/8b1d993))\n* generator-dxcli@1.15.3 ([338f439](https://github.com/oclif/cli/commit/338f439))\n* make examples follow version of create-dxcli ([de2b9fc](https://github.com/oclif/cli/commit/de2b9fc))\n* release examples ([2f901ca](https://github.com/oclif/cli/commit/2f901ca))\n* release examples automatically ([6c95bd9](https://github.com/oclif/cli/commit/6c95bd9))\n* rename package to [@anycli](https://github.com/anycli)/cli ([ec28865](https://github.com/oclif/cli/commit/ec28865))\n* simplified package-scripts ([f31fae9](https://github.com/oclif/cli/commit/f31fae9))\n* updated generator ([13e5829](https://github.com/oclif/cli/commit/13e5829))\n* updated generator ([87b5922](https://github.com/oclif/cli/commit/87b5922))\n* updated generator-dxcli ([b2be420](https://github.com/oclif/cli/commit/b2be420))\n* use new parser model ([7e7f7d3](https://github.com/oclif/cli/commit/7e7f7d3))", 870 | "tree": { 871 | "url": "https://api.github.com/repos/oclif/oclif/git/trees/d83ffc44f381467cf7aacbe9f6284da6fe5f2055", 872 | "sha": "d83ffc44f381467cf7aacbe9f6284da6fe5f2055" 873 | }, 874 | "comment_count": 0 875 | }, 876 | "author": null, 877 | "committer": null, 878 | "parents": [ 879 | { 880 | "url": "https://api.github.com/repos/oclif/oclif/commits/cb1ab3507bc44d6039a4b9ce226a8ba7e5e4f247", 881 | "html_url": "https://github.com/oclif/oclif/commit/cb1ab3507bc44d6039a4b9ce226a8ba7e5e4f247", 882 | "sha": "cb1ab3507bc44d6039a4b9ce226a8ba7e5e4f247" 883 | } 884 | ], 885 | "repository": { 886 | "id": 117152902, 887 | "node_id": "MDEwOlJlcG9zaXRvcnkxMTcxNTI5MDI=", 888 | "name": "oclif", 889 | "full_name": "oclif/oclif", 890 | "private": false, 891 | "owner": { 892 | "login": "oclif", 893 | "id": 35349060, 894 | "node_id": "MDEyOk9yZ2FuaXphdGlvbjM1MzQ5MDYw", 895 | "avatar_url": "https://avatars3.githubusercontent.com/u/35349060?v=4", 896 | "gravatar_id": "", 897 | "url": "https://api.github.com/users/oclif", 898 | "html_url": "https://github.com/oclif", 899 | "followers_url": "https://api.github.com/users/oclif/followers", 900 | "following_url": "https://api.github.com/users/oclif/following{/other_user}", 901 | "gists_url": "https://api.github.com/users/oclif/gists{/gist_id}", 902 | "starred_url": "https://api.github.com/users/oclif/starred{/owner}{/repo}", 903 | "subscriptions_url": "https://api.github.com/users/oclif/subscriptions", 904 | "organizations_url": "https://api.github.com/users/oclif/orgs", 905 | "repos_url": "https://api.github.com/users/oclif/repos", 906 | "events_url": "https://api.github.com/users/oclif/events{/privacy}", 907 | "received_events_url": "https://api.github.com/users/oclif/received_events", 908 | "type": "Organization", 909 | "site_admin": false 910 | }, 911 | "html_url": "https://github.com/oclif/oclif", 912 | "description": "Node.js Open CLI Framework. Built with 💜 by Heroku.", 913 | "fork": false, 914 | "url": "https://api.github.com/repos/oclif/oclif", 915 | "forks_url": "https://api.github.com/repos/oclif/oclif/forks", 916 | "keys_url": "https://api.github.com/repos/oclif/oclif/keys{/key_id}", 917 | "collaborators_url": "https://api.github.com/repos/oclif/oclif/collaborators{/collaborator}", 918 | "teams_url": "https://api.github.com/repos/oclif/oclif/teams", 919 | "hooks_url": "https://api.github.com/repos/oclif/oclif/hooks", 920 | "issue_events_url": "https://api.github.com/repos/oclif/oclif/issues/events{/number}", 921 | "events_url": "https://api.github.com/repos/oclif/oclif/events", 922 | "assignees_url": "https://api.github.com/repos/oclif/oclif/assignees{/user}", 923 | "branches_url": "https://api.github.com/repos/oclif/oclif/branches{/branch}", 924 | "tags_url": "https://api.github.com/repos/oclif/oclif/tags", 925 | "blobs_url": "https://api.github.com/repos/oclif/oclif/git/blobs{/sha}", 926 | "git_tags_url": "https://api.github.com/repos/oclif/oclif/git/tags{/sha}", 927 | "git_refs_url": "https://api.github.com/repos/oclif/oclif/git/refs{/sha}", 928 | "trees_url": "https://api.github.com/repos/oclif/oclif/git/trees{/sha}", 929 | "statuses_url": "https://api.github.com/repos/oclif/oclif/statuses/{sha}", 930 | "languages_url": "https://api.github.com/repos/oclif/oclif/languages", 931 | "stargazers_url": "https://api.github.com/repos/oclif/oclif/stargazers", 932 | "contributors_url": "https://api.github.com/repos/oclif/oclif/contributors", 933 | "subscribers_url": "https://api.github.com/repos/oclif/oclif/subscribers", 934 | "subscription_url": "https://api.github.com/repos/oclif/oclif/subscription", 935 | "commits_url": "https://api.github.com/repos/oclif/oclif/commits{/sha}", 936 | "git_commits_url": "https://api.github.com/repos/oclif/oclif/git/commits{/sha}", 937 | "comments_url": "https://api.github.com/repos/oclif/oclif/comments{/number}", 938 | "issue_comment_url": "https://api.github.com/repos/oclif/oclif/issues/comments{/number}", 939 | "contents_url": "https://api.github.com/repos/oclif/oclif/contents/{+path}", 940 | "compare_url": "https://api.github.com/repos/oclif/oclif/compare/{base}...{head}", 941 | "merges_url": "https://api.github.com/repos/oclif/oclif/merges", 942 | "archive_url": "https://api.github.com/repos/oclif/oclif/{archive_format}{/ref}", 943 | "downloads_url": "https://api.github.com/repos/oclif/oclif/downloads", 944 | "issues_url": "https://api.github.com/repos/oclif/oclif/issues{/number}", 945 | "pulls_url": "https://api.github.com/repos/oclif/oclif/pulls{/number}", 946 | "milestones_url": "https://api.github.com/repos/oclif/oclif/milestones{/number}", 947 | "notifications_url": "https://api.github.com/repos/oclif/oclif/notifications{?since,all,participating}", 948 | "labels_url": "https://api.github.com/repos/oclif/oclif/labels{/name}", 949 | "releases_url": "https://api.github.com/repos/oclif/oclif/releases{/id}", 950 | "deployments_url": "https://api.github.com/repos/oclif/oclif/deployments" 951 | }, 952 | "score": 7.7055984 953 | } 954 | ] 955 | } 956 | --------------------------------------------------------------------------------