Configure fe2 to load from instances (#1856)

This modifies the frontend2 so that we can load real data from cli2
instances, including:
- the root `sourcecred.json` config
- files inside the `config/` directory
- files inside the `output/` directory

This works both for the dev server and the compiled output.

In the case of the dev server, it's now necessary to provide the path to
the cli2 instance you're developing against. You can pass this via the
`--instance` argument, as in `yarn start2 --instance=/path/`, or via
the `$SOURCECRED_DEV_INSTANCE` environment variable (recommended). If
neither is provided, or if that path doesn't look like a valid instance,
an error is thrown.

In the case of the built output, given a valid sc2 instance, you can set
it up via the following:

```
cp -r build2/favicon.png build2/index.html build2/static $INSTANCE
```

Then spin up a simple http server in the $INSTANCE.

You can look at console messages in the frontend to verify that the
instance is working (note it expects to see a discourse config file, not
a GitHub config file as was used in some previous examples).

Test plan:
Setup an example Discourse instance, and then turn on the server via
`yarn start2 --instance=PATH`,
`SOURCECRED_DEV_INSTANCE=path yarn start2`,
and by manually copying in the built outputs using the instructions
above.

In each case, you can load the homepage and check the console to see
that assets loaded successfully.
This commit is contained in:
Dandelion Mané 2020-06-17 18:47:12 -07:00 committed by GitHub
parent cb637dbaa4
commit e6a3d776ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 62 additions and 9 deletions

View File

@ -6,8 +6,8 @@ import type {
$Response as ExpressResponse, $Response as ExpressResponse,
} from "express"; } from "express";
*/ */
const os = require("os");
const path = require("path"); const path = require("path");
const fs = require("fs-extra");
const webpack = require("webpack"); const webpack = require("webpack");
const RemoveBuildDirectoryPlugin = require("./RemoveBuildDirectoryPlugin"); const RemoveBuildDirectoryPlugin = require("./RemoveBuildDirectoryPlugin");
const CopyPlugin = require("copy-webpack-plugin"); const CopyPlugin = require("copy-webpack-plugin");
@ -40,18 +40,55 @@ async function makeConfig(
devServer: { devServer: {
inline: false, inline: false,
before: (app /*: ExpressApp */) => { before: (app /*: ExpressApp */) => {
const apiRoot = "/api/v1/data"; let developmentInstancePath /*: ?string */ =
process.env["SOURCECRED_DEV_INSTANCE"];
const lastArg = process.argv[process.argv.length - 1];
if (lastArg.startsWith("--instance")) {
const pieces = lastArg.split("=");
developmentInstancePath = pieces[1];
}
if (developmentInstancePath == null) {
throw new Error(
"Please provide a SourceCred cli2 instance, via $SOURCECRED_DEV_INSTANCE, or --instance=PATH"
);
}
const configPath = path.join(
developmentInstancePath,
"sourcecred.json"
);
if (!fs.existsSync(configPath)) {
// Sanity check; we won't try to verify that the right output files are there, since it may change and the
// frontend should have helpful error messages in that case. But if there's no sourcecred.json file, then
// it's likely the developer has made a mistake and would find a quick failure helpful.
throw new Error(
`Provided instance directory ${developmentInstancePath} doesn't have a sourcecred.json file`
);
}
const configContents = fs.readFileSync(configPath);
const serveConfig = (_unused_req, res /*: ExpressResponse */) => {
res.status(200).send(configContents);
};
const rejectCache = (_unused_req, res /*: ExpressResponse */) => { const rejectCache = (_unused_req, res /*: ExpressResponse */) => {
res.status(400).send("Bad Request: Cache unavailable at runtime\n"); res.status(400).send("Bad Request: Cache unavailable at runtime\n");
}; };
app.get(`${apiRoot}/cache`, rejectCache);
app.get(`${apiRoot}/cache/*`, rejectCache); app.get("/sourcecred.json", serveConfig);
app.get(`/cache`, rejectCache);
app.get(`/cache/*`, rejectCache);
// It's important that we individually whitelist directories (and the
// sourcecred.json file) rather than indiscriminately serving from
// root, because there might be a "permanent" frontend installed in
// that instance, and we don't want to accidentally load the existing
// frontend instead of our development copy.
app.use( app.use(
apiRoot, "/output/",
express.static( express.static(path.join(developmentInstancePath, "output"))
process.env.SOURCECRED_DIRECTORY || );
path.join(os.tmpdir(), "sourcecred") app.use(
) "/config/",
express.static(path.join(developmentInstancePath, "config"))
); );
}, },
}, },

View File

@ -5,7 +5,23 @@ import React from "react";
import type {Assets} from "../webutil/assets"; import type {Assets} from "../webutil/assets";
import {StyleSheet, css} from "aphrodite/no-important"; import {StyleSheet, css} from "aphrodite/no-important";
async function loadAndReport(assets, path) {
const url = assets.resolve(path);
const response = await fetch(url);
if (!response.ok) {
console.error(path, response);
}
const json = await response.json();
console.log(path, json);
}
export default class HomePage extends React.Component<{|+assets: Assets|}> { export default class HomePage extends React.Component<{|+assets: Assets|}> {
async componentDidMount() {
loadAndReport(this.props.assets, "sourcecred.json");
loadAndReport(this.props.assets, "output/credResult.json");
loadAndReport(this.props.assets, "config/sourcecred/discourse/config.json");
}
render() { render() {
return ( return (
<div className={css(styles.container)}> <div className={css(styles.container)}>