mirror of
https://github.com/status-im/sourcecred.git
synced 2025-02-17 06:56:36 +00:00
Add skeleton of sourcecred analyze
(#942)
The `analyze` command is the first step towards #704 and #703. When fully implemented, it will run PageRank for a loaded repository, generating a complete graph and cred attribution. For now, this just adds a scaffold. It does basic argument parsing, and has help text, but the actual command is not yet implemented. Test plan: Unit tests verify that the analyze command is hooked into `sourcecred` and `sourcecred help`, and that it responds to the `--help` command and parses its arguments appropriately.
This commit is contained in:
parent
d3e79e3c4e
commit
542e2f9723
84
src/cli/analyze.js
Normal file
84
src/cli/analyze.js
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
// @flow
|
||||||
|
// Implementation of `sourcecred analyze`
|
||||||
|
|
||||||
|
import {stringToRepoId, repoIdToString, type RepoId} from "../core/repoId";
|
||||||
|
import dedent from "../util/dedent";
|
||||||
|
import type {Command} from "./command";
|
||||||
|
import * as Common from "./common";
|
||||||
|
|
||||||
|
function usage(print: (string) => void): void {
|
||||||
|
print(
|
||||||
|
dedent`\
|
||||||
|
usage: sourcecred analyze REPO_ID
|
||||||
|
sourcecred analyze --help
|
||||||
|
|
||||||
|
Analyze a loaded repository, generating a cred attribution for it.
|
||||||
|
|
||||||
|
REPO_ID refers to a GitHub repository in the form OWNER/NAME: for
|
||||||
|
example, torvalds/linux. The REPO_ID may be an 'aggregated
|
||||||
|
repository' generated via the \`--output\` flag to \`sourcecred
|
||||||
|
load\`
|
||||||
|
|
||||||
|
Note: This command is not yet implemented.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
REPO_ID
|
||||||
|
Repository to analyze
|
||||||
|
|
||||||
|
--help
|
||||||
|
Show this help message and exit, as 'sourcecred help analyze'.
|
||||||
|
|
||||||
|
Environment variables:
|
||||||
|
SOURCECRED_DIRECTORY
|
||||||
|
Directory owned by SourceCred, in which data, caches,
|
||||||
|
registries, etc. are stored. Optional: defaults to a
|
||||||
|
directory 'sourcecred' under your OS's temporary directory;
|
||||||
|
namely:
|
||||||
|
${Common.defaultSourcecredDirectory()}
|
||||||
|
`.trimRight()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function die(std, message) {
|
||||||
|
std.err("fatal: " + message);
|
||||||
|
std.err("fatal: run 'sourcecred help analyze' for help");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const analyze: Command = async (args, std) => {
|
||||||
|
let repoId: RepoId | null = null;
|
||||||
|
for (let i = 0; i < args.length; i++) {
|
||||||
|
switch (args[i]) {
|
||||||
|
case "--help": {
|
||||||
|
usage(std.out);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
// Should be a repository
|
||||||
|
if (repoId != null) {
|
||||||
|
return die(std, "multiple repositories provided");
|
||||||
|
}
|
||||||
|
repoId = stringToRepoId(args[i]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (repoId == null) {
|
||||||
|
return die(std, "repository not specified");
|
||||||
|
}
|
||||||
|
|
||||||
|
std.out(`would analyze ${repoIdToString(repoId)}, but not yet implemented`);
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const help: Command = async (args, std) => {
|
||||||
|
if (args.length === 0) {
|
||||||
|
usage(std.out);
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
usage(std.err);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default analyze;
|
81
src/cli/analyze.test.js
Normal file
81
src/cli/analyze.test.js
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
// @flow
|
||||||
|
|
||||||
|
import {run} from "./testUtil";
|
||||||
|
import analyze, {help} from "./analyze";
|
||||||
|
|
||||||
|
describe("cli/analyze", () => {
|
||||||
|
describe("'help' command", () => {
|
||||||
|
it("prints usage when given no arguments", async () => {
|
||||||
|
expect(await run(help, [])).toEqual({
|
||||||
|
exitCode: 0,
|
||||||
|
stdout: expect.arrayContaining([
|
||||||
|
expect.stringMatching(/^usage: sourcecred analyze/),
|
||||||
|
]),
|
||||||
|
stderr: [],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it("fails when given arguments", async () => {
|
||||||
|
expect(await run(help, ["foo/bar"])).toEqual({
|
||||||
|
exitCode: 1,
|
||||||
|
stdout: [],
|
||||||
|
stderr: expect.arrayContaining([
|
||||||
|
expect.stringMatching(/^usage: sourcecred analyze/),
|
||||||
|
]),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("'analyze' command", () => {
|
||||||
|
it("prints usage with '--help'", async () => {
|
||||||
|
expect(await run(analyze, ["--help"])).toEqual({
|
||||||
|
exitCode: 0,
|
||||||
|
stdout: expect.arrayContaining([
|
||||||
|
expect.stringMatching(/^usage: sourcecred analyze/),
|
||||||
|
]),
|
||||||
|
stderr: [],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("errors if no repository is specified", async () => {
|
||||||
|
expect(await run(analyze, [])).toEqual({
|
||||||
|
exitCode: 1,
|
||||||
|
stdout: [],
|
||||||
|
stderr: [
|
||||||
|
"fatal: repository not specified",
|
||||||
|
"fatal: run 'sourcecred help analyze' for help",
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("errors if multiple repositories are specified", async () => {
|
||||||
|
expect(await run(analyze, ["foo/bar", "zoink/zod"])).toEqual({
|
||||||
|
exitCode: 1,
|
||||||
|
stdout: [],
|
||||||
|
stderr: [
|
||||||
|
"fatal: multiple repositories provided",
|
||||||
|
"fatal: run 'sourcecred help analyze' for help",
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("errors if provided a invalid repository", async () => {
|
||||||
|
expect(await run(analyze, ["--zoomzoom"])).toEqual({
|
||||||
|
exitCode: 1,
|
||||||
|
stdout: [],
|
||||||
|
stderr: [
|
||||||
|
expect.stringContaining("Error: Invalid repo string: --zoomzoom"),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("prints a not-yet-implemented message for a valid repo", async () => {
|
||||||
|
expect(await run(analyze, ["sourcecred/example-github"])).toEqual({
|
||||||
|
exitCode: 0,
|
||||||
|
stdout: [
|
||||||
|
"would analyze sourcecred/example-github, but not yet implemented",
|
||||||
|
],
|
||||||
|
stderr: [],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -5,6 +5,7 @@ import type {Command} from "./command";
|
|||||||
import dedent from "../util/dedent";
|
import dedent from "../util/dedent";
|
||||||
|
|
||||||
import {help as loadHelp} from "./load";
|
import {help as loadHelp} from "./load";
|
||||||
|
import {help as analyzeHelp} from "./analyze";
|
||||||
|
|
||||||
const help: Command = async (args, std) => {
|
const help: Command = async (args, std) => {
|
||||||
if (args.length === 0) {
|
if (args.length === 0) {
|
||||||
@ -15,6 +16,7 @@ const help: Command = async (args, std) => {
|
|||||||
const subHelps: {[string]: Command} = {
|
const subHelps: {[string]: Command} = {
|
||||||
help: metaHelp,
|
help: metaHelp,
|
||||||
load: loadHelp,
|
load: loadHelp,
|
||||||
|
analyze: analyzeHelp,
|
||||||
};
|
};
|
||||||
if (subHelps[command] !== undefined) {
|
if (subHelps[command] !== undefined) {
|
||||||
return subHelps[command](args.slice(1), std);
|
return subHelps[command](args.slice(1), std);
|
||||||
@ -32,6 +34,7 @@ function usage(print: (string) => void): void {
|
|||||||
|
|
||||||
Commands:
|
Commands:
|
||||||
load load repository data into SourceCred
|
load load repository data into SourceCred
|
||||||
|
analyze analyze cred for a loaded repository
|
||||||
help show this help message
|
help show this help message
|
||||||
|
|
||||||
Use 'sourcecred help COMMAND' for help about an individual command.
|
Use 'sourcecred help COMMAND' for help about an individual command.
|
||||||
|
@ -35,6 +35,16 @@ describe("cli/help", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("prints help about 'sourcecred analyze'", async () => {
|
||||||
|
expect(await run(help, ["analyze"])).toEqual({
|
||||||
|
exitCode: 0,
|
||||||
|
stdout: expect.arrayContaining([
|
||||||
|
expect.stringMatching(/^usage: sourcecred analyze/),
|
||||||
|
]),
|
||||||
|
stderr: [],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("fails when given an unknown command", async () => {
|
it("fails when given an unknown command", async () => {
|
||||||
expect(await run(help, ["wat"])).toEqual({
|
expect(await run(help, ["wat"])).toEqual({
|
||||||
exitCode: 1,
|
exitCode: 1,
|
||||||
|
@ -7,6 +7,7 @@ import {VERSION_SHORT} from "../app/version";
|
|||||||
|
|
||||||
import help from "./help";
|
import help from "./help";
|
||||||
import load from "./load";
|
import load from "./load";
|
||||||
|
import analyze from "./analyze";
|
||||||
|
|
||||||
const sourcecred: Command = async (args, std) => {
|
const sourcecred: Command = async (args, std) => {
|
||||||
if (args.length === 0) {
|
if (args.length === 0) {
|
||||||
@ -22,6 +23,8 @@ const sourcecred: Command = async (args, std) => {
|
|||||||
return help(args.slice(1), std);
|
return help(args.slice(1), std);
|
||||||
case "load":
|
case "load":
|
||||||
return load(args.slice(1), std);
|
return load(args.slice(1), std);
|
||||||
|
case "analyze":
|
||||||
|
return analyze(args.slice(1), std);
|
||||||
default:
|
default:
|
||||||
std.err("fatal: unknown command: " + JSON.stringify(args[0]));
|
std.err("fatal: unknown command: " + JSON.stringify(args[0]));
|
||||||
std.err("fatal: run 'sourcecred help' for commands and usage");
|
std.err("fatal: run 'sourcecred help' for commands and usage");
|
||||||
|
@ -13,6 +13,7 @@ function mockCommand(name) {
|
|||||||
|
|
||||||
jest.mock("./help", () => mockCommand("help"));
|
jest.mock("./help", () => mockCommand("help"));
|
||||||
jest.mock("./load", () => mockCommand("load"));
|
jest.mock("./load", () => mockCommand("load"));
|
||||||
|
jest.mock("./analyze", () => mockCommand("analyze"));
|
||||||
|
|
||||||
describe("cli/sourcecred", () => {
|
describe("cli/sourcecred", () => {
|
||||||
it("fails with usage when invoked with no arguments", async () => {
|
it("fails with usage when invoked with no arguments", async () => {
|
||||||
@ -55,6 +56,14 @@ describe("cli/sourcecred", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("responds to 'analyze'", async () => {
|
||||||
|
expect(await run(sourcecred, ["analyze", "foo/bar", "foo/baz"])).toEqual({
|
||||||
|
exitCode: 2,
|
||||||
|
stdout: ['out(analyze): ["foo/bar","foo/baz"]'],
|
||||||
|
stderr: ["err(analyze)"],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("fails given an unknown command", async () => {
|
it("fails given an unknown command", async () => {
|
||||||
expect(await run(sourcecred, ["wat"])).toEqual({
|
expect(await run(sourcecred, ["wat"])).toEqual({
|
||||||
exitCode: 1,
|
exitCode: 1,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user