1344 Commits

Author SHA1 Message Date
Robin van Boven
4d86c538be
Use stable stringify for cred.json & graph.json in load (#1576)
The upcoming DataDirectory class will use stable stringify too.
But since that will affect the snapshots, make sure those are
updated before we switch to the new load implementation.
2020-01-20 21:44:16 +01:00
Dandelion Mané
f5a44a5a4f
Enable eslint eqeqeq rule (#1579)
The [eqeqeq rule][1] allows us to ban potentially confusing usage of the
type-coercing `==` equality operator. I've set it to allow `== null`, and
to disallow any other use of `==`.

Alongside activating the rule, I've fixed 16 violations. Inspired by
[review of #1560][2].

Test plan: `yarn test` passes.

[1]: https://eslint.org/docs/rules/eqeqeq
[2]: https://github.com/sourcecred/sourcecred/pull/1560#discussion_r368331081
2020-01-19 20:20:15 -08:00
Brian Litwin
90c76a5023 mirror: add network request logging (#1562)
Logs network requests and responses into a table in the db. Also logs
an `UpdateId` that links the mutation to the network request/response
that generated it.

This is a breaking change to the Mirror database format; existing caches
will need to be regenerated.

Resolves #1450.

Test Plan:
Unit tests added, covering the core functionality and integration into
the public APIs. After loading a project and manually inspecting it in
an sqlite shell, the results in the `network_log` table looked
reasonable.
2020-01-19 17:58:49 -08:00
Brian Litwin
dea45fb8c1 Update distributionToCred contract (#1560)
Resolves #1317

Updates timeline cred to handle the case where the scoring nodes' total
cred sums to zero in an interval. In practice, we've encountered this
circumstance when a github.io repository contains timestamps that
predates any User's contributions by several weeks, such as
sfosc.github.io.

Test Plan:

- Added a test case to handle this circumstance
- Updated a test case per discussion on #1317 to return a cred score
of 0 for all nodes in all intervals when there are no scoring nodes
passed to the function, so that we handle these cases consistently.

Also loaded sfosc.github.io and observed that the cred output appeared
to match expectations and didn't contain any `NaN` or `Infinity` values
as it did before.
2020-01-19 15:09:51 -08:00
William Chargin
74acd1fa80
lint: require camel case variables (#1563)
Summary:
We’ve had this policy unspoken since the beginning; making it explicit
as a lint rule makes it one less thing to think about. The few existing
offenders can almost all be changed with no value lost; one is an extern
that needs a lint suppression.

Test Plan:
That Flow and tests still pass suffices.

wchargin-branch: lint-camelcase
2020-01-17 08:55:57 -08:00
Dandelion Mané
a36873810a
Refactor Weights to remove awareness of types (#1558)
I'm currently on a quest to separate cred computation away from any
plugin metadata (see #1557). This means we need a way to represent node
and edge weights without any explicit concept of 'types'.

This commit is a first step towards that. It removes the distinction
between 'type weights' and 'manual weights' in the weights data type.
Instead, we now just have node weights and edge weights. In contrast to
before, all weights are now interpreted as prefix matchers, e.g. a
single node or edge may match multiple weights; when this occurs, the
weights compose multiplicatively.

Since types were already identified by prefix, if a plugin wants to
assign a weight to a particular type, it may do so by specifying a
weight for that type's prefix. As before, it's possible to have a
type-level weight and a weight on a specific node, and compose them
multiplicatively.

As an added bonus, we could now sensibly have 'plugin-level' weights and
'type-level weights' and compose them multiplicatively. Thus, if we
realized that the Foo plugin is undervalued relative to the Bar plugin,
we could increase the Foo weight rather than needing to adjust all of
its types individually.

So as to keep the scope for this commit somewhat manageable, I modified
the underlying data type for Weights, but not any of the cred
computation interfaces. The weights pipeline still takes the plugin
declarations, and we still get the default type level weights from the
plugin's types. A future commit will modify the pipeline so that the
plugins provide default types alongside the Graph.

I deliberately did not provide an upgrade handler for the old style
weights JSON. This is sensible as the semantics are now different. In
the past, it was possible to specify a weight for a single node without
affecting the weights of other nodes whose addresses have the first
node's address as a prefix. Since this is no longer possible, there is
no universally "correct" way to handle the old weights files. In
practice, there are so few users that it is not a big deal either way.

Test plan:
This change has implications across the codebase and UI. In addition to
`yarn test --full` passing, I verified that:
- updating and recomputing works in the mainline UI
- updating and recomputing works in the legacy UI
- downloading weights from the UI and then explicitly loading them still
works
2020-01-13 20:00:29 -08:00
Robin van Boven
063c08d974
UniRef: add GithubReferenceDetector (#1543)
As a RelationalView is not designed for multiple repositories, we
should implement our own merging of mappings obtained from
RelationalViews.

fromRelationalViews is a factory function which does this for us.
And by accepting an array of RelationalViews it's more apparent
it should be used this way.
2020-01-13 14:13:30 +01:00
Robin van Boven
f16119af43
UniRef: add urlReferenceMap to RelationalView (#1542)
RelationalView provides easy access to ReferentEntities, which we
can use for reference detection. The map produced by
urlReferenceMap can be used easily by a MappedReferenceDetector.

As discussed in #1532 we'll use RelationalView for this before
deprecating it.
2020-01-13 14:09:19 +01:00
Dandelion Mané
d7cacf7173
Enforce validation of GitHub tokens using types (#1520)
Currently, we have robust GitHub token validation logic. However, at a
type level, usage of this logic is unenforced, so many places in the
codebase don't use validation; most crucially, the `Common.githubToken`
method doesn't, which means that the CLI doesn't validate GitHub tokens.

Instead, `Common.githubToken` currently provides a deceptive signature:

`function githubToken(): string | null`

One might reasonably think that the presence of a string means that
there is a GitHub token, and that you can test `if (token != null)`.
However, a command line user can easily provide an empty string:

`SOURCECRED_GITHUB_TOKEN=null node bin/sourcecred.js load ...`

In this case, the user was trying to unset the GitHub token, but this
actually provides a string-y GitHub token, so at a type level, it looks
like a GitHub token is present.

No more! This commit adds `opaque type GitHubToken: string = string` in
the `github/token.js` module. Since the type is opaque, it only has one
legal constructor: the `validateToken` method in `github/token.js`. The
functions that actually use the token have been updated to require this
type. Therefore, we now enforce at the type level that every usage of a
GitHub token needs to be validated, ensuring that we no longer confuse
empty strings for valid GitHub tokens.

Note that making GitHub token an opaque subtype of string
(`GithubToken: string`) is important because it means that consumers can
still pass or store the token as a string; however, no fresh ones can be
constructed except by the validator.

Test plan: Implementation-wise, this is a simple refactor; `yarn test`
passes.
2020-01-12 21:25:28 -08:00
Robin van Boven
9e8e6845bc
UniRef: implement DiscourseReferenceDetector (#1530)
Discourse ReferenceDetector detector that relies on database lookups.

(Should be the main way to solve #1479, when UniRef is used by all plugins)
2020-01-11 12:59:45 +01:00
Robin van Boven
5cf0fba634
Initiatives: add queries to mirror to implement DiscourseQueries (#1490)
We defined a DiscourseQueries interface, intended as a subset of
the Discourse plugin's MirrorRepository methods. This subset is
used by the Initiatives plugin to source Iniaitive data.

We're now adding the new methods it needed to the MirrorRepository.
2020-01-11 11:49:45 +01:00
Dandelion Mané
adec1b0b5d
Move repoId.js from core to plugins/github (#1535)
In the early days of the project, we used GitHub repository ids as the
core way of identifiying projects. This was a weird choice, since it's
so specific to the GitHub plugin. In #1238 we added a (theoretically)
agnostic type in `Project`, although in practice it's still pretty
coupled. Still, it will be best to move the `RepoId` type out of `core`
and to the GitHub plugin where it belongs.

This leaves a few "awkard" imports from plugin code, (e.g. in the api
module), but generally the places that are importing `RepoId` were
already importing stuff from Discourse and Identity plugins. In either
case, I'd rather have the awkwardness of depending on the RepoId in core
places be obvious (from the dependency on plugin code) rather than
giving a false appearance that RepoIds are really a core concept.

Test plan: `yarn test` passes.
2020-01-10 19:01:06 -08:00
Robin van Boven
c1f123f352
CI: make sure we log in for deployments (#1541)
Over at
https://github.com/sourcecred/sourcecred/pull/1514#discussion_r365443659
I suggested adding `deploy: false` to be less surprising with the
tags we're pushing to DockerHub.

As mentioned in #1512, we actually need `publish: true` to log in
with DockerHub.
2020-01-10 23:57:31 +01:00
Robin van Boven
90b77a85dd
CI: use cache_from shorthand provided by Docker orb (#1540)
After we wrote our Docker jobs, the cache_from was added to
the orb. This is shorter and less error prone because we don't
need to provide the identical arguments twice.

The change was made in
https://github.com/CircleCI-Public/docker-orb/pull/27
by @vsoch. And was released as of v0.5.15 of the Docker orb.

The change updates to the latest version of the orb currently
available.
2020-01-10 23:27:17 +01:00
Robin van Boven
41f3803a97
CI: add tagged-release workflow (#1514)
Fixes #1512

In contrast with our previous "tags only" deploy job, this
configuration makes sure all it's preceding jobs are also
set to a "tags only" filter in order to run.
2020-01-10 22:54:31 +01:00
Robin van Boven
02c59d9967
Fix: support HTTP references on Discourse (#1529)
Note, adding support in this single function doesn't solve some of the greater issues with HTTP/HTTPS. Because the protocol is included in the node addresses, converging nodes or canonicalizing on either protocol would be important for instances that support HTTP. That problem is outside of scope for the reference detector though.
2020-01-10 12:12:25 +01:00
William Chargin
4d77516bf4
api: add server for easy Observable integration (#1537)
Summary:
This commit adds a simple Python server for connecting the output of
`yarn api` (or `yarn api --watch`) to an observable notebook. We need a
custom server rather than just `python3 -m http.server` to send CORS
headers properly. This server enables a very tight loop from editing
SourceCred core code on your local filesystem to seeing live updates in
an Observable notebook, with latency on the order of one second.

Test Plan:
Run `yarn api --watch` in the background. Launch the new API server.
Navigate to <https://observablehq.com/demo>. Copy the two paragraphs of
Observable code from `scripts/serve_api.py` into _separate_ Observable
cells, and execute them. Note that `myGraph` becomes a valid SourceCred
graph. Modify `src/core/graph.js` to add `this._aaa = 123;` to the top
of the `Graph` constructor. Re-execute the first Observable cell (the
one that loads the SourceCred module), and note that `myGraph` updates
to include the new `_aaa` attribute:

![Screenshot of Observable notebook after test plan][ss]

[ss]: https://user-images.githubusercontent.com/4317806/71958748-dddf8680-31a5-11ea-9016-5df76ceeea46.png

wchargin-branch: api-server
2020-01-09 21:33:09 -08:00
William Chargin
8ed0585bb6
api: extract browser-friendly build target (#1536)
Summary:
This re-packages the build for the internal APIs exposed under #1526 to
be more browser-friendly. Removing `target: "node"` (and adding an
explicit `globalObject: "this"` for best-effort cross-compatibility) is
the biggest change from the backend build; removing all the extra
loaders and static site generation is the biggest change from the
frontend build.

This build configuration is forked from `webpack.config.backend.js`.

Test Plan:
Run `yarn api`, then upload the contents of `dist/api.js` to an
Observable notebook and require it as an ES module. Verify that the
SourceCred APIs are exposed: e.g., `sourcecred.core.graph.Graph` should
be a valid constructor.

wchargin-branch: api-build
2020-01-09 21:27:37 -08:00
Dandelion Mané
be7b465f98
Remove core/pagerankGraph (#1533)
The `pagerankGraph` module was an attempt to do a better job of
co-ordinating the data needed to run Pagerank, by wrapping the Graph
class alongside context on edge weights, etc. However, it was obsoleted
by work on TimelineCred. Thus, we can remove it entirely. I intend to
make another attempt at collecting all the data needed for cred analysis
in a way that doesn't couple with plugin code, and this time it will be
timeline-aware.

Test plan: `yarn test`
2020-01-09 12:23:51 -08:00
William Chargin
6b85296e55
api: expose new bundle of select internals (#1526)
Summary:
For convenient import by scripts and Observable notebooks that want to
use SourceCred code outside its normal build system. We export a subset
of the codebase, including some core data structures and algorithms and
also some plugin metadata, but no plugin loading code.

To build, run `yarn backend` (or `yarn backend --watch`), then grab the
new `bin/api.js` file.

Test Plan:
Sample usage, with normal Node:

```javascript
const {
  core: {
    graph: {Graph, NodeAddress, EdgeAddress},
  },
} = require("./api").default;

function node(address) {
  return {
    address,
    description: "blurgh",
    timestampMs: -1,
  };
}

const g = new Graph();
g.addNode(node(NodeAddress.fromParts(["people", "alice"])));
g.addNode(node(NodeAddress.fromParts(["people", "bob"])));
g.addEdge({
  address: EdgeAddress.fromParts(["friendship"]),
  src: NodeAddress.fromParts(["people", "alice"]),
  dst: NodeAddress.fromParts(["people", "bob"]),
  timestampMs: 0,
});

console.log(require("json-stable-stringify")(g));
```

This prints a valid graph JSON object.

wchargin-branch: api-bundle
2020-01-08 23:38:39 -08:00
Dandelion Mané
01248fb8f6
Remove the repoIdRegistry (#1534)
Before we added the concept of "SourceCred Projects", we tracked cred
instances via their GitHub repostiory id. The replacement for this
system was added in #1238, I missed the RepoIdRegistry in the cleanup.

This commit removes all code pertaining to the now-obsolete
RepoIdRegistry.

Test plan:
- `yarn test --full` passes
- manual inspection of `yarn start`; it still loads properly
- manual inspection of the output for build_static_site.sh
- `git grep repoIdRegistry` returns no hits
2020-01-08 12:58:04 -08:00
William Chargin
32f83ad676
ci: fast-fail on forked PRs with no credentials (#1522)
Summary:
PRs created from forks don’t have credentials when running CI. This
commit causes the `test-full` job (which requires credentials) to fail
fast with a helpful error message.

Test Plan:
Push distinct versions of this commit to a fork and to the main
repository, and open pull requests for each. Note that the tests pass
from the main repository, but fail with a nice message from the fork:

![Screenshot of expected fast-fail behavior][ss]

The “team member pushes to trusted branch” workflow has already been
successfully exercised for #1521.

[ss]: https://user-images.githubusercontent.com/4317806/71707839-b782ab00-2da1-11ea-8aa9-7d8720538a87.png

wchargin-branch: forked-pr-fail-fast
2020-01-07 21:03:23 -08:00
Robin van Boven
fa00751cd4
CI: remove broken tag deploy (#1513)
See #1512 for full context.

Short explanation:
Because the job wants to run only on tag pushes, but requires
the `test` job (which doesn't run on tag pushes), the job
will never run.
2020-01-08 00:01:02 +01:00
Robin van Boven
3cfb338336
Discourse: add mirror findUsername (#1528)
Gets the username of a user, if it exists.
Helpful for fixing capitalization issues such as #1479,
and verifying the user exists for reference detection.
2020-01-07 22:25:09 +01:00
Robin van Boven
6c15372d9f
CI: separate node10 and 12 cache (#1527)
Previously both node versions would share the same cache.
This caused one of the two versions to always rebuild
the `better-sqlite3` package, costing about 1 min per job.

Now we're using a different cache key for each version,
rebuilding a cached `better-sqlite3` should no longer be
necessary.
2020-01-07 22:16:39 +01:00
Robin van Boven
03d525810d
Initiatives: implement DiscourseInitiativeRepository (#1483)
Uses the Discourse mirror data and parsing function to create an
InitiativeRepository implementation. Which we can use for createGraph.
2020-01-07 14:43:45 +01:00
Robin van Boven
105912e498
Initiatives: create Initiative from discourse topics (#1480)
This adds the Discourse specific semantics on top of the basic template parsing.
2020-01-07 14:21:54 +01:00
Robin van Boven
4ab58a09b5
Initiatives: implement "cooked HTML" template parsing (#1478)
Will return a partial Initiative, or throw when the template isn't matched.
2020-01-07 14:15:26 +01:00
Robin van Boven
b05cc84f2e
Initiatives: implement basic createGraph (#1477) 2020-01-07 14:07:27 +01:00
Robin van Boven
579b01ed46
UniRef: adding TranslatingReferenceDetector implementation (#1511)
The TranslatingReferenceDetector is an abstraction particularly useful for the
Initiatives reference detector. Which should use the Discourse reference
detector as it's base and translate the node address of the returned discourse
topic to the initiative's node address.
2020-01-07 13:48:25 +01:00
Robin van Boven
953cbfaac2
UniRef: add CascadingReferenceDetector implementation (#1510)
The CascadingReferenceDetector is an abstraction we can use for combining multiple ReferenceDetector instances and giving them a priority.
2020-01-07 13:39:21 +01:00
Robin van Boven
821be0b46e
UniRef: add MappedReferenceDetector implementation (#1509)
The current reference detection implementation internal to the GitHub plugin
uses a map similar to this. This class being near to that makes it easy to adopt.
It's also very simple to use for tests.
2020-01-07 13:26:53 +01:00
Robin van Boven
4bdc7a57b7
UniRef: Declare ReferenceDetector interface (#1508)
The core declaration of the ReferenceDetector interface.

Reason I'm adding an index.js file is to allow (core) classes that implement
this interface to have separate files, while keeping redundancy out of the
import statements.
2020-01-07 12:27:19 +01:00
William Chargin
cdb360f6f9
docs: add note about full test suite credentials (#1524)
Summary:
Contributors who open PRs from a fork will need to have their commits
“blessed” by a core team member before the `test-full` CI job will run
successfully. This commit explains that to ward off any confusion.

Test Plan:
This workflow was recently exercised for #1521, successfully.

wchargin-branch: contributing-test-full
2020-01-05 11:18:54 -08:00
Robin van Boven
b860f31a19
Use createProject to set default values for Project (#1492)
Creation of new Project instances is spread out across the code.
So whenever there's a change in it's format, the PR is cluttered
with adding a logical default value in many places. It means
our default values might be inconsistent as well.

For example #1385 adds many `identities: [],` lines.
A similar situation would happen with the planned Initiatives
plugin, adding many `initiatives: null,` lines.

Using this function we can manage what default values to add
from a central place. Avoiding noise and code churn.
2020-01-03 15:11:41 +01:00
Robin van Boven
b7b93d2a8d
Add unit tests for projectFromJSON's upgrade support (#1519) 2020-01-03 14:57:46 +01:00
Robin van Boven
fb16530b8f
Update projectFromJSON & upgrade function signatures (#1518)
This creates better flow type coverage for the upgrading
from older Project types feature.

Note projectFromJSON's function signature changes like
this:

- (Compatible<Project>) => Project
+ (Compatible<any>) => Project

And that makes sense, because we use this function to
validate an object we parsed from JSON at runtime. It
could actually be anything.

Added benefit is that is makes writing unit tests possible.
Because now will flow not throw a type error when we provide
something other than Compatible<Project> as input, to test
upgrading or validation functionality.

Note that the underlying utility fromCompat already uses
Compatible<any> for the same object.
2020-01-03 14:50:20 +01:00
Robin van Boven
89529ea4fe
Define previous Project type versions (#1517)
These are important to accurately add types to function
signatures of validating and upgrading logic.
2020-01-03 14:36:01 +01:00
burrrata
5b9455cf9d Add instructions to set up the GitHub API Token (#1521)
The README explains how to set the SOURCECRED_GITHUB_TOKEN, but later in
the Docker section. People who aren't using Docker will follow the
initial installation instructions. This commit adds the instructions to
set that up when users first install and set up SourceCred.
2020-01-02 20:17:31 -08:00
Dandelion Mané
507091a976
Allow while(true) loops (#1495)
The eslint no-constant-condition rule disallows while(true) loops,
since the true is a constant condition. However, I find the allowed
alternative (`for (;;)`) less readable, so I am adding the sub-rule that
allows constant conditions for loops.

Test plan: A followon commit uses a while(true) loop, and, assuming this
patch is applied, it does not result in a lint error.

Co-authored-by: Robin van Boven <497556+Beanow@users.noreply.github.com>
2019-12-28 15:58:43 -08:00
Robin van Boven
8f32912270
Initiatives: define internal datatype Initiative (#1417) 2019-12-23 00:43:36 +01:00
Robin van Boven
1b0eb483ce
Initiatives: create plugin declaration (#1416) 2019-12-23 00:36:57 +01:00
Robin van Boven
5727a831a9
Security: force update yarn version in Dockerfile (#1503)
Note, unless you used the SourceCred Docker image's bundled
npm or yarn to install your own package.json dependencies,
you were not vulnerable. Otherwise the same risk applies as
[in this NPM blog][1].

You can patch the vulnerability by using the latest Docker image
using `docker pull sourcecred/sourcecred` as soon as this commit
is included in the latest release.

## Commit details

In a [recent security issue][1] found in NPM and Yarn, handling
binary file installation has changed. Quoting from there:

> The bin script linking libraries in use in npm v6.13.4 were
> updated such that, when installing binary entries of top-level
> globally installed packages, they will only overwrite existing
> binary files if they are currently installed on behalf of the
> same package being installed.  For example, npm install –global
> foo could overwrite /usr/local/bin/foo if and only if
> /usr/local/bin/foo is currently a link to a previously installed
> version of foo.

In our case, we specifically want this behavior in our Dockerfile.
The node:12 base image comes with an NPM and Yarn version installed.
We're using npm i -g yarn@<version> to upgrade the yarn installation
to a predictable minimum, should we have an older version from the
base image. But since they're from different installation sources,
it causes an error as it would overwrite the yarn binary that wasn't
previously owned by npm install.

Our own package.json or yarn.lock did not appear to have any risk
of exploitation. However since we bundle our image with npm and yarn,
people using our image could in theory use it to install their own
packages. Meaning we should include the fixed npm and yarn versions
to protect users in such a scenario.

[1]: https://blog.npmjs.org/post/189618601100/binary-planting-with-the-npm-cli
2019-12-22 21:42:03 +01:00
greenkeeper[bot]
e68f99bf00 Update jest-fetch-mock to the latest version 🚀 (#1494)
* chore(package): update jest-fetch-mock to version 3.0.0

* chore(package): update lockfile yarn.lock
2019-12-22 13:23:19 -06:00
burrrata
02515de550 Fix broken README link (#1507)
The link to the prototype was outdated.

Test plan: Manually check the link.
2019-12-22 13:20:23 -06:00
greenkeeper[bot]
0aea1ff13b Update flow-bin to the latest version 🚀 (#1489)
* chore(package): update flow-bin to version 0.114.0

* chore(package): update lockfile yarn.lock
2019-12-16 14:34:16 -06:00
Robin van Boven
f35d7e088f
Chore: update packages & bugfix new versions (#1487)
* chore(package): yarn upgrade

Updates all packages within version range.

* Bugfix update stacktrace matching code

The stacktrace has changed, most likely due to
a babel plugin updating. It now seems based on
the name of the `handlingErrors` argument
instead of the variable name storing the
anonymous function.

* Bugfix update react-router patch version

By updating the react packages, warnings were
logged about unsafe componentWillMount usage.
These warnings tripped a unit test.
react-router was the cause of these, so this
update avoids getting the warnings.
2019-12-10 20:52:55 +01:00
Robin van Boven
a41eb71949
GitHub: assume installation token length of 40 (#1486)
In the documentation 16 characters were displayed.
But testing showed we're typically seeing 40.

Fixes #1474
2019-12-10 18:45:54 +01:00
greenkeeper[bot]
ef3e1d6c48 Update eslint-plugin-import to the latest version 🚀 (#1484)
* chore(package): update eslint-plugin-import to version 2.19.0

* chore(package): update lockfile yarn.lock
2019-12-09 15:19:42 +01:00
greenkeeper[bot]
7951d3adae Update file-loader to the latest version 🚀 (#1468)
* chore(package): update file-loader to version 5.0.2

Closes #1468

* chore(package): update lockfile yarn.lock
2019-12-09 15:07:58 +01:00