Replace `oclif` with `cli` (#744)

Summary:
This commit changes the CLI to use the code in `cli` instead of `oclif`.
A subsequent commit will remove the dependency on OClif altogether.
Resolves #580.

Test Plan:
Note that `yarn backend; node bin/sourcecred.js help` works. Note that
the documentation in the README is still correct.

wchargin-branch: cli-replace-oclif
This commit is contained in:
William Chargin 2018-09-02 16:11:56 -07:00 committed by GitHub
parent 17172c2d96
commit e71264f5cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 5 additions and 256 deletions

View File

@ -27,9 +27,7 @@ module.exports = {
// source file, and the key will be the filename of the bundled entry
// point within the build directory.
backendEntryPoints: {
sourcecred: resolveApp("src/oclif/sourcecred.js"),
"commands/load": resolveApp("src/oclif/commands/load.js"),
cli: resolveApp("src/cli/main.js"),
sourcecred: resolveApp("src/cli/main.js"),
//
fetchAndPrintGithubRepo: resolveApp(
"src/plugins/github/bin/fetchAndPrintGithubRepo.js"

View File

@ -130,12 +130,5 @@
"files": [
"/bin",
"/build"
],
"oclif": {
"commands": "./bin/commands",
"bin": "sourcecred",
"plugins": [
"@oclif/plugin-help"
]
}
]
}

View File

@ -1,8 +1,8 @@
// @flow
// The repoRegistry is written by the CLI load command
// (src/oclif/commands/load.js; src/cli/load.js) and is read by the
// RepositorySelect component (src/app/credExplorer/RepositorySelect.js)
// The repoRegistry is written by the CLI load command (src/cli/load.js)
// and is read by the RepositorySelect component
// (src/app/credExplorer/RepositorySelect.js)
import deepEqual from "lodash.isequal";
import {toCompat, fromCompat, type Compatible} from "../../util/compat";
import type {Repo} from "../../core/repo";

View File

@ -1,202 +0,0 @@
// @flow
import {Command, flags} from "@oclif/command";
import mkdirp from "mkdirp";
import path from "path";
import fs from "fs";
import stringify from "json-stable-stringify";
import {loadGithubData} from "../../plugins/github/loadGithubData";
import {loadGitData} from "../../plugins/git/loadGitData";
import {
pluginNames,
defaultPlugins,
nodeMaxOldSpaceSizeFlag,
sourcecredDirectoryFlag,
} from "../common";
import {repoToString, stringToRepo} from "../../core/repo";
import {
toJSON,
fromJSON,
addRepo,
emptyRegistry,
REPO_REGISTRY_FILE,
} from "../../app/credExplorer/repoRegistry";
const execDependencyGraph = require("../../tools/execDependencyGraph").default;
export default class PluginGraphCommand extends Command {
static description = "load data required for SourceCred";
static strict = false;
static args = [
{
name: "repos",
required: true,
description:
"GitHub repos to load (one per argument), represented as OWNER/NAME",
},
];
static flags = {
plugin: flags.string({
description:
"plugin whose data to load (loads default plugins if not set)",
required: false,
options: pluginNames(),
}),
output: flags.string({
description:
"the GitHub repo under which to store output; " +
"required unless exactly one repository is specified",
required: false,
}),
"sourcecred-directory": sourcecredDirectoryFlag(),
"max-old-space-size": nodeMaxOldSpaceSizeFlag(),
"github-token": flags.string({
description:
"a GitHub API token, as generated at " +
"https://github.com/settings/tokens/new" +
"; required only if using the GitHub plugin",
env: "SOURCECRED_GITHUB_TOKEN",
}),
};
async run() {
const {
argv,
flags: {
output: defaultOutput,
"github-token": githubToken,
"sourcecred-directory": basedir,
"max-old-space-size": maxOldSpaceSize,
plugin,
},
} = this.parse(PluginGraphCommand);
const repos = argv.map((s) => stringToRepo(s));
const outputRepo = (() => {
if (defaultOutput != null) {
return stringToRepo(defaultOutput);
} else if (repos.length === 1) {
return repos[0];
} else {
throw new Error("output repository not specified");
}
})();
if (!plugin) {
loadDefaultPlugins({
basedir,
plugin,
outputRepo,
repos,
githubToken,
maxOldSpaceSize,
});
} else {
loadPlugin({basedir, plugin, outputRepo, repos, githubToken});
}
}
}
function loadDefaultPlugins({
basedir,
outputRepo,
repos,
githubToken,
maxOldSpaceSize,
}) {
if (githubToken == null) {
// TODO: This check should be abstracted so that plugins can
// specify their argument dependencies and get nicely
// formatted errors.
console.error("fatal: No GitHub token specified. Try `--help'.");
process.exitCode = 1;
return;
}
const tasks = [
...defaultPlugins().map((pluginName) => ({
id: `load-${pluginName}`,
cmd: [
"node",
`--max_old_space_size=${maxOldSpaceSize}`,
"./bin/sourcecred.js",
"load",
...repos.map((repo) => repoToString(repo)),
"--plugin",
pluginName,
"--github-token",
githubToken,
"--output",
repoToString(outputRepo),
"-d",
basedir,
"--max-old-space-size",
maxOldSpaceSize,
],
deps: [],
})),
];
execDependencyGraph(tasks, {taskPassLabel: "DONE"}).then(({success}) => {
if (success) {
addToRepoRegistry({basedir, repo: outputRepo});
}
process.exitCode = success ? 0 : 1;
});
}
function loadPlugin({basedir, plugin, outputRepo, repos, githubToken}) {
function scopedDirectory(key) {
const directory = path.join(basedir, key, repoToString(outputRepo), plugin);
mkdirp.sync(directory);
return directory;
}
const outputDirectory = scopedDirectory("data");
const cacheDirectory = scopedDirectory("cache");
switch (plugin) {
case "github":
if (githubToken == null) {
// TODO: This check should be abstracted so that plugins can
// specify their argument dependencies and get nicely
// formatted errors.
console.error("fatal: No GitHub token specified. Try `--help'.");
process.exitCode = 1;
return;
} else {
loadGithubData({
token: githubToken,
repos,
outputDirectory,
cacheDirectory,
});
}
break;
case "git":
loadGitData({repos, outputDirectory, cacheDirectory});
break;
default:
console.error("fatal: Unknown plugin: " + (plugin: empty));
process.exitCode = 1;
return;
}
}
function addToRepoRegistry(options) {
// TODO: Make this function transactional before loading repositories in
// parallel.
const {basedir, repo} = options;
const outputFile = path.join(basedir, REPO_REGISTRY_FILE);
let registry = null;
if (fs.existsSync(outputFile)) {
const contents = fs.readFileSync(outputFile);
const registryJSON = JSON.parse(contents.toString());
registry = fromJSON(registryJSON);
} else {
registry = emptyRegistry();
}
registry = addRepo(repo, registry);
fs.writeFileSync(outputFile, stringify(toJSON(registry)));
}

View File

@ -1,35 +0,0 @@
// @flow
import {flags} from "@oclif/command";
import os from "os";
import path from "path";
export type PluginName = "github" | "git";
export function pluginNames(): PluginName[] {
return ["github", "git"];
}
export function defaultPlugins(): PluginName[] {
return ["github", "git"];
}
function defaultStorageDirectory() {
return path.join(os.tmpdir(), "sourcecred");
}
export function sourcecredDirectoryFlag() {
return flags.string({
char: "d",
description: "directory for storing graphs and other SourceCred data",
env: "SOURCECRED_DIRECTORY",
default: () => defaultStorageDirectory(),
});
}
export function nodeMaxOldSpaceSizeFlag() {
return flags.integer({
description: "--max_old_space_size flag to node; increases available heap",
default: 8192,
env: "SOURCECRED_NODE_MAX_OLD_SPACE",
});
}

View File

@ -1,5 +0,0 @@
// @flow
require("../tools/entry");
require("@oclif/command")
.run()
.catch(require("@oclif/errors/handle"));