Commit Graph

774 Commits

Author SHA1 Message Date
Dandelion Mané 9d24190c03
GH Porcelain: add `Repository.from` (#257)
I think the absence of this method when I added the `Repository` class
was a bug.

Test plan: There are new unit tests.
2018-05-10 11:24:58 -07:00
Dandelion Mané 5c44dd0373
GH Porcelain: Move `authors` top-level Porcelain (#254)
Currently, the `authors` method is attached at the Repository level.
This is incorrect; it actually finds all the authors in the graph, not
all the authors of that repository. This commit moves the method to the
correct class.

Test plan: This function is only used in test code. The tests still
pass.
2018-05-10 11:24:24 -07:00
William Chargin 61d3cb3f52
Implement basic PageRank analysis (#252)
Summary:
We don’t expect the results to be of good quality right now. Rather,
this gives us a starting point from which to iterate the algorithm.

The convergence criterion also needs to be adjusted. (In particular, it
should almost certainly not be a constant.)

Test Plan:
Run `yarn start`. Select a graph, like `sourcecred/example-github`. Open
the JS console and click “Run basic PageRank”. Watch the console.

wchargin-branch: basic-pagerank
2018-05-10 11:21:18 -07:00
William Chargin 8e4668cc91
Fetch generated graphs on the frontend (#251)
Summary:
This commit enables the cred explorer to fetch pre-generated graphs. The
form has poor UX, but gets the job done. (To do later: saving in
localStorage, allowing fetching the list of possible graphs, linking the
submit button so that it’s triggered by `Enter`, etc.).

Test Plan:
Set up with `node ./bin/sourcecred.js graph sourcecred sourcecred`, then
run `yarn start` and check the following cases:
  - success case: `sourcecred`/`sourcecred`
  - error case: `sarcecrod`/`sarcecrod` (nice console error)
  - error case: the repository owner or name is an empty string or has
    invalid characters, like `../../secret.txt` (nice console error)

wchargin-branch: fetch-graphs
2018-05-09 12:47:56 -07:00
Dandelion Mané 6ca4f77b6d
Add dependency on tfjs-core (#250) 2018-05-09 10:22:48 -07:00
William Chargin cb1339a0a7
Start a production server from `sourcecred start` (#247)
Summary:
This commit changes `yarn start` to run a production version of the API
server, which serves static files from a pre-built directory and also
handles API requests.

Test Plan:
```shell
$ yarn build
$ yarn backend
$ node ./bin/sourcecred.js start -d /tmp/srccrd &
$ mkdir -p /tmp/srccrd
$ echo hello >/tmp/srccrd/world
$ curl -s localhost:4000/ | file -
/dev/stdin: HTML document, ASCII text, with very long lines, with no line terminators
$ curl -s localhost:4000/api/v1/data/world
hello
```

wchargin-branch: cli-start-prod-server
2018-05-08 22:00:36 -07:00
Dandelion Mané ac8d0ff66c
Setup credExplorer app scaffold (#249)
Test plan: Run `yarn start`, and observe that the Cred Explorer is now
included in the nav bar, and can be selected, in which case a short
message is displayed.
2018-05-08 17:05:56 -07:00
Dandelion Mané 0c59435a2b
Move testUtil.js into src/app (#248)
testUtil contains some useful configuration endpoints for our frontend
testing. This commit moves it out of the artifact editor and up to
`src/app`.

Test plan: `yarn travis` passes.
2018-05-08 17:03:01 -07:00
Dandelion Mané d9b4673dbd
Factor out `github.porcelain.asEntity` (#246)
@wchargin suggested that the entity-wrapping logic in porcelain
reference handling should be factored out as its own method. This was a
great suggestion; it will be very useful for plugin consumers, and it
also results in better test coverage.

Test plan: The new unit tests are nice. For your own safety, do not
question or quibble with the magnificent foo plugin.
2018-05-08 16:33:19 -07:00
William Chargin 9ea1f981aa
Proxy Webpack dev server through to an API server (#245)
Summary:
This way, our frontend can talk to a backend that can read from the
filesystem (among other things).

Paired with @decentralion.

Test Plan:
```
$ yarn backend
$ SOURCECRED_DIRECTORY=/tmp/srccrd yarn start
$ # verify that the browser looks good
$ mkdir /tmp/srccrd
$ echo hello >/tmp/srccrd/world
$ curl localhost:3000/api/v1/data/world
hello
$ curl localhost:4000/api/v1/data/world
hello
```

wchargin-branch: webpack-proxy
2018-05-08 16:09:37 -07:00
Dandelion Mané 62b9f70d00
Remove the old `experiments` directory (#244) 2018-05-08 15:32:37 -07:00
Dandelion Mané 3166c2a56c
Turn on flow for config/env.js (#243)
It was doing some clever array construction that added possible booleans
to the array, then filtered them out. To make the typing simpler for
Flow's inspection, we now only put string elements in the array.

Test plan: `yarn travis --full` passed, and the CLI still works.
2018-05-08 14:56:06 -07:00
William Chargin 2e8653a3ee
Turn on flow for scripts/start.js (#242)
Summary:
  - The value of `process.stdout.isTTY` is either `true` or `undefined`.
    Flow (reasonably) dislikes this, so we add an explicit check.
  - More `package.json` burnination.

Test Plan:
Note that `require("./package.json").proxy === undefined` in the Node
console, and that `yarn start` works.

wchargin-branch: flow--scripts-start
2018-05-08 14:51:55 -07:00
Dandelion Mané 824df7e916
Turn on flow for scripts/{backend,build,test}.js (#241)
- scripts/backend.js: We incorrectly set an environment variable to
a boolean, when in fact it must be a string. Fixed it to set a string
value "true", and updated usage in config/babel.js
- scripts/test.js: No changes
- scripts/build.js: Removed a call to printHostingInstructions, so that
we don't need to require the package.json.

Test plan:
`yarn travis --full` passes, and the SourceCred cli still works.
2018-05-08 14:35:56 -07:00
Dandelion Mané 1647c1abac
Fix flow errors in fetchAndPrintGithubRepo.js (#240)
Fixing the flow error corresponded to (correctly) documenting that the
GitHub token is mandatory, not optional.

Test plan: `yarn travis --full` still passes.
2018-05-08 14:24:11 -07:00
Dandelion Mané d34503799c
Enable flow: sourcecred.js and editor/App.test.js (#239)
They were already correct from a typing perspective, so no other changes
needed.
2018-05-08 14:21:26 -07:00
Dandelion Mané d221a933d8
Fix flow errors in paths.js (#238)
- Fix accidental string-to-NaN coercion in ensureSlash
- Don't dynamically require package.json; to determine public url, just
use the environment variable or "/"

Test plan: `yarn start` and travis still work
2018-05-08 14:21:19 -07:00
William Chargin 7d9a98128d
Extract a generic `LocalStore` module (#235)
Summary:
This way, different plugins can have `LocalStore`s with different cache
keys.

Test Plan:
Note that the app (`yarn start`) preserves the local store values from
before this change, and that updates continue to work.

wchargin-branch: generic-localstore
2018-05-08 13:40:48 -07:00
Dandelion Mané 372f8f9bd6 Setup routing within App.js
This commit modifies App.js to use routing, such that it's possible to
navigate between a home screen, and the Artifact Editor.

Test plan: Run `yarn start`, and navigate between the home screen and
artifact editor. Verify that the artifact editor still works as
expected.
2018-05-08 12:55:38 -07:00
Dandelion Mané 0149d74971 Add react-router-dom
This commit adds a npm and flow-typed dependency, with no functional
change.

Test plan: `yarn travis` passes.
2018-05-08 12:55:38 -07:00
Dandelion Mané e1808d1126 Add src/app/App.js
This commit adds src/app/App.js, which proxies in the frontend from
src/plugins/artifact/editor/App.js. The observed behavior (run `yarn
start`; see Artifact Editor) is unchanged.

Test plan: Observe that `yarn start` has the same behavior, and travis
passes.
2018-05-08 12:55:38 -07:00
Dandelion Mané 63351e6149 Move app scaffolding to src/app
This commit executes a micro-refactor to move all top-level app setup
code out of src/plugins/artifact/editor and into src/app. The observed
behavior from `yarn start`, which is to show the artifact editor, is
unchanged.
2018-05-08 12:55:38 -07:00
Dandelion Mané c2fb88b11a Turn on flow for index.js
Test plan: `yarn travis` passes
2018-05-08 12:55:38 -07:00
William Chargin 57682065fd
Add `sourcecred start` (#234)
Summary:
We need a way for our web applications to interact with data on the
filesystem. In this commit, we introduce a webserver that serves
statically from two directory trees: first, the result of a live-updated
Webpack build; second, the SourceCred data directory.

Test Plan:
Run `yarn backend` and `node ./bin/sourcecred.js start`. When ready,
navigate to the server’s root route in a web browser. Note that a nice
React app is displayed. Then, change something in that React app source.
Note that the server console displays Webpack’s update messages, and
that refreshing the page in the browser renders the new version of the
app. Finally, visit

    /__data__/graphs/sourcecred/example-github/graph.json

in the browser to see the graph for the example repository, assuming
that you had generated its graph previously.

wchargin-branch: start
2018-05-07 20:10:49 -07:00
William Chargin 18ddbfff3e
Add dependency on express (#233)
wchargin-branch: express
2018-05-07 20:05:52 -07:00
Dandelion Mané 93e2798f37
Ensure that flow is used in all js files (#232)
This script ensures that either //@flow or //@no-flow is present in
every js file. Every existing js file that would fail this check has
been given //@no-flow, we should work to remove all of these in the
future.

Test plan:
I verified that `yarn travis` fails before fixing the other js files,
and passes afterwards.
2018-05-07 20:02:19 -07:00
Dandelion Mané ed1adc7b37
Rename `src/plugins/github/{api,porcelain}` (#231)
I also added a module-level docstring for the porcelain.
2018-05-07 19:18:01 -07:00
Dandelion Mané 9b3019434d
Create `github.Porcelain`: whole-graph porcelain (#230)
Now that we have repository nodes (#171), it makes sense that the Github
porcelain should provide a way to wrap the entire graph, and provide
easy access for the various repositories. This adds a `Porcelain` class
to fulfill that need.

The `Porcelain` is very straightforward: it takes in the whole graph,
and gives a way to get all the Repositories, or to request a particular
Repository by owner/name. In the odd case wherein a graph contains
multiple repository nodes with the same owner and name, an error is
thrown. Per standard JS map semantics (bleh), it can return undefined if
there is no matching repository.

Test plan:
See that the unit tests now use the standard behavior, and a test
verifies behavior for non-existant repositories. I don't have a test
case where there are multiple repo nodes, but that itself would be an
error, so throwing an error in that case is just defensive programming.
2018-05-07 17:56:33 -07:00
Dandelion Mané f219636a56
Create "REPOSITORY" nodes in GitHub plugin graph (#229)
This commit creates a new node type in the GitHub graph: the REPOSITORY
node. The REPOSITORY node has the following payload properties:
- url (string)
- name (string)
- owner (string)

Things this commit does:
- Add new node type and payload type (RepositoryNodePayload)
- Update parser to instantiate the new node type
- Update api.js to have Repository wrap the new node type (thus
Repository is a GitHub entity)
- Update snapshots
- Update users of GitHub node types to ensure they are exhaustive

Things that will come in a followon commit:
- Add CONTAINS edges from the repository to all its PRs and Issues
- Update the Repository porcelain to use those edges, rather than
scanning the graph for every possible Issue/PR (eventually those might
belong to other Repositories)
- Create a GitHubGraph abstraction in the porcelain, which makes it easy
to find all of the Repositories in a graph

Note that retrieving the repository owner technically involved fetching
the whole owner representation (as a GitHub user). I could have chosen
to add that user to the graph, with a "OWNS" edge pointing to the
repository. For simplicity's sake, I've declined to do that, and instead
just parse the owner's name directly.

Test plan:
Added tests to verify that the Repository porcelain entity has the right
properties. Combined with the snapshot tests, that should be sufficient.
2018-05-07 17:28:47 -07:00
Dandelion Mané 9d4ae8b901
Deleted users no longer break GitHub parser (#228)
When a GitHub user delete their account, all of their comments remain,
but with a `null` author. Previously, our code did not account for this
possibility. Now it does (by simply not adding an AUTHORSHIP edge).
Conveniently, our porcelain API already represents authors as a list, so
this doesn't require any change in porcelain API usage.

Test plan:
I did not add any unit tests, simply because
creating-and-deleting a GitHub user to make a repro seemed like a bit of
a pain. However, it is very unlikely that this bug will re-occur,
because the nullability of AuthorJSON is now enforced at the type level
inside graphql.js.

Also, running `node bin/sourcecred.js src-d go-git` now succeedsinstead
of failing.
2018-05-07 17:06:26 -07:00
William Chargin 0cae9d742d
Extract a common SOURCECRED_DIRECTORY flag (#227)
Summary:
This solves two problems:

 1. The “output directory” argument to `sourcecred graph` is also the
    input directory to other commands, like `sourcecred analyze`
    (hypothetically). In such cases, it would be nice for the flag to
    have the same name, but clearly `--output-directory` does not always
    make sense.

 2. In addition to storing graphs, we’ll need to store other kinds of
    data: settings, intermediate data for plugins to cache results, etc.
    We should store these under a single umbrella.

With both of these problems in mind, it makes sense to create a
`SOURCECRED_DIRECTORY` flag under which we store all relevant data.

Test Plan:
Run:
```shell
$ yarn backend
$ node bin/sourcecred.js help graph
$ node bin/sourcecred.js graph sourcecred example-github
$ node bin/sourcecred.js graph sourcecred example-github -d /tmp/sorcecrod
$ SOURCECRED_DIRECTORY=/tmp/srccrd node bin/sourcecred.js graph sourcecred example-github
$ for dir in /tmp/{sourcecred,sorcecrod,srccrd}; do find "${dir}"; done
```

wchargin-branch: graph-directory
2018-05-07 17:05:58 -07:00
William Chargin 498480db06
Factor out `defaultStorageDirectory` function (#226)
Test Plan:
Run `yarn backend && node bin/sourcecred.js help graph`.

wchargin-branch: defaultstoragedirectory
2018-05-07 16:23:53 -07:00
Dandelion Mané 61635a14a7
Remove redundant scripts (#225)
Our SourceCred CLI tool now ipmlements printCombinedGraph and
cloneAndPrintGitGraph, but with more principled implementations and
interfaces :)

Test plan:
`yarn travis --full` passes, so I didn't delete any needed test infra.
2018-05-07 16:15:00 -07:00
Dandelion Mané 55856d7a46
CLI commands error on unhandled promise rejection (#224)
Previously, if a CLI command had an unhandled promise rejection, this
would result in a spurious success and zero exit value.

This commit causes all of our CLI commands to instead fail if they have
an unhandled promise rejection.

Test plan: Previously, `sourcecred graph src-d go-git` would claim to
succeed, although it actually fails due to an unrelated bug. After this
change is applied, it correctly fails to retrieve the GitHub graph (and
hte combine step is never run).
2018-05-07 16:04:54 -07:00
Dandelion Mané 82bf739f35
Query GitHub for repository information (#222)
This commit pulls new information from GitHub about the url, name, and
owner of a GitHub repository. Part of #171.

Test plan: example-github-repo.json has been updated. `yarn travis
--full` passes. This should be sufficient.
2018-05-07 15:48:30 -07:00
Dandelion Mané f3bfed3deb
Split `GithubResponseJSON` and `RepositoryJSON` (#219)
Currently, we generate a `RepositoryJSON` object via querying GitHub.
That `RepositoryJSON` object has a `repository` field... which is weird,
and suggests we got the names slightly wrong.

This commit renames the top-level response to `GithubResponseJSON`, and
factors the `repository` field out as `RepositoryJSON`. Correspondingly,
the `addData` and `addRepository` methods on the parser are now
distinct.

This is a precursor for #171.

Test plan: This is a simple refactor; the fact that yarn travis passes
should be sufficient.
2018-05-07 14:49:59 -07:00
William Chargin 1e0d846675
Change plugin graph label from "PASS" to "DONE" (#221)
Test Plan:
Run `node bin/sourcecred.js graph sourcecred example-github` and note
the new output:
```
Storing graphs into: /tmp/sourcecred/sourcecred/example-github

Starting tasks
  GO   create-git
  GO   create-github
 DONE  create-git
 DONE  create-github
  GO   combine
 DONE  combine

Full results
 DONE  create-git
 DONE  create-github
 DONE  combine

Overview
Final result:  SUCCESS
```

wchargin-branch: plugin-graph-label
2018-05-07 14:47:43 -07:00
William Chargin 4e8d5b574a
Make `execDependencyGraph` labels configurable (#220)
Summary:
The `"PASS"` label only makes sense for tests. This commit makes the
labels configurable, so that the verbiage can make more sense in other
contexts, too.

Test Plan:
Apply a patch like
```diff
diff --git a/config/travis.js b/config/travis.js
index af0996b..b0ab3b6 100644
--- a/config/travis.js
+++ b/config/travis.js
@@ -10,7 +10,11 @@ function main() {
     process.argv.includes("--full")
       ? "FULL"
       : "BASIC";
-  execDependencyGraph(makeTasks(mode)).then(({success}) => {
+  execDependencyGraph(makeTasks(mode), {
+    taskLaunchLabel: " YO ",
+    taskPassLabel: "WHEE",
+    taskFailLabel: "UHOH",
+  }).then(({success}) => {
     process.exitCode = success ? 0 : 1;
   });
 }
```
and note that `GITHUB_TOKEN=none yarn travis --full` exhibits all
desired messages.

wchargin-branch: configurable-labels
2018-05-07 14:39:59 -07:00
William Chargin 2aeeca9a13
Implement a command-line interface (#217)
Summary:
This commit implements the `sourcecred` command-line utility, which has
three subcommands:
  - `plugin-graph` creates one plugin’s graph;
  - `combine` combines multiple on-disk graphs; and
  - `graph` creates all plugins’ graphs and combines them.

As an implementation detail, the `into.sh` script is very convenient,
avoiding needing to do any pipe management in Node (which is Not Fun).
When we build for release, we may want to factor that differently.

Test Plan:
To see it all in action, run `yarn backend`, and then try:
```
$ export SOURCECRED_GITHUB_TOKEN="your_token_here"
$ node ./bin/sourcecred.js graph sourcecred sourcecred
Using output directory: /tmp/sourcecred/sourcecred

Starting tasks
  GO   create-git
  GO   create-github
 PASS  create-github
 PASS  create-git
  GO   combine
 PASS  combine

Full results
 PASS  create-git
 PASS  create-github
 PASS  combine

Overview
Final result:  SUCCESS

$ ls /tmp/sourcecred/sourcecred/
graph-github.json  graph-git.json  graph.json

$ jq '.nodes | length' /tmp/sourcecred/sourcecred/*.json
1000
7302
8302
```
The `node sourcecred.js graph` command takes 9.8s for me.

(The salient point of the last command is that the two small graphs have
node count adding up to the node count of the big graph. Incidentally,
we are [almost][1] at a nice round number of nodes in the GitHub graph.)

[1]: https://xkcd.com/1000/

wchargin-branch: cli
2018-05-07 12:23:09 -07:00
William Chargin d7bfa02a54
Change `execDependencyGraph` export format (#216)
Summary:
To be honest, I have no idea what exactly this does or why it’s
necessary, but if we don’t do this then it is not possible to `import`
the exported member from a Webpack-bundled script. I’ve seen this
pattern before; one day I’ll actually figure out what it does. :-)

Test Plan:
Note that `yarn travis` (success) and `yarn travis --full` (failure; no
GitHub token) both have the expected behaviors.

wchargin-branch: execdependencygraph-export
2018-05-07 10:30:56 -07:00
Dandelion Mané 72ca52f579
Change package.json name to sourcecred (#215) 2018-05-05 00:04:42 -07:00
Dandelion Mané fa4082c95b
Minimal toy oclif integration (#214)
This commit adds [oclif] as a command-line framework. It is successfully
integrated with webpack.

[oclif]: https://github.com/oclif/oclif

Usage:
`yarn backend` to build the cli.
`node bin/sourcecred.js` to launch the CLI and see usage
`node bin/sourcecred.js example` for one example command
`node bin/sourcecred.js goodbye` for another example command
2018-05-04 19:28:37 -07:00
William Chargin e9dbdeca96
Target latest Node for backend applications (#213)
Summary:
Consequently, Babel won’t transform classes to their roughly equivalent
ES5 counterparts, etc.

Test Plan:
Create `src/classy.js` with `class X {}; console.log(X);`. Then, add a
build target for `classy: resolveApp("src/classy.js"),` in `paths.js`.
Use `yarn backend` and inspect the contents of `bin/classy.js`; in
particular, look at the definition of `X` (whatever the argument to
`console.log` is). Before this commit, the result will be a big
complicated mess. After this commit, it will be `class X {}`.

Note also that `yarn travis --full` passes, indicating that the two
manual tests, which call out to the utilities in `bin/`, still work.

wchargin-branch: target-node
2018-05-04 19:22:39 -07:00
William Chargin b5e894bbb4
Fork `babel-preset-react-app` into config/ (#212)
Summary:
We want to change this configuration so that our compilation of backend
applications can target latest Node. This commit forks the current
configuration so that we can modify it easily.

Test Plan:
Both `yarn start` and `yarn travis` work. The generated backend
applications work, too.

wchargin-branch: fork-babel-config
2018-05-04 19:19:45 -07:00
Dandelion Mané de5542de6a
Exclude node modules from backend build (#211)
Setup following directions from [webpack-node-externals]

[webpack-node-externals]: https://www.npmjs.com/package/webpack-node-externals

This unblocks #210.

Test plan: `yarn backend` still succeeds, and the binary scripts still
work. The resultant binaries are much smaller, as seen below (note build
time is the same).

before:
```
❯ yarn backend
yarn run v1.5.1
$ node scripts/backend.js
Building backend applications...
Compiled successfully.

File sizes after gzip:

  231.37 KB  bin/printCombinedGraph.js
  199.5 KB   bin/fetchAndPrintGithubRepo.js
  46.41 KB   bin/cloneAndPrintGitGraph.js
  21.48 KB   bin/createExampleRepo.js
  17.71 KB   bin/loadAndPrintGitRepository.js

Build completed; results in 'bin'.
Done in 4.46s.
```

after:
```
❯ yarn backend
yarn run v1.5.1
$ node scripts/backend.js
Building backend applications...
Compiled successfully.

File sizes after gzip:

  27.78 KB  bin/printCombinedGraph.js
  12.73 KB  bin/cloneAndPrintGitGraph.js
  12.41 KB  bin/fetchAndPrintGithubRepo.js
  6.03 KB   bin/loadAndPrintGitRepository.js
  5.52 KB   bin/createExampleRepo.js

Build completed; results in 'bin'.
Done in 4.28s.
```
2018-05-04 16:31:39 -07:00
William Chargin d3443a3d4c
Extract `execDependencyGraph` core from CI script (#208)
Summary:
We’d like to use the same abstraction for creating multiple cred graphs
and then combining them together. This will enable us to do that.

Test Plan:
Run `yarn travis` to test the success case, and `yarn travis --full`
(without setting a `GITHUB_TOKEN`) to test the failure case.

wchargin-branch: execdepgraph
2018-05-04 15:47:26 -07:00
William Chargin a642ed46b9
Expose `Graph.mergeManyConservative` (#209)
Summary:
This offers #205 to general users.

Test Plan:
Existing tests suffice.

wchargin-branch: merge-many-conservative
2018-05-04 15:42:39 -07:00
Dandelion Mané e3469f157d
Add `src/tools/bin/printCombinedGraph.js` (#207)
`printCombinedGraph` loads and prints a cross-plugin combined
contribution graph for a given GitHub repository.

It is a simple executable wrapper around `src/tools/loadCombinedGraph`.

Example usage:
`node bin/printCombinedGraph.js sourcecred example-git $GITHUB_TOKEN`
2018-05-04 12:10:20 -07:00
Dandelion Mané e66ed45cba
Add CLI for printing a fresh Git graph (#206)
`cloneAndPrintGitGraph` clones a git repository, and generates a Git
object graph for that repository.

This can be run as follows:
```
yarn backend;
node bin/cloneAndPrintGitGraph sourcecred example-git
```

This commit also adds two utility modules:
* `cloneAndLoadRepository` , which clones a Git repository to a tmpdir,
parses the `Repository` data out, and then cleans up.
* `cloneGitGraph`, which calls `cloneAndLoadRepository` and `createGraph`

Test plan: These don't fit well into our CI, because they require
network access to clone repositories from GitHub. I verified that the
functions work via the demo script above.
2018-05-04 11:35:14 -07:00
William Chargin d3dcf1ef5a
Speed up Git graph creation (#205)
Summary:
Because of `mergeConservative`’s naive implementation, using it as a
reducer results in a roughly quadratic algorithm. Replacing this with a
mutative accumulation has the same semantics but goes much faster.

Test Plan:
For correctness: tests pass. For performance: apply the following patch
to collect timing data. Then run:
```shell
$ NODE_ENV=production yarn backend
$ node bin/loadAndPrintGitRepository.js . >/tmp/sourcecred-git
$ node bin/createGitGraph.js /tmp/sourcecred-git
```
to run against the current state of SourceCred. Before this patch, the
elapsed time is about 6m00s; after this patch, it is about 0m1.3s.
Specifically:
```
$ cat timing_before
[0] Git graph creation: 239593.958ms
[1] Git graph creation: 240380.557ms
[2] Git graph creation: 241687.042ms

$ cat timing_after
[0] Git graph creation: 1585.903ms
[1] Git graph creation: 1315.430ms
[2] Git graph creation: 1373.833ms
```

<details>
<summary>Patch to collect timing data</summary>

```diff
diff --git a/config/paths.js b/config/paths.js
index f875eee..1bc1469 100644
--- a/config/paths.js
+++ b/config/paths.js
@@ -64,5 +64,6 @@ module.exports = {
     loadAndPrintGitRepository: resolveApp(
       "src/plugins/git/bin/loadAndPrintRepository.js"
     ),
+    createGitGraph: resolveApp("src/plugins/git/bin/createGraph.js"),
   },
 };
diff --git a/src/plugins/git/bin/createGraph.js b/src/plugins/git/bin/createGraph.js
new file mode 100644
index 0000000..a35ca1b
--- /dev/null
+++ b/src/plugins/git/bin/createGraph.js
@@ -0,0 +1,25 @@
+// @flow
+
+import {readFileSync} from "fs";
+
+import {createGraph} from "../createGraph";
+
+function main() {
+  const filename = process.argv[2];
+  const raw = JSON.parse(readFileSync(filename).toString());
+  const results = [];
+  for (let i = 0; i < 3; i++) {
+    const timer = `[${i}] Git graph creation`;
+    console.time(timer);
+    results.push(createGraph(raw));
+    console.timeEnd(timer);
+  }
+  console.log(
+    "Checksum: " +
+      JSON.stringify(
+        results.map((graph) => graph.nodes().length ^ graph.edges().length)
+      )
+  );
+}
+
+main();
```

</details>

wchargin-branch: collect-gold-rings
2018-05-04 10:52:41 -07:00