UNPKG

32 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 fg = require("fast-glob");
32const fs = require("fs-extra");
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 = yield fg(`${appRoot.path}/**/${cfgFile}`, { ignore: [`${appRoot.path}/**/{.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 = yield fg(`${appRoot.path}/**/${cfgFile}`, { ignore: [`${appRoot.path}/**/{.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 config "workspaceIds", which takes precedence over
444 * cfg.workspaceId, which may be undefined, null, or an empty array.
445 *
446 * @param cfg current configuration, whose workspaceIds
447 * properties may be modified by this function
448 * @return the resolved workspace IDs
449 */
450function resolveWorkspaceIds(cfg) {
451 if (process.env.ATOMIST_WORKSPACES) {
452 cfg.workspaceIds = process.env.ATOMIST_WORKSPACES.split(",");
453 }
454 else if (config_1.config("workspaceIds")) {
455 cfg.workspaceIds = config_1.config("workspaceIds");
456 }
457 return cfg.workspaceIds;
458}
459exports.resolveWorkspaceIds = resolveWorkspaceIds;
460/**
461 * Resolve the HTTP port from the environment and configuration. The
462 * PORT environment variable takes precedence over the config value.
463 */
464function resolvePort(cfg) {
465 if (process.env.PORT) {
466 cfg.http.port = parseInt(process.env.PORT, 10);
467 }
468 return cfg.http.port;
469}
470exports.resolvePort = resolvePort;
471const EnvironmentVariablePrefix = "ATOMIST_";
472/**
473 * Resolve ATOMIST_ environment variables and add them to config.
474 * Variables of like ATOMIST_custom_foo_bar will be converted to
475 * a json path of custom.foo.bar.
476 * @param {Configuration} cfg
477 */
478function resolveEnvironmentVariables(cfg) {
479 for (const key in process.env) {
480 if (key.startsWith(EnvironmentVariablePrefix) && process.env.hasOwnProperty(key)) {
481 const cleanKey = key.slice(EnvironmentVariablePrefix.length).split("_").join(".");
482 if (cleanKey[0] !== cleanKey[0].toUpperCase()) {
483 _.update(cfg, cleanKey, () => process.env[key]);
484 }
485 }
486 }
487}
488exports.resolveEnvironmentVariables = resolveEnvironmentVariables;
489/**
490 * Resolve placeholders against the process.env.
491 * Placeholders should be of form ${ENV_VAR}. Placeholders support default values
492 * in case they aren't defined: ${ENV_VAR:default value}
493 */
494function resolvePlaceholders(cfg, replacer = resolvePlaceholder, visited = [cfg.sdm, cfg.local, cfg.events, cfg.commands, cfg.listeners]) {
495 return __awaiter(this, void 0, void 0, function* () {
496 yield resolvePlaceholdersRecursively(cfg, visited, replacer);
497 });
498}
499exports.resolvePlaceholders = resolvePlaceholders;
500function resolvePlaceholdersRecursively(obj, visited, replacer) {
501 return __awaiter(this, void 0, void 0, function* () {
502 if (!visited.includes(obj)) {
503 visited.push(obj);
504 for (const property in obj) {
505 if (_.has(obj, property)) {
506 if (typeof obj[property] === "object") {
507 yield resolvePlaceholdersRecursively(obj[property], visited, replacer);
508 }
509 else if (typeof obj[property] === "string") {
510 obj[property] = yield replacer(obj[property]);
511 }
512 }
513 }
514 }
515 });
516}
517const PlaceholderExpression = /\$\{([.a-zA-Z_-]+)([.:0-9a-zA-Z-_ \" ]+)*\}/g;
518function resolvePlaceholder(value) {
519 return __awaiter(this, void 0, void 0, function* () {
520 if (!PlaceholderExpression.test(value)) {
521 return value;
522 }
523 PlaceholderExpression.lastIndex = 0;
524 let currentValue = value;
525 let result;
526 // tslint:disable-next-line:no-conditional-assignment
527 while (result = PlaceholderExpression.exec(currentValue)) {
528 const fm = result[0];
529 const envValue = process.env[result[1]];
530 const defaultValue = result[2] ? result[2].trim().slice(1) : undefined;
531 if (envValue) {
532 currentValue = currentValue.split(fm).join(envValue);
533 }
534 else if (defaultValue) {
535 currentValue = currentValue.split(fm).join(defaultValue);
536 }
537 else {
538 throw new Error(`Environment variable '${result[1]}' is not defined`);
539 }
540 }
541 return currentValue;
542 });
543}
544/**
545 * Invoke postProcessors on the provided configuration.
546 */
547function invokePostProcessors(cfg) {
548 return cfg.postProcessors.reduce((pp, fp) => pp.then(fp), Promise.resolve(cfg));
549}
550exports.invokePostProcessors = invokePostProcessors;
551/**
552 * Make sure final configuration has the minimum configuration it
553 * needs. It will throw an error if required properties are missing.
554 *
555 * @param cfg final configuration
556 */
557function validateConfiguration(cfg) {
558 if (!cfg) {
559 throw new Error(`no configuration defined`);
560 }
561 const errors = [];
562 if (!cfg.name) {
563 errors.push("you must set a 'name' property in your configuration");
564 }
565 if (!cfg.version) {
566 errors.push("you must set a 'version' property in your configuration");
567 }
568 if (!cfg.apiKey) {
569 logger_1.logger.info("To obtain an 'apiKey' visit https://app.atomist.com/apikeys and run 'atomist config' " +
570 "to configure the apiKey in your local configuration");
571 errors.push("you must set an 'apiKey' property in your configuration");
572 }
573 cfg.workspaceIds = cfg.workspaceIds || [];
574 cfg.groups = cfg.groups || [];
575 if (cfg.workspaceIds.length < 1 && cfg.groups.length < 1) {
576 errors.push("you must either provide an array of 'groups' in your configuration or, more likely, provide " +
577 "an array of 'workspaceIds' in your configuration or set the ATOMIST_WORKSPACES environment variable " +
578 "to a comma-separated list of workspace IDs");
579 }
580 if (cfg.workspaceIds.length > 0 && cfg.groups.length > 0) {
581 errors.push("you cannot specify both 'workspaceIds' and 'groups' in your configuration, you must set one " +
582 "to an empty array");
583 }
584 if (errors.length > 0) {
585 const msg = `Configuration (${stringify(cfg, string_1.obfuscateJson)}) is not correct: ${errors.join("; ")}`;
586 throw new Error(msg);
587 }
588}
589exports.validateConfiguration = validateConfiguration;
590/**
591 * Load and populate the automation configuration. The configuration
592 * is loaded from several locations with the following precedence from
593 * highest to lowest.
594 *
595 * 1. `ATOMIST_` environment variables, see [[resolveEnvironmentVariables]]
596 * 2. Configuration returned from the post-processors.
597 * 3. Recognized environment variables, see [[resolveWorkspaceIds]],
598 * [[resolvePort]]
599 * 4. The value of the ATOMIST_CONFIG environment variable, parsed as
600 * JSON and cast to AutomationServerOptions
601 * 5. The contents of the ATOMIST_CONFIG_PATH file as AutomationServerOptions
602 * 6. The contents of the user's client.config.json as UserConfig
603 * resolving user and per-module configuration into Configuration
604 * 7. The automation atomist.config.js or _all_ index.js files'
605 * configurations exported as `configuration` from those files
606 * 8. ProductionDefaultConfiguration if ATOMIST_ENV or NODE_ENV is set
607 * to "production" or TestingDefaultConfiguration if ATOMIST_ENV or
608 * NODE_ENV is set to "staging" or "testing", with ATOMIST_ENV
609 * taking precedence over NODE_ENV.
610 * 9. LocalDefaultConfiguration
611 *
612 * If any of the sources are missing, they are ignored. Any truthy
613 * configuration values specified by sources of higher precedence
614 * cause any values provided by sources of lower precedence to be
615 * ignored. Arrays are replaced, not merged. Typically the only
616 * required values in the configuration for a successful registration
617 * are the apiKey and non-empty workspaceIds.
618 *
619 * Placeholder of the form `${ENV_VARIABLE}` in string configuration
620 * values will get resolved against the environment. The resolution
621 * happens after all of the above configuration sources have been
622 * merged.
623 *
624 * After all sources are merged and the resulting configuration
625 * processed for placeholders and environment variables, the
626 * configuration is validated using [[validateConfiguration]].
627 *
628 * The configuration exported from the index.js (or atomist.config.js)
629 * is modified to contain the final configuration values and returned
630 * from this function.
631 *
632 * @param cfgPath path to file exporting the configuration object, if
633 * not provided the package is searched for one
634 * @return merged configuration object
635 */
636function loadConfiguration(cfgPath) {
637 return __awaiter(this, void 0, void 0, function* () {
638 // Register the logger globally so that downstream modules can see it
639 global.__logger = logger_1.logger;
640 let cfg;
641 try {
642 const defCfg = defaultConfiguration();
643 const autoCfg = (yield loadAutomationConfig(cfgPath)) || (yield loadIndexConfig());
644 const userCfg = loadUserConfiguration(defCfg.name, defCfg.version);
645 const atmPathCfg = loadAtomistConfigPath();
646 const atmCfg = loadAtomistConfig();
647 cfg = mergeConfigs({}, defCfg, autoCfg, userCfg, atmPathCfg, atmCfg);
648 resolveWorkspaceIds(cfg);
649 resolvePort(cfg);
650 }
651 catch (e) {
652 e.message = `Failed to load configuration: ${e.message}`;
653 logger_1.logger.error(e.message);
654 if (e.stack) {
655 logger_1.logger.error(`Stack trace:\n${e.stack}`);
656 }
657 throw e;
658 }
659 const completeCfg = yield invokePostProcessors(cfg);
660 completeCfg.postProcessors = [];
661 resolveEnvironmentVariables(completeCfg);
662 yield resolvePlaceholders(completeCfg);
663 validateConfiguration(completeCfg);
664 return completeCfg;
665 });
666}
667exports.loadConfiguration = loadConfiguration;
668/**
669 * Default set of regular expressions used to remove sensitive
670 * information from messages and logs. The entries are applied in
671 * order, so more specific regular expressions should be placed
672 * earlier in the list to avoid a shorter replacement preventing a
673 * longer replacement from being applied.
674 */
675exports.DEFAULT_REDACTION_PATTERNS = [
676 {
677 regexp: /\b[A-F0-9]{64}\b/g,
678 replacement: "[ATOMIST_API_KEY]",
679 },
680 {
681 regexp: /[1-9][0-9]+-[0-9a-zA-Z]{40}/g,
682 replacement: "[TWITTER_ACCESS_TOKEN]",
683 },
684 {
685 regexp: /EAACEdEose0cBA[0-9A-Za-z]+/g,
686 replacement: "[FACEBOOK_ACCESS_TOKEN]",
687 },
688 {
689 regexp: /AIza[0-9A-Za-z\-_]{35}/g,
690 replacement: "[GOOGLE_API_KEY]",
691 },
692 {
693 regexp: /[0-9]+-[0-9A-Za-z_]{32}\.apps\.googleusercontent\.com/g,
694 replacement: "[GOOGLE_OAUTH_ID]",
695 },
696 {
697 regexp: /sk_live_[0-9a-z]{32}/g,
698 replacement: "[PICATIC_API_KEY|",
699 },
700 {
701 regexp: /sk_live_[0-9a-zA-Z]{24}/g,
702 replacement: "[STRIPE_REGULAR_API_KEY]",
703 },
704 {
705 regexp: /rk_live_[0-9a-zA-Z]{24}/g,
706 replacement: "[STRIPE_RESTRICTED_API_KEY]",
707 },
708 {
709 regexp: /sq0atp-[0-9A-Za-z\-_]{22}/g,
710 replacement: "[SQUARE_OAUTH_TOKEN]",
711 },
712 {
713 regexp: /sq0csp-[0-9A-Za-z\-_]{43}/g,
714 replacement: "[SQUARE_OAUTH_SECRET]",
715 },
716 {
717 regexp: /access_token\$production\$[0-9a-z]{16}\$[0-9a-f]{32}/g,
718 replacement: "[BRAINTREE_ACCESS_TOKEN]",
719 },
720 {
721 regexp: /amzn\.mws\.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/g,
722 replacement: "[AMAZON_AUTH_TOKEN]",
723 },
724 {
725 regexp: /SK[0-9a-fA-F]{32}/g,
726 replacement: "[TWILLIO_API_KEY]",
727 },
728 {
729 regexp: /key-[0-9a-zA-Z]{32}/g,
730 replacement: "[MAILGUN_KEY]",
731 },
732 {
733 regexp: /[0-9a-f]{32}-us[0-9]{1,2}/g,
734 replacement: "[MAILCHIMP_API_KEY]",
735 },
736 {
737 regexp: /\bAK[0-9A-Z]{18}\b/g,
738 replacement: "[AMAZON_ACCESS_KEY]",
739 },
740 {
741 regexp: /\b(https?:\/\/)(?:v1\.)?[a-f0-9]{40}((?::x-oauth-basic)?@)/g,
742 replacement: "$1[GITHUB_TOKEN]$2",
743 },
744 {
745 // https://perishablepress.com/stop-using-unsafe-characters-in-urls/
746 // https://www.ietf.org/rfc/rfc3986.txt
747 regexp: /\b((?:ht|f|sm)tps?:\/\/[^:/?#\[\]@""<>{}|\\^``\s]+:)[^:/?#\[\]@""<>{}|\\^``\s]+@/g,
748 replacement: "$1[URL_PASSWORD]@",
749 },
750];
751/**
752 * Default configuration when running in neither testing or
753 * production.
754 */
755exports.LocalDefaultConfiguration = {
756 workspaceIds: [],
757 groups: [],
758 environment: "local",
759 policy: "ephemeral",
760 endpoints: {
761 api: "https://automation.atomist.com/registration",
762 graphql: "https://automation.atomist.com/graphql/team",
763 auth: "https://api.atomist.com/v2/auth",
764 },
765 http: {
766 enabled: true,
767 host: "0.0.0.0",
768 auth: {
769 basic: {
770 enabled: false,
771 },
772 bearer: {
773 enabled: false,
774 },
775 },
776 customizers: [],
777 client: {
778 factory: httpClient_1.DefaultHttpClientFactory,
779 },
780 },
781 ws: {
782 enabled: true,
783 client: {
784 factory: wsClient_1.DefaultWebSocketFactory,
785 },
786 termination: {
787 graceful: false,
788 gracePeriod: shutdown_1.defaultGracePeriod,
789 },
790 compress: false,
791 timeout: 30000,
792 lifecycle: new WebSocketLifecycle_1.QueuingWebSocketLifecycle(),
793 },
794 graphql: {
795 client: {
796 factory: GraphClientFactory_1.DefaultGraphClientFactory,
797 },
798 },
799 applicationEvents: {
800 enabled: false,
801 },
802 cluster: {
803 enabled: false,
804 },
805 logging: {
806 level: "info",
807 file: {
808 enabled: true,
809 level: "debug",
810 },
811 banner: {
812 enabled: true,
813 contributors: [],
814 },
815 },
816 statsd: {
817 enabled: false,
818 client: {
819 factory: statsdClient_1.DefaultStatsDClientFactory,
820 },
821 },
822 redact: {
823 log: true,
824 messages: true,
825 patterns: exports.DEFAULT_REDACTION_PATTERNS,
826 },
827 commands: undefined,
828 events: undefined,
829 ingesters: [],
830 listeners: [],
831 postProcessors: [],
832};
833/**
834 * Configuration defaults for production environments.
835 */
836exports.ProductionDefaultConfiguration = {
837 environment: "production",
838 policy: "durable",
839 http: {
840 port: 2866,
841 auth: {
842 basic: {
843 enabled: true,
844 },
845 bearer: {
846 enabled: true,
847 },
848 },
849 },
850 ws: {
851 termination: {
852 graceful: true,
853 },
854 compress: true,
855 },
856 applicationEvents: {
857 enabled: true,
858 },
859 cluster: {
860 enabled: true,
861 },
862 logging: {
863 level: "info",
864 file: {
865 enabled: false,
866 },
867 },
868 statsd: {
869 enabled: true,
870 },
871};
872/**
873 * Configuration defaults for pre-production environments.
874 */
875exports.TestingDefaultConfiguration = {
876 environment: "testing",
877 policy: "durable",
878 http: {
879 auth: {
880 basic: {
881 enabled: true,
882 },
883 bearer: {
884 enabled: true,
885 },
886 },
887 },
888 ws: {
889 termination: {
890 graceful: true,
891 },
892 compress: true,
893 },
894 applicationEvents: {
895 enabled: true,
896 },
897 cluster: {
898 enabled: true,
899 },
900 logging: {
901 level: "info",
902 file: {
903 enabled: false,
904 },
905 },
906 statsd: {
907 enabled: true,
908 },
909};
910//# sourceMappingURL=configuration.js.map
\No newline at end of file