2018-05-08 21:56:06 +00:00
|
|
|
|
// @flow
|
2018-08-16 20:38:13 +00:00
|
|
|
|
const {spawnSync, execFileSync} = require("child_process");
|
2018-02-18 16:13:29 +00:00
|
|
|
|
const fs = require("fs");
|
2018-08-16 20:38:13 +00:00
|
|
|
|
const stringify = require("json-stable-stringify");
|
2018-02-18 16:13:29 +00:00
|
|
|
|
const path = require("path");
|
2018-08-16 20:38:13 +00:00
|
|
|
|
|
2018-02-18 16:13:29 +00:00
|
|
|
|
const paths = require("./paths");
|
2018-02-17 21:28:47 +00:00
|
|
|
|
|
2018-11-01 18:33:03 +00:00
|
|
|
|
/*:: import type {GitState} from "../src/core/version"; */
|
2018-11-02 01:11:48 +00:00
|
|
|
|
/*:: import type {RepoIdRegistry} from "../src/core/repoIdRegistry"; */
|
2018-08-16 20:38:13 +00:00
|
|
|
|
|
2018-02-17 21:28:47 +00:00
|
|
|
|
// Make sure that including paths.js after env.js will read .env variables.
|
2018-02-18 16:13:29 +00:00
|
|
|
|
delete require.cache[require.resolve("./paths")];
|
2018-02-17 21:28:47 +00:00
|
|
|
|
|
|
|
|
|
const NODE_ENV = process.env.NODE_ENV;
|
|
|
|
|
if (!NODE_ENV) {
|
|
|
|
|
throw new Error(
|
2018-02-18 16:13:29 +00:00
|
|
|
|
"The NODE_ENV environment variable is required but was not specified."
|
2018-02-17 21:28:47 +00:00
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use
|
|
|
|
|
var dotenvFiles = [
|
|
|
|
|
`${paths.dotenv}.${NODE_ENV}.local`,
|
|
|
|
|
`${paths.dotenv}.${NODE_ENV}`,
|
|
|
|
|
// Don't include `.env.local` for `test` environment
|
|
|
|
|
// since normally you expect tests to produce the same
|
|
|
|
|
// results for everyone
|
2018-05-08 21:56:06 +00:00
|
|
|
|
...(NODE_ENV !== "test" ? [`${paths.dotenv}.local`] : []),
|
2018-02-17 21:28:47 +00:00
|
|
|
|
paths.dotenv,
|
2018-05-08 21:56:06 +00:00
|
|
|
|
];
|
2018-02-17 21:28:47 +00:00
|
|
|
|
|
|
|
|
|
// Load environment variables from .env* files. Suppress warnings using silent
|
|
|
|
|
// if this file is missing. dotenv will never modify any environment variables
|
|
|
|
|
// that have already been set. Variable expansion is supported in .env files.
|
|
|
|
|
// https://github.com/motdotla/dotenv
|
|
|
|
|
// https://github.com/motdotla/dotenv-expand
|
2018-02-18 16:13:29 +00:00
|
|
|
|
dotenvFiles.forEach((dotenvFile) => {
|
2018-02-17 21:28:47 +00:00
|
|
|
|
if (fs.existsSync(dotenvFile)) {
|
2018-02-18 16:13:29 +00:00
|
|
|
|
require("dotenv-expand")(
|
|
|
|
|
require("dotenv").config({
|
2018-02-17 21:28:47 +00:00
|
|
|
|
path: dotenvFile,
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// We support resolving modules according to `NODE_PATH`.
|
|
|
|
|
// This lets you use absolute paths in imports inside large monorepos:
|
|
|
|
|
// https://github.com/facebookincubator/create-react-app/issues/253.
|
|
|
|
|
// It works similar to `NODE_PATH` in Node itself:
|
|
|
|
|
// https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders
|
|
|
|
|
// Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored.
|
|
|
|
|
// Otherwise, we risk importing Node.js core modules into an app instead of Webpack shims.
|
|
|
|
|
// https://github.com/facebookincubator/create-react-app/issues/1023#issuecomment-265344421
|
|
|
|
|
// We also resolve them to make sure all tools using them work consistently.
|
|
|
|
|
const appDirectory = fs.realpathSync(process.cwd());
|
2018-02-18 16:13:29 +00:00
|
|
|
|
process.env.NODE_PATH = (process.env.NODE_PATH || "")
|
2018-02-17 21:28:47 +00:00
|
|
|
|
.split(path.delimiter)
|
2018-02-18 16:13:29 +00:00
|
|
|
|
.filter((folder) => folder && !path.isAbsolute(folder))
|
|
|
|
|
.map((folder) => path.resolve(appDirectory, folder))
|
2018-02-17 21:28:47 +00:00
|
|
|
|
.join(path.delimiter);
|
|
|
|
|
|
2018-08-16 20:38:13 +00:00
|
|
|
|
// Get the state of the SourceCred Git repository. This requires that
|
|
|
|
|
// Git be installed. If this fails for you, please install Git.
|
|
|
|
|
//
|
|
|
|
|
// If the dependency on Git becomes a problem, we can consider making
|
|
|
|
|
// this optional. However, note that this computation is performed at
|
|
|
|
|
// build time, so end users of SourceCred as a library or application
|
|
|
|
|
// should not need this dependency.
|
Fix flakey CI memory issues (#1230)
Ever since I upgraded all of the dependencies, we've been having
regular CI failures, which seem to share a common root cause of memory
exhaustion. Here are some examples: [1], [2].
[1]: https://circleci.com/gh/sourcecred/sourcecred/1246
[2]: https://circleci.com/gh/sourcecred/sourcecred/1239
After some experimentation, I've found that we can solve the
issue by ensuring that jest runs on its own in CI, so that it doesn't
contend with other tests for memories. Also, I reduce its max workers to
2, which matches the number of CPUs in the CircleCI containers.
Unfortunately, this does increase our build time. The postcommit (non
full) test now takes 45-60s (up from 30-50s), and the full test is also
a little slower. However, building in about one minute is still
acceptably fast, and having regular flakey test failures is not
acceptable, so this is still a win.
If we want to improve on this in the future, we should look into the git
shells getting spawned in `config/env.js`. I noticed that they were
often involved in the out-of-memory failures.
Also, I modified `.circleci/config.yml` so that any branch matching the
regular expression `/ci-.*/` will trigger a full build. That makes it
easier to test against CI failures.
Test plan: I ran about ~10 full builds with this change, and more with
similar variations, and they all passed. Verify that the full builds
that are run for this commit also all pass! Also, verify that running
yarn test locally has unchanged behavior, and running
`yarn test --ci` locally lets jest run to completion before running
any other test.
2019-07-16 00:51:14 +00:00
|
|
|
|
//
|
|
|
|
|
// Note: Calling `getGitState` inside test code seems to increase memory usage,
|
|
|
|
|
// potentially leading to CI out-of-memory failures. We should investigate
|
|
|
|
|
// this, and consider making `getGitState` a compile step for test code.
|
2018-08-16 20:38:13 +00:00
|
|
|
|
function getGitState() /*: GitState */ {
|
|
|
|
|
const env = {
|
env: pass parent PATH to Git invocations (#900)
Summary:
Our environment-stripping logic used in `config/env.js` to read the
current Git state included stripping the `PATH` environment variable.
This had the effect that the system Git executable would always be used
in preference to a user-installed version.
Test Plan:
Run `/usr/bin/git --version`, and then install a different version of
Git. (For instance, check out an old tag, then `make && make install`
from the `git/git` repository.) Then, add
```js
console.log(execFileSync("git", ["--version"], {env}).toString());
```
to `getGitState` in `config/env.js`, and run
NODE_ENV=development node ./config/env.js
Note that this prints the version of the system Git before this change,
and the user Git after this change.
Alternately, local-install a version of Git earlier than 2.6.0, and note
that `yarn unit` now _fails_ because the `--date=format:…` syntax is not
known to such versions of Git. Prior to this commit, the tests would
pass as long as the system Git were more recent.
wchargin-branch: env-git-path
2018-10-02 00:52:56 +00:00
|
|
|
|
GIT_ATTR_NOSYSTEM: "1",
|
|
|
|
|
GIT_CONFIG_NOSYSTEM: "1",
|
2018-08-16 20:38:13 +00:00
|
|
|
|
LANG: "C",
|
|
|
|
|
LC_ALL: "C",
|
env: pass parent PATH to Git invocations (#900)
Summary:
Our environment-stripping logic used in `config/env.js` to read the
current Git state included stripping the `PATH` environment variable.
This had the effect that the system Git executable would always be used
in preference to a user-installed version.
Test Plan:
Run `/usr/bin/git --version`, and then install a different version of
Git. (For instance, check out an old tag, then `make && make install`
from the `git/git` repository.) Then, add
```js
console.log(execFileSync("git", ["--version"], {env}).toString());
```
to `getGitState` in `config/env.js`, and run
NODE_ENV=development node ./config/env.js
Note that this prints the version of the system Git before this change,
and the user Git after this change.
Alternately, local-install a version of Git earlier than 2.6.0, and note
that `yarn unit` now _fails_ because the `--date=format:…` syntax is not
known to such versions of Git. Prior to this commit, the tests would
pass as long as the system Git were more recent.
wchargin-branch: env-git-path
2018-10-02 00:52:56 +00:00
|
|
|
|
PATH: process.env.PATH,
|
2018-08-16 20:38:13 +00:00
|
|
|
|
TZ: "UTC",
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const diffIndex = spawnSync(
|
|
|
|
|
"git",
|
|
|
|
|
["-C", __dirname, "diff-index", "--quiet", "HEAD", "--"],
|
|
|
|
|
{env}
|
|
|
|
|
);
|
|
|
|
|
const dirty = diffIndex.status !== 0;
|
|
|
|
|
if (diffIndex.status !== 0 && diffIndex.status !== 1) {
|
|
|
|
|
throw new Error(diffIndex.status + ": " + diffIndex.stderr.toString());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const commitHash = execFileSync(
|
|
|
|
|
"git",
|
|
|
|
|
["-C", __dirname, "rev-parse", "--short=12", "--verify", "HEAD"],
|
|
|
|
|
{env}
|
|
|
|
|
)
|
|
|
|
|
.toString()
|
|
|
|
|
.trim();
|
|
|
|
|
|
2018-10-02 01:07:52 +00:00
|
|
|
|
const iso8601Timestamp = execFileSync(
|
2018-08-16 20:38:13 +00:00
|
|
|
|
"git",
|
|
|
|
|
[
|
|
|
|
|
"-C",
|
|
|
|
|
__dirname,
|
|
|
|
|
"show",
|
|
|
|
|
"--no-patch",
|
|
|
|
|
"--format=%cd",
|
2018-10-02 01:07:52 +00:00
|
|
|
|
"--date=iso8601",
|
2018-08-16 20:38:13 +00:00
|
|
|
|
commitHash,
|
|
|
|
|
],
|
|
|
|
|
{env}
|
|
|
|
|
)
|
|
|
|
|
.toString()
|
|
|
|
|
.trim();
|
2018-10-02 01:07:52 +00:00
|
|
|
|
const commitDate = new Date(iso8601Timestamp);
|
|
|
|
|
function zeroPad(number /*: number */, length /*: number */) /*: string */ {
|
|
|
|
|
return String(number).padStart(length, "0");
|
|
|
|
|
}
|
|
|
|
|
const commitTimestamp = [
|
|
|
|
|
zeroPad(commitDate.getFullYear(), 4),
|
2018-10-31 00:24:39 +00:00
|
|
|
|
zeroPad(commitDate.getMonth() + 1, 2),
|
|
|
|
|
zeroPad(commitDate.getDate(), 2),
|
2018-10-02 01:07:52 +00:00
|
|
|
|
"-",
|
|
|
|
|
zeroPad(commitDate.getHours(), 2),
|
|
|
|
|
zeroPad(commitDate.getMinutes(), 2),
|
|
|
|
|
].join("");
|
2018-08-16 20:38:13 +00:00
|
|
|
|
|
|
|
|
|
return {commitHash, commitTimestamp, dirty};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const SOURCECRED_GIT_STATE = stringify(getGitState());
|
|
|
|
|
process.env.SOURCECRED_GIT_STATE = SOURCECRED_GIT_STATE;
|
|
|
|
|
|
2019-07-16 18:31:11 +00:00
|
|
|
|
function getClientEnvironment(projectIds /*: $ReadOnlyArray<string> | null*/) {
|
2018-08-16 18:27:37 +00:00
|
|
|
|
const raw = {};
|
|
|
|
|
// Useful for determining whether we’re running in production mode.
|
|
|
|
|
// Most importantly, it switches React into the correct mode.
|
|
|
|
|
raw.NODE_ENV = process.env.NODE_ENV || "development";
|
2018-11-01 19:38:18 +00:00
|
|
|
|
// Used by `src/core/version.js`.
|
2018-08-16 20:38:13 +00:00
|
|
|
|
raw.SOURCECRED_GIT_STATE = SOURCECRED_GIT_STATE;
|
2018-11-01 19:38:18 +00:00
|
|
|
|
// Optional. Used by `src/homepage/routeData.js`
|
2019-07-16 18:31:11 +00:00
|
|
|
|
raw.PROJECT_IDS = stringify(projectIds);
|
2018-08-16 18:27:37 +00:00
|
|
|
|
|
|
|
|
|
// Stringify all values so we can feed into Webpack's DefinePlugin.
|
|
|
|
|
const stringified = {"process.env": {}};
|
2018-08-31 23:34:18 +00:00
|
|
|
|
const individuallyStringified = {};
|
2018-08-16 18:27:37 +00:00
|
|
|
|
for (const key of Object.keys(raw)) {
|
2018-08-31 23:34:18 +00:00
|
|
|
|
const value = JSON.stringify(raw[key]);
|
|
|
|
|
stringified["process.env"][key] = value;
|
|
|
|
|
individuallyStringified["process.env." + key] = value;
|
2018-08-16 18:27:37 +00:00
|
|
|
|
}
|
2018-02-17 21:28:47 +00:00
|
|
|
|
|
2018-08-31 23:34:18 +00:00
|
|
|
|
return {raw, stringified, individuallyStringified};
|
2018-02-17 21:28:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
module.exports = getClientEnvironment;
|