UNPKG

32.9 kBJavaScriptView Raw
1"use strict";
2/*
3 * Copyright © 2018 Atomist, Inc.
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 */
18var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
19 function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
20 return new (P || (P = Promise))(function (resolve, reject) {
21 function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
22 function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
23 function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
24 step((generator = generator.apply(thisArg, _arguments || [])).next());
25 });
26};
27Object.defineProperty(exports, "__esModule", { value: true });
28/* tslint:disable:max-file-line-count */
29const appRoot = require("app-root-path");
30const cluster = require("cluster");
31const fs = require("fs-extra");
32const glob = require("glob");
33const stringify = require("json-stringify-safe");
34const _ = require("lodash");
35const os = require("os");
36const p = require("path");
37const semver = require("semver");
38const globals_1 = require("./globals");
39const WebSocketLifecycle_1 = require("./internal/transport/websocket/WebSocketLifecycle");
40const config_1 = require("./internal/util/config");
41const shutdown_1 = require("./internal/util/shutdown");
42const string_1 = require("./internal/util/string");
43const GraphClientFactory_1 = require("./spi/graph/GraphClientFactory");
44const httpClient_1 = require("./spi/http/httpClient");
45const wsClient_1 = require("./spi/http/wsClient");
46const statsdClient_1 = require("./spi/statsd/statsdClient");
47const logger_1 = require("./util/logger");
48const packageJson_1 = require("./util/packageJson");
49/**
50 * Generate defaults for various configuration option values. These
51 * will only be used if values are not provided by any source. Values
52 * not provided here will be `undefined`.
53 *
54 * @return default configuration
55 */
56function defaultConfiguration() {
57 const pj = packageJson_1.loadHostPackageJson() || {};
58 pj.name = pj.name || "atm-client-" + string_1.guid();
59 pj.version = pj.version || "0.0.0";
60 pj.keywords = pj.keywords || [];
61 const cfg = loadDefaultConfiguration();
62 cfg.name = pj.name;
63 cfg.version = pj.version;
64 cfg.keywords = pj.keywords;
65 cfg.application = pj.name.replace(/^@.*?\//, "");
66 return cfg;
67}
68exports.defaultConfiguration = defaultConfiguration;
69/**
70 * Exposes the configuration for lookup of configuration values.
71 * This is useful for components to obtain values eg. from configuration.custom
72 * like user provided secrets etc.
73 * @param {string} path the property path evaluated against the configuration instance
74 * @returns {T}
75 */
76function configurationValue(path = "", defaultValue) {
77 if (globals_1.automationClientInstance()) {
78 const conf = globals_1.automationClientInstance().configuration;
79 let value;
80 if (!path || path.length === 0) {
81 value = conf;
82 }
83 else {
84 value = _.get(conf, path);
85 }
86 // tslint:disable-next-line:no-null-keyword
87 if (value !== null && value !== undefined) {
88 return value;
89 }
90 }
91 if (defaultValue !== undefined) {
92 return defaultValue;
93 }
94 throw new Error(`Required @Value '${path}' not available`);
95}
96exports.configurationValue = configurationValue;
97/**
98 * Return the default configuration based on NODE_ENV or ATOMIST_ENV.
99 * ATOMIST_ENV takes precedence if it is set.
100 */
101function loadDefaultConfiguration() {
102 const cfg = exports.LocalDefaultConfiguration;
103 let envSpecificCfg = {};
104 const nodeEnv = process.env.ATOMIST_ENV || process.env.NODE_ENV;
105 if (nodeEnv === "production") {
106 envSpecificCfg = exports.ProductionDefaultConfiguration;
107 cfgLog("production default");
108 }
109 else if (nodeEnv === "staging" || nodeEnv === "testing") {
110 envSpecificCfg = exports.TestingDefaultConfiguration;
111 cfgLog("testing default");
112 }
113 else if (nodeEnv) {
114 cfg.environment = nodeEnv;
115 }
116 // For internal testing use to switch to staging apis
117 if (process.env.ATOMIST_ENDPOINTS === "staging") {
118 cfg.endpoints = {
119 graphql: "https://automation-staging.atomist.services/graphql/team",
120 api: "https://automation-staging.atomist.services/registration",
121 auth: "https://api-staging.atomist.services/v2/auth",
122 };
123 }
124 return mergeConfigs(cfg, envSpecificCfg);
125}
126/**
127 * Return Atomist user configuration directory.
128 */
129function userConfigDir() {
130 return p.join(os.homedir(), ".atomist");
131}
132/**
133 * Return user automation client configuration path.
134 */
135function userConfigPath() {
136 const clientConfigFile = "client.config.json";
137 return p.join(userConfigDir(), clientConfigFile);
138}
139exports.userConfigPath = userConfigPath;
140/**
141 * Return user automation client configuration paths including
142 * such referenced by configuration profiles.
143 */
144function userConfigPaths() {
145 const configPaths = [userConfigPath()];
146 const profiles = process.env.ATOMIST_CONFIG_PROFILE || process.env.ATOMIST_CONFIG_PROFILES;
147 if (!!profiles) {
148 for (const profile of profiles.split(",")) {
149 configPaths.push(p.join(userConfigDir(), `client.config-${profile}.json`));
150 }
151 }
152 return configPaths;
153}
154exports.userConfigPaths = userConfigPaths;
155/**
156 * Write user config securely, creating directories as necessary.
157 */
158function writeUserConfig(cfg) {
159 const cfgDir = userConfigDir();
160 return fs.ensureDir(cfgDir)
161 .then(() => fs.chmod(cfgDir, 0o700))
162 .then(() => fs.writeJson(userConfigPath(), cfg, {
163 spaces: 2,
164 encoding: "utf8",
165 mode: 0o600,
166 }));
167}
168exports.writeUserConfig = writeUserConfig;
169/**
170 * Read and return user config from UserConfigFile.
171 */
172function getUserConfig() {
173 const configPaths = userConfigPaths();
174 const userConfigs = [];
175 for (const configPath of configPaths) {
176 if (fs.existsSync(configPath)) {
177 try {
178 const cfg = fs.readJsonSync(configPath);
179 // user config should not have name or version
180 if (cfg.name) {
181 delete cfg.name;
182 }
183 if (cfg.version) {
184 delete cfg.version;
185 }
186 userConfigs.push(cfg);
187 }
188 catch (e) {
189 e.message = `Failed to read user config: ${e.message}`;
190 throw e;
191 }
192 }
193 }
194 if (userConfigs.length > 0) {
195 return mergeConfigs({}, ...userConfigs);
196 }
197 else {
198 return undefined;
199 }
200}
201exports.getUserConfig = getUserConfig;
202/**
203 * Log the loading of a configuration
204 *
205 * @param source name of configuration source
206 */
207function cfgLog(source) {
208 if (cluster.isMaster) {
209 logger_1.logger.debug(`Loading configuration '${source}'`);
210 }
211}
212/**
213 * Overwrite values in the former configuration with values in the
214 * latter. The start object is modified.
215 *
216 * @param obj starting configuration
217 * @param override configuration values to add/override those in start
218 * @return resulting merged configuration
219 */
220function mergeConfigs(obj, ...sources) {
221 return _.mergeWith(obj, ...sources, (objValue, srcValue) => {
222 if (_.isArray(srcValue)) {
223 return srcValue;
224 }
225 });
226}
227exports.mergeConfigs = mergeConfigs;
228/**
229 * Overwrite values in the former configuration with values in the
230 * latter. The start object is modified. Arrays are concatenated.
231 *
232 * @param obj starting configuration
233 * @param override configuration values to add/override those in start
234 * @return resulting merged configuration
235 */
236function deepMergeConfigs(obj, ...sources) {
237 return _.mergeWith(obj, ...sources, (objValue, srcValue) => {
238 if (_.isArray(objValue) && srcValue) {
239 return objValue.concat(srcValue);
240 }
241 });
242}
243exports.deepMergeConfigs = deepMergeConfigs;
244/**
245 * Merge a user's global and proper per-module configuration, if it
246 * exists. Values from the per-module configuration take precedence
247 * over the user-wide values. Per-module configuration is gotten from
248 * the first per-module configuration that matches name and,
249 * optionally, the version is within the per-module configuration's
250 * version range. A module configuration without a version range
251 * matches the named module with any version. If no version is
252 * provided, any version range is satisfied, meaning the first
253 * per-module configuration with a matching name is used. If no name
254 * is provide, only the user configuration is loaded. The first
255 * per-module match is used. This means if you have multiple
256 * configurations for the same named module and you want to include a
257 * default configuration for that module, put a configuration without
258 * a version range _after_ all the configurations with version ranges.
259 * Note that only values from the first per-module match are used.
260 *
261 * @param userConfig the user's configuration, which may include per-module configuration
262 * @param name automation client package name to load as module config if it exists
263 * @param version automation client package version to load as module config if
264 * version satifies module config version range
265 * @return the merged module and user configuration
266 */
267function resolveModuleConfig(userConfig, name, version) {
268 const cfg = {};
269 if (userConfig) {
270 cfgLog(userConfigPath());
271 const uc = _.cloneDeep(userConfig);
272 let mc = {};
273 if (userConfig.modules) {
274 delete uc.modules;
275 if (name) {
276 let modCfg;
277 const moduleConfigs = userConfig.modules.filter(m => m.name === name);
278 if (version) {
279 modCfg = moduleConfigs.find(m => !m.version || semver.satisfies(version, m.version));
280 }
281 else if (moduleConfigs.length > 0) {
282 modCfg = moduleConfigs[0];
283 }
284 if (modCfg) {
285 cfgLog("module");
286 if (modCfg.name) {
287 delete modCfg.name;
288 }
289 if (modCfg.version) {
290 delete modCfg.version;
291 }
292 mc = modCfg;
293 }
294 }
295 }
296 mergeConfigs(cfg, uc, mc);
297 }
298 return cfg;
299}
300exports.resolveModuleConfig = resolveModuleConfig;
301/**
302 * Try to read user config, overriding its values with a per-module
303 * configuration that matches this automation.
304 *
305 * @param name automation client package name to load as module config if it exists
306 * @param version automation client package version to load as module config if
307 * version satifies module config version range
308 * @return module-specific config with user config supplying defaults
309 */
310function loadUserConfiguration(name, version) {
311 const userConfig = getUserConfig();
312 return resolveModuleConfig(userConfig, name, version);
313}
314exports.loadUserConfiguration = loadUserConfiguration;
315/**
316 * Load the automation configuration from the configuration object
317 * exported from cfgPath and return it. If no configuration path is
318 * provided, the package will be searched for a file named
319 * atomist.config.js. If no atomist.config.js is found, an empty
320 * object is returned. If more than one is found, an exception is
321 * thrown.
322 *
323 * @param cfgPath location of automation configuration
324 * @return automation configuration or undefined
325 */
326function loadAutomationConfig(configPath) {
327 return __awaiter(this, void 0, void 0, function* () {
328 let cfgPath = configPath;
329 if (!cfgPath) {
330 const cfgFile = "atomist.config.js";
331 const files = glob.sync(`${appRoot.path}/**/${cfgFile}`, { ignore: ["**/{.git,node_modules}/**"] });
332 if (files.length === 1) {
333 cfgPath = files[0];
334 }
335 else if (files.length > 1) {
336 throw new Error(`More than one automation configuration found in package: ${files.join(", ")}`);
337 }
338 }
339 if (cfgPath) {
340 try {
341 let cfg = require(cfgPath).configuration;
342 if (cfg instanceof Promise) {
343 cfg = yield cfg;
344 }
345 cfgLog(cfgPath);
346 return cfg;
347 }
348 catch (e) {
349 e.message = `Failed to load ${cfgPath}.configuration: ${e.message}`;
350 throw e;
351 }
352 }
353 return undefined;
354 });
355}
356exports.loadAutomationConfig = loadAutomationConfig;
357/**
358 * Load the automation configuration from the configuration objects
359 * exported and merged by all index.js files in the automation client.
360 *
361 * @return automation configuration
362 */
363function loadIndexConfig() {
364 return __awaiter(this, void 0, void 0, function* () {
365 const cfgFile = "index.js";
366 const files = glob.sync(`${appRoot.path}/**/${cfgFile}`, { ignore: ["**/{.git,node_modules}/**"] });
367 if (files.length > 0) {
368 const cfgs = [];
369 for (const f of files) {
370 try {
371 let cfg = require(f).configuration || {};
372 if (cfg instanceof Promise) {
373 cfg = yield cfg;
374 }
375 cfgLog(f);
376 cfgs.push(cfg);
377 }
378 catch (e) {
379 e.message = `Failed to load ${f}.configuration: ${e.message}`;
380 throw e;
381 }
382 }
383 return deepMergeConfigs({}, ...cfgs);
384 }
385 return {};
386 });
387}
388exports.loadIndexConfig = loadIndexConfig;
389/**
390 * Load configuration from the file defined by the ATOMIST_CONFIG_PATH
391 * environment variable, if it the variable is defined and the file
392 * exists, and return it. The contents of the ATOMIST_CONFIG_PATH
393 * file should be serialized JSON of AutomationServerOptions. If the
394 * environment variable is not defined or the file path specified by
395 * its value cannot be read as JSON, an empty object is returned.
396 *
397 * @return automation server options
398 */
399function loadAtomistConfigPath() {
400 let cfg = {};
401 if (process.env.ATOMIST_CONFIG_PATH) {
402 try {
403 cfg = fs.readJsonSync(process.env.ATOMIST_CONFIG_PATH);
404 cfgLog("ATOMIST_CONFIG_PATH");
405 }
406 catch (e) {
407 e.message = `Failed to read ATOMIST_CONFIG_PATH: ${e.message}`;
408 throw e;
409 }
410 }
411 return cfg;
412}
413exports.loadAtomistConfigPath = loadAtomistConfigPath;
414/**
415 * Load configuration from the ATOMIST_CONFIG environment variable, if
416 * it the variable is defined, and merge it into the passed in
417 * configuration. The value of the ATOMIST_CONFIG environment
418 * variable should be serialized JSON of AutomationServerOptions. The
419 * values from the environment variable will override values in the
420 * passed in configuration. If the environment variable is not
421 * defined, the passed in configuration is returned unchanged.
422 *
423 * @return automation server options
424 */
425function loadAtomistConfig() {
426 let cfg = {};
427 if (process.env.ATOMIST_CONFIG) {
428 try {
429 cfg = JSON.parse(process.env.ATOMIST_CONFIG);
430 cfgLog("ATOMIST_CONFIG");
431 }
432 catch (e) {
433 e.message = `Failed to parse contents of ATOMIST_CONFIG environment variable: ${e.message}`;
434 throw e;
435 }
436 }
437 return cfg;
438}
439exports.loadAtomistConfig = loadAtomistConfig;
440/**
441 * Examine environment, config, and cfg for Atomist workspace IDs.
442 * The ATOMIST_WORKSPACES environment variable takes precedence over
443 * the configuration "workspaceIds", which takes precedence over
444 * cfg.workspaceId, which may be undefined, null, or an empty array.
445 * If the ATOMIST_WORKSPACES environment variable is not set,
446 * workspaceIds is not set in config, and workspaceIds is falsey in
447 * cfg and teamIds is resolvable from the configuration, workspaceIds
448 * is set to teamIds.
449 *
450 * @param cfg current configuration, whose workspaceIds and teamIds
451 * properties may be modified by this function
452 * @return the resolved workspace IDs
453 */
454function resolveWorkspaceIds(cfg) {
455 if (process.env.ATOMIST_WORKSPACES) {
456 cfg.workspaceIds = process.env.ATOMIST_WORKSPACES.split(",");
457 }
458 else if (config_1.config("workspaceIds")) {
459 cfg.workspaceIds = config_1.config("workspaceIds");
460 }
461 return cfg.workspaceIds;
462}
463exports.resolveWorkspaceIds = resolveWorkspaceIds;
464/**
465 * Resolve a value from a environment variables or configuration keys.
466 * The environment variables are checked in order and take precedence
467 * over the configuration key, which are also checked in order. If
468 * no truthy values are found, undefined is returned.
469 *
470 * @param environmentVariables environment variables to check
471 * @param configKeyPaths configuration keys, as JSON paths, to check
472 * @param defaultValue value to use if no environment variables or config keys have values
473 * @return first truthy value found, or defaultValue
474 */
475function resolveConfigurationValue(environmentVariables, configKeyPaths, defaultValue) {
476 for (const ev of environmentVariables) {
477 if (process.env[ev]) {
478 return process.env[ev];
479 }
480 }
481 for (const cv of configKeyPaths) {
482 if (config_1.config(cv)) {
483 return config_1.config(cv);
484 }
485 }
486 return defaultValue;
487}
488exports.resolveConfigurationValue = resolveConfigurationValue;
489/**
490 * Resolve the HTTP port from the environment and configuration. The
491 * PORT environment variable takes precedence over the config value.
492 */
493function resolvePort(cfg) {
494 if (process.env.PORT) {
495 cfg.http.port = parseInt(process.env.PORT, 10);
496 }
497 return cfg.http.port;
498}
499exports.resolvePort = resolvePort;
500const EnvironmentVariablePrefix = "ATOMIST_";
501/**
502 * Resolve ATOMIST_ environment variables and add them to config.
503 * Variables of like ATOMIST_custom_foo_bar will be converted to
504 * a json path of custom.foo.bar.
505 * @param {Configuration} cfg
506 */
507function resolveEnvironmentVariables(cfg) {
508 for (const key in process.env) {
509 if (key.startsWith(EnvironmentVariablePrefix)
510 && process.env.hasOwnProperty(key)) {
511 const cleanKey = key.slice(EnvironmentVariablePrefix.length).split("_").join(".");
512 if (cleanKey[0] !== cleanKey[0].toUpperCase()) {
513 _.update(cfg, cleanKey, () => process.env[key]);
514 }
515 }
516 }
517}
518exports.resolveEnvironmentVariables = resolveEnvironmentVariables;
519/**
520 * Resolve placeholders against the process.env.
521 * Placeholders should be of form ${ENV_VAR}. Placeholders support default values
522 * in case they aren't defined: ${ENV_VAR:default value}
523 */
524function resolvePlaceholders(cfg, replacer = resolvePlaceholder) {
525 return __awaiter(this, void 0, void 0, function* () {
526 yield resolvePlaceholdersRecursively(cfg, [], replacer);
527 });
528}
529exports.resolvePlaceholders = resolvePlaceholders;
530function resolvePlaceholdersRecursively(obj, visited, replacer) {
531 return __awaiter(this, void 0, void 0, function* () {
532 if (!visited.includes(obj)) {
533 visited.push(obj);
534 for (const property in obj) {
535 if (_.has(obj, property)) {
536 if (typeof obj[property] === "object") {
537 yield resolvePlaceholdersRecursively(obj[property], visited, replacer);
538 }
539 else if (typeof obj[property] === "string") {
540 obj[property] = yield replacer(obj[property]);
541 }
542 }
543 }
544 }
545 });
546}
547const PlaceholderExpression = /\$\{([.a-zA-Z_-]+)([.:0-9a-zA-Z-_ \" ]+)*\}/g;
548function resolvePlaceholder(value) {
549 return __awaiter(this, void 0, void 0, function* () {
550 if (!PlaceholderExpression.test(value)) {
551 return value;
552 }
553 PlaceholderExpression.lastIndex = 0;
554 let currentValue = value;
555 let result;
556 // tslint:disable-next-line:no-conditional-assignment
557 while (result = PlaceholderExpression.exec(currentValue)) {
558 const fm = result[0];
559 const envValue = process.env[result[1]];
560 const defaultValue = result[2] ? result[2].trim().slice(1) : undefined;
561 if (envValue) {
562 currentValue = currentValue.split(fm).join(envValue);
563 }
564 else if (defaultValue) {
565 currentValue = currentValue.split(fm).join(defaultValue);
566 }
567 else {
568 throw new Error(`Environment variable '${result[1]}' is not defined`);
569 }
570 }
571 return currentValue;
572 });
573}
574/**
575 * Invoke postProcessors on the provided configuration.
576 */
577function invokePostProcessors(cfg) {
578 return cfg.postProcessors.reduce((pp, fp) => pp.then(fp), Promise.resolve(cfg));
579}
580exports.invokePostProcessors = invokePostProcessors;
581/**
582 * Make sure final configuration has the minimum configuration it
583 * needs. It will throw an error if required properties are missing.
584 *
585 * @param cfg final configuration
586 */
587function validateConfiguration(cfg) {
588 if (!cfg) {
589 throw new Error(`no configuration defined`);
590 }
591 const errors = [];
592 if (!cfg.name) {
593 errors.push("you must set a 'name' property in your configuration");
594 }
595 if (!cfg.version) {
596 errors.push("you must set a 'version' property in your configuration");
597 }
598 if (!cfg.apiKey) {
599 logger_1.logger.info("To obtain an 'apiKey' visit https://app.atomist.com/apikeys and run 'atomist config' " +
600 "to configure the apiKey in your local configuration");
601 errors.push("you must set an 'apiKey' property in your configuration");
602 }
603 cfg.workspaceIds = cfg.workspaceIds || [];
604 cfg.groups = cfg.groups || [];
605 if (cfg.workspaceIds.length < 1 && cfg.groups.length < 1) {
606 errors.push("you must either provide an array of 'groups' in your configuration or, more likely, provide " +
607 "an array of 'workspaceIds' in your configuration or set the ATOMIST_WORKSPACES environment variable " +
608 "to a comma-separated list of workspace IDs");
609 }
610 if (cfg.workspaceIds.length > 0 && cfg.groups.length > 0) {
611 errors.push("you cannot specify both 'workspaceIds' and 'groups' in your configuration, you must set one " +
612 "to an empty array");
613 }
614 if (errors.length > 0) {
615 const msg = `Configuration (${stringify(cfg, string_1.obfuscateJson)}) is not correct: ${errors.join("; ")}`;
616 throw new Error(msg);
617 }
618}
619exports.validateConfiguration = validateConfiguration;
620/**
621 * Load and populate the automation configuration. The configuration
622 * is loaded from several locations with the following precedence from
623 * highest to lowest.
624 *
625 * 0. Recognized environment variables (see below)
626 * 1. The value of the ATOMIST_CONFIG environment variable, parsed as
627 * JSON and cast to AutomationServerOptions
628 * 2. The contents of the ATOMIST_CONFIG_PATH file as AutomationServerOptions
629 * 3. The contents of the user's client.config.json as UserConfig
630 * resolving user and per-module configuration into Configuration
631 * 4. The automation's index.js (or atomist.config.js) exported configuration as
632 * Configuration
633 * 5. ProductionDefaultConfiguration if ATOMIST_ENV or NODE_ENV is set
634 * to "production" or TestingDefaultConfiguration if ATOMIST_ENV or
635 * NODE_ENV is set to "staging" or "testing", with ATOMIST_ENV
636 * taking precedence over NODE_ENV.
637 * 6. LocalDefaultConfiguration
638 *
639 * If any of the sources are missing, they are ignored. Any truthy
640 * configuration values specified by sources of higher precedence
641 * cause any values provided by sources of lower precedence to be
642 * ignored. Arrays are replaced, not merged. Typically the only
643 * required values in the configuration for a successful registration
644 * are the apiKey and non-empty workspaceIds.
645 *
646 * Placeholder of the form `${ENV_VARIABLE}` in string configuration
647 * values will get resolved against the environment. The resolution
648 * happens at the very end when all configs have been merged.
649 *
650 * The configuration exported from the index.js (or atomist.config.js) is modified
651 * to contain the final configuration values and returned from this
652 * function.
653 *
654 * @param cfgPath path to file exporting the configuration object, if
655 * not provided the package is searched for one
656 * @return merged configuration object
657 */
658function loadConfiguration(cfgPath) {
659 return __awaiter(this, void 0, void 0, function* () {
660 // Register the logger globally so that downstream modules can see it
661 global.__logger = logger_1.logger;
662 let cfg;
663 try {
664 const defCfg = defaultConfiguration();
665 const autoCfg = (yield loadAutomationConfig(cfgPath)) || (yield loadIndexConfig());
666 const userCfg = loadUserConfiguration(defCfg.name, defCfg.version);
667 const atmPathCfg = loadAtomistConfigPath();
668 const atmCfg = loadAtomistConfig();
669 cfg = mergeConfigs({}, defCfg, autoCfg, userCfg, atmPathCfg, atmCfg);
670 resolveWorkspaceIds(cfg);
671 resolvePort(cfg);
672 }
673 catch (e) {
674 logger_1.logger.error(`Failed to load configuration: ${e.message}`);
675 if (e.stack) {
676 logger_1.logger.error(`Stack trace:\n${e.stack}`);
677 }
678 return Promise.reject(e);
679 }
680 return invokePostProcessors(cfg)
681 .then(completeCfg => {
682 completeCfg.postProcessors = [];
683 resolveEnvironmentVariables(completeCfg);
684 return completeCfg;
685 })
686 .then(completeCfg => {
687 return resolvePlaceholders(completeCfg)
688 .then(() => completeCfg);
689 })
690 .then(completeCfg => {
691 validateConfiguration(completeCfg);
692 return completeCfg;
693 });
694 });
695}
696exports.loadConfiguration = loadConfiguration;
697/**
698 * Default set of regular expressions used to remove sensitive
699 * information from messages and logs. The entries are applied in
700 * order, so more specific regular expressions should be placed
701 * earlier in the list to avoid a shorter replacement preventing a
702 * longer replacement from being applied.
703 */
704exports.DEFAULT_REDACTION_PATTERNS = [
705 {
706 regexp: /\b[A-F0-9]{64}\b/g,
707 replacement: "[ATOMIST_API_KEY]",
708 },
709 {
710 regexp: /[1-9][0-9]+-[0-9a-zA-Z]{40}/g,
711 replacement: "[TWITTER_ACCESS_TOKEN]",
712 },
713 {
714 regexp: /EAACEdEose0cBA[0-9A-Za-z]+/g,
715 replacement: "[FACEBOOK_ACCESS_TOKEN]",
716 },
717 {
718 regexp: /AIza[0-9A-Za-z\-_]{35}/g,
719 replacement: "[GOOGLE_API_KEY]",
720 },
721 {
722 regexp: /[0-9]+-[0-9A-Za-z_]{32}\.apps\.googleusercontent\.com/g,
723 replacement: "[GOOGLE_OAUTH_ID]",
724 },
725 {
726 regexp: /sk_live_[0-9a-z]{32}/g,
727 replacement: "[PICATIC_API_KEY|",
728 },
729 {
730 regexp: /sk_live_[0-9a-zA-Z]{24}/g,
731 replacement: "[STRIPE_REGULAR_API_KEY]",
732 },
733 {
734 regexp: /rk_live_[0-9a-zA-Z]{24}/g,
735 replacement: "[STRIPE_RESTRICTED_API_KEY]",
736 },
737 {
738 regexp: /sq0atp-[0-9A-Za-z\-_]{22}/g,
739 replacement: "[SQUARE_OAUTH_TOKEN]",
740 },
741 {
742 regexp: /sq0csp-[0-9A-Za-z\-_]{43}/g,
743 replacement: "[SQUARE_OAUTH_SECRET]",
744 },
745 {
746 regexp: /access_token\$production\$[0-9a-z]{16}\$[0-9a-f]{32}/g,
747 replacement: "[BRAINTREE_ACCESS_TOKEN]",
748 },
749 {
750 regexp: /amzn\.mws\.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/g,
751 replacement: "[AMAZON_AUTH_TOKEN]",
752 },
753 {
754 regexp: /SK[0-9a-fA-F]{32}/g,
755 replacement: "[TWILLIO_API_KEY]",
756 },
757 {
758 regexp: /key-[0-9a-zA-Z]{32}/g,
759 replacement: "[MAILGUN_KEY]",
760 },
761 {
762 regexp: /[0-9a-f]{32}-us[0-9]{1,2}/g,
763 replacement: "[MAILCHIMP_API_KEY]",
764 },
765 {
766 regexp: /\bAK[0-9A-Z]{18}\b/g,
767 replacement: "[AMAZON_ACCESS_KEY]",
768 },
769 {
770 regexp: /\b(https?:\/\/)(?:v1\.)?[a-f0-9]{40}((?::x-oauth-basic)?@)/g,
771 replacement: "$1[GITHUB_TOKEN]$2",
772 },
773 {
774 // https://perishablepress.com/stop-using-unsafe-characters-in-urls/
775 // https://www.ietf.org/rfc/rfc3986.txt
776 regexp: /\b((?:ht|f|sm)tps?:\/\/[^:/?#\[\]@""<>{}|\\^``\s]+:)[^:/?#\[\]@""<>{}|\\^``\s]+@/g,
777 replacement: "$1[URL_PASSWORD]@",
778 },
779];
780/**
781 * Default configuration when running in neither testing or
782 * production.
783 */
784exports.LocalDefaultConfiguration = {
785 workspaceIds: [],
786 groups: [],
787 environment: "local",
788 policy: "ephemeral",
789 endpoints: {
790 api: "https://automation.atomist.com/registration",
791 graphql: "https://automation.atomist.com/graphql/team",
792 auth: "https://api.atomist.com/v2/auth",
793 },
794 http: {
795 enabled: true,
796 host: "0.0.0.0",
797 auth: {
798 basic: {
799 enabled: false,
800 },
801 bearer: {
802 enabled: false,
803 },
804 },
805 customizers: [],
806 client: {
807 factory: httpClient_1.DefaultHttpClientFactory,
808 },
809 },
810 ws: {
811 enabled: true,
812 client: {
813 factory: wsClient_1.DefaultWebSocketFactory,
814 },
815 termination: {
816 graceful: false,
817 gracePeriod: shutdown_1.defaultGracePeriod,
818 },
819 compress: false,
820 timeout: 30000,
821 lifecycle: new WebSocketLifecycle_1.QueuingWebSocketLifecycle(),
822 },
823 graphql: {
824 client: {
825 factory: GraphClientFactory_1.DefaultGraphClientFactory,
826 },
827 },
828 applicationEvents: {
829 enabled: false,
830 },
831 cluster: {
832 enabled: false,
833 },
834 logging: {
835 level: "info",
836 file: {
837 enabled: true,
838 level: "debug",
839 },
840 banner: {
841 enabled: true,
842 contributors: [],
843 },
844 },
845 statsd: {
846 enabled: false,
847 client: {
848 factory: statsdClient_1.DefaultStatsDClientFactory,
849 },
850 },
851 redact: {
852 log: true,
853 messages: true,
854 patterns: exports.DEFAULT_REDACTION_PATTERNS,
855 },
856 commands: undefined,
857 events: undefined,
858 ingesters: [],
859 listeners: [],
860 postProcessors: [],
861};
862/**
863 * Configuration defaults for production environments.
864 */
865exports.ProductionDefaultConfiguration = {
866 environment: "production",
867 policy: "durable",
868 http: {
869 port: 2866,
870 auth: {
871 basic: {
872 enabled: true,
873 },
874 bearer: {
875 enabled: true,
876 },
877 },
878 },
879 ws: {
880 termination: {
881 graceful: true,
882 },
883 compress: true,
884 },
885 applicationEvents: {
886 enabled: true,
887 },
888 cluster: {
889 enabled: true,
890 },
891 logging: {
892 level: "info",
893 file: {
894 enabled: false,
895 },
896 },
897 statsd: {
898 enabled: true,
899 },
900};
901/**
902 * Configuration defaults for pre-production environments.
903 */
904exports.TestingDefaultConfiguration = {
905 environment: "testing",
906 policy: "durable",
907 http: {
908 auth: {
909 basic: {
910 enabled: true,
911 },
912 bearer: {
913 enabled: true,
914 },
915 },
916 },
917 ws: {
918 termination: {
919 graceful: true,
920 },
921 compress: true,
922 },
923 applicationEvents: {
924 enabled: true,
925 },
926 cluster: {
927 enabled: true,
928 },
929 logging: {
930 level: "info",
931 file: {
932 enabled: false,
933 },
934 },
935 statsd: {
936 enabled: true,
937 },
938};
939//# sourceMappingURL=configuration.js.map
\No newline at end of file