Skip to content

Commit

Permalink
Merge pull request #49 from readmeio/feature/qol-improvements
Browse files Browse the repository at this point in the history
🌺 Quality of life improvements
  • Loading branch information
erunion authored Aug 2, 2019
2 parents 33d4cc1 + 84f819d commit 5213eba
Show file tree
Hide file tree
Showing 30 changed files with 1,371 additions and 480 deletions.
7 changes: 7 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,16 @@ jobs:
- image: circleci/node:10
environment: *environment

node12:
<<: *default
docker:
- image: circleci/node:12
environment: *environment

workflows:
version: 2
build:
jobs:
- node8
- node10
- node12
108 changes: 60 additions & 48 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,122 +1,134 @@
# `rdme` - ReadMe's CLI

[![](https://d3vv6lp55qjaqc.cloudfront.net/items/1M3C3j0I0s0j3T362344/Untitled-2.png)](https://readme.io)
[![](https://d3vv6lp55qjaqc.cloudfront.net/items/1M3C3j0I0s0j3T362344/Untitled-2.png)](https://readme.com)

[![CircleCI](https://circleci.com/gh/readmeio/rdme.svg?style=svg)](https://circleci.com/gh/readmeio/rdme)
[![npm](https://img.shields.io/npm/v/rdme)](https://npm.im/rdme) [![CircleCI](https://circleci.com/gh/readmeio/rdme.svg?style=svg)](https://circleci.com/gh/readmeio/rdme)

### Table of Contents
* [What is rdme?](#about-rdme)
* [Configuration](#installation)
* [Installation](#installation)
* [Login](#logging-in-to-a-readme-project)
* [Usage](#usage)
* [Flags](#rdme-flags)
* [Swagger](#swagger)
* [Common options](#common-rdme-options)
* [Swagger / OpenAPI](#swagger-/-openapi)
* [Docs](#docs)
* [Versions](#versions)
* [Opening A Project Spec](#open)
* [Opening a Project](#open)
* [Future](#future)

### About `rdme`
`rdme` is the CLI wrapper for [ReadMe's RESTful API](https://readme.readme.io/v2.0/reference). It allows you to upload and edit [Swagger](https://swagger.io/) and [OAS](https://swagger.io/specification/) files associated with projects you create on [readme](https://readme.com/). Additionally, you can sync documentation with your project, and manage project versions.
`rdme` is the CLI wrapper for [ReadMe's RESTful API](https://readme.readme.io/v2.0/reference). It allows you to upload and edit [Swagger](https://swagger.io/) and [OAS](https://swagger.io/specification/) files associated with projects you create on [ReadMe](https://readme.com/). Additionally, you can sync documentation with your project, and manage project versions.

## Configuration
### Installation
```sh
npm install rdme
```
### Logging in to a ReadMe project

If you login to a project, you will not have to provide the `--key` option because we save it locally:
### Authentication
If you authenticate `rdme` to your ReadMe project, we will save your API key to a local configuration file (`~/.config/configstore/rdme-production.json`) so you will not have to provide the `--key` option to commands that require it.

```sh
rdme login
```

## Usage
### `rdme` flags
```
swaggerfile.json || oasfile.yaml
--key # API key associated with your ReadMe project
--version # The version
--id # The id of a OAS file previously uploaded. This is available through uploading a new file through this CLI
--fork # The semantic version which you'd like to fork from
--codename # The codename or nickname for a particular version
--main # Should this version be the primary (default) version for your project?
--beta # Is this version in beta?
--isPublic # Would you like to make this version public? Any primary version must be public
```
If you wish to get more information about any command within `rdme`, you can execute `rdme help <command>` or `rdme <command> --help`. You an also execute `rdme help` to see a global list of commands that `rdme` offers.

### Common `rdme` options
* `--key <string>`: The API key associated with your ReadMe project. You can obtain this from your dashboard, or alternatively if you log in with `rdme login`, we will save your API key to a local configuration file (`~/.config/configstore/rdme-production.json`), saving you the hassle of having to supply this argument on commands that have it.
* `--version <string>`: Your project version.

### Swagger / OpenAPI
ReadMe supports both [Swagger 2.0](https://swagger.io/docs/specification/2-0/basic-structure/) and [OpenAPI 3.0](https://swagger.io/docs/specification/about/).

#### Uploading a new API description to ReadMe
This will upload `path-to-swagger.json` to your project and return an ID and URL for you to later update your file, and view it in the client.

### Swagger
#### Uploading a new Swagger file to ReadMe
This will return an id and url for you to update your file and view it in the client, respectively
```sh
rdme swagger {path-to-swagger.json} --key={api-key}
rdme swagger [path-to-file.json]
```

#### Editing an existing Swagger file
#### Editing (resync) an existing API description
This will edit (resync) an existing API description (identified by `--id`) within your ReadMe project.

```sh
rdme swagger {path-to-swagger.json} --key={api-key} --id={existing-id}
rdme swagger [path-to-file.json] --id={existing-id}
```

#### Uploading or editing a swagger file including with specified version
#### Uploading or editing an API description in a project version
You can additional include a version flag, specifying the target version for your file's destination

```sh
rdme swagger {path-to-swagger.json} --key={api-key} --version={project-version}
rdme swagger [path-to-file.json] --version={project-version}
```

#### Omitting the file path
If you run `rdme` within a directory that contains your Swagger or OAS file, you can omit the file path.
Be sure to use one of the following file names: `swagger.json`, `swagger.yaml`, `openapi.json`, `openapi.yaml` if you do.
```sh
rdme swagger --key={api-key}
rdme swagger [path-to-file.json] --id={existing-id} --version={project-version}
```

### Docs
#### Syncing a folder of markdown docs to ReadMe
#### Omitting the file path
If you run `rdme` within a directory that contains your Swagger or OAS file, you can omit the file path. We will then look for a file with the following names, and upload that: `swagger.json`, `swagger.yaml`, `openapi.json`, and `openapi.yaml`

```sh
rdme docs path-to-markdown-files --key={api-key} --version={project-version}
rdme swagger
```

#### Edit a single readme doc on your local machine
### Docs
#### Syncing a folder of Markdown docs to ReadMe
```sh
rdme docs path-to-markdown-files --version={project-version}
```

#### Edit a single ReadMe doc on your local machine
```sh
rdme docs:edit <slug> --key={api-key} --version={project-version}
rdme docs:edit <slug> --version={project-version}
```

### Versions
#### Get all versions associated with your project
```sh
rdme versions --key={api-key}
rdme versions
```

If you wish to see the raw output from our API in this response, supply the `--raw` flag.

#### Get all information about a particular version
```sh
rdme versions --key={api-key} --version={project-version}
rdme versions --version={project-version}
```

#### Create a new version using flags
If you wish to see the raw output from our API in this response, supply the `--raw` flag.

#### Create a new version
```sh
rdme versions:create --key={api-key} --version={project-version} --fork={version-fork} --codename={version-name} --main --beta
rdme versions:create <version>
```

#### Create a new version without flags
Creating a version without version-specific flags will allow the ReadMe CLI to prompt you with configuration options
##### Automating this process
If you wish to automate the process of creating a new project version, and not have the CLI prompt you for input, you can do so by supplying the necessary flags to `versions:create`.

For example:

```sh
rdme versions:create --key={api-key} --version={project-version}
rdme versions:create <version> --fork={version-fork} --codename={version-name} --main --beta
```

See `rdme versions:create --help` for a full list of flags.

#### Update a version
The command to update a version takes the same flags as creating a new version
```sh
rdme versions:update --key={api-key} --version={project-version}
rdme versions:update <version>
```

Like `versions:create`, if you wish to automate this process and not be blocked by CLI input, you can supply the necessary flags to this command. See `rdme versions:update --help` or [automating this process](#automating-this-process) for more information.

#### Delete a version
You can remove a specific version from your project, as well as all of the attached specs

```sh
rdme versions:delete --key={api-key} --version={project-version}
rdme versions:delete <version>
```

### Open your ReadMe project in your browser
Expand All @@ -127,4 +139,4 @@ rdme open
```

## Future
We are continually expanding and improving the offerings of this application as we expand our public API and are able. Some interactions may change over time.
We are continually expanding and improving the offerings of this application as we expand our public API and are able. Some interactions may change over time, but we will do our best to retain backwards compatibility.
108 changes: 90 additions & 18 deletions cli.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/* eslint-disable no-underscore-dangle */
const cliArgs = require('command-line-args');
const path = require('path');
// We have to do this otherwise `require('config')` loads
// from the cwd where the user is running `rdme` which
Expand All @@ -15,34 +17,104 @@ process.env.NODE_CONFIG_DIR = configDir;

const { version } = require('./package.json');
const configStore = require('./lib/configstore');
const help = require('./lib/help');
const commands = require('./lib/commands');

function load(command = '', subcommand = '') {
const file = path.join(__dirname, 'lib', command, subcommand);
try {
// eslint-disable-next-line global-require, import/no-dynamic-require
return require(file);
} catch (e) {
const error = new Error('Command not found.');
error.description = `Type ${`${config.cli} help`.yellow} to see all commands`;
throw error;
/**
* @param {Array} processArgv - An array of arguments from the current process. Can be used to mock
* fake CLI calls.
* @return {Promise}
*/
module.exports = processArgv => {
const mainArgs = [
{ name: 'help', alias: 'h', type: Boolean, description: 'Display this usage guide' },
{
name: 'version',
alias: 'v',
type: Boolean,
description: `Show the current ${config.cli} version`,
},
{ name: 'command', type: String, defaultOption: true },
];

const argv = cliArgs(mainArgs, { partial: true, argv: processArgv });
const cmd = argv.command || false;

// Add support for `-V` as an additional `--version` alias.
if (typeof argv._unknown !== 'undefined') {
if (argv._unknown.indexOf('-V') !== -1) {
argv.version = true;
}
}
}

module.exports = function(cmd, args, opts = {}) {
if (opts.version && (!cmd || cmd === 'help')) return Promise.resolve(version);
if (argv.version && (!cmd || cmd === 'help')) return Promise.resolve(version);

let command = cmd || '';
let subcommand;

if (command.includes(':')) {
[command, subcommand] = cmd.split(':');
if (!command) {
command = 'help';
}

const optsWithStoredKey = Object.assign({}, { key: configStore.get('apiKey') }, opts);
if (command === 'help') {
argv.help = true;
}

try {
return load(opts.help ? 'help' : command, subcommand).run({ args, opts: optsWithStoredKey });
let cmdArgv;
let bin;

// Handling for `rdme help` and `rdme help <command>` cases.
if (command === 'help') {
if ((argv._unknown || []).length === 0) {
return Promise.resolve(help.globalUsage(mainArgs));
}

if (argv._unknown.indexOf('-H') !== -1) {
return Promise.resolve(help.globalUsage(mainArgs));
}

cmdArgv = cliArgs([{ name: 'subcommand', type: String, defaultOption: true }], {
argv: argv._unknown,
});
if (!cmdArgv.subcommand) {
return Promise.resolve(help.globalUsage(mainArgs));
}

bin = commands.load(cmdArgv.subcommand);
return Promise.resolve(help.commandUsage(bin));
}

bin = commands.load(command);

// Handling for `rdme <command> --help`.
if (argv.help) {
return Promise.resolve(help.commandUsage(bin));
}

try {
cmdArgv = cliArgs(bin.args, { argv: argv._unknown || [] });
} catch (e) {
// If we have a command that has its own `--version` argument to accept data, that argument,
// if supplied in the `--version VERSION_STRING` format instead of `--version=VERSION_STRING`,
// will collide with the global version argument because their types differ and the argument
// parser gets confused.
//
// Instead of failing out to the user with an undecipherable "Unknown value: ..." error, let's
// try to parse their request again but a tad less eager.
if (e.name !== 'UNKNOWN_VALUE' || (e.name === 'UNKNOWN_VALUE' && !argv.version)) {
throw e;
}

cmdArgv = cliArgs(bin.args, { partial: true, argv: processArgv.slice(1) });
}

cmdArgv = Object.assign({}, { key: configStore.get('apiKey') }, cmdArgv);

return bin.run(cmdArgv);
} catch (e) {
if (e.message === 'Command not found.') {
e.description = `Type \`${`${config.cli} help`.yellow}\` ${`to see all commands`.red}`;
}

return Promise.reject(e);
}
};
Loading

0 comments on commit 5213eba

Please sign in to comment.