UNPKG

19.3 kBTypeScriptView Raw
1import * as exp from "express";
2import { HandleCommand } from "./HandleCommand";
3import { HandleEvent } from "./HandleEvent";
4import { ExpressServerOptions } from "./internal/transport/express/ExpressServer";
5import { AutomationEventListener } from "./server/AutomationEventListener";
6import { AutomationMetadataProcessor } from "./spi/env/MetadataProcessor";
7import { SecretResolver } from "./spi/env/SecretResolver";
8import { HttpClientFactory } from "./spi/http/httpClient";
9import { Maker } from "./util/constructionUtils";
10/**
11 * Customize the express server configuration: For example to add custom routes
12 *
13 * Example:
14 *
15 * const newRouteCustomizer = (express: exp.Express, ...handlers: exp.RequestHandler[]) => {
16 * express.get("/new-route", ...handlers, (req, res) => {
17 * res.json({ key: "value" });
18 * });
19 * }
20 */
21export declare type ExpressCustomizer = (express: exp.Express, ...handlers: exp.RequestHandler[]) => void;
22/**
23 * Post process the configuration after is has been merged from the various locations, but
24 * before starting the automation client.
25 */
26export declare type ConfigurationPostProcessor<T = AnyOptions> = (configuration: Configuration) => Promise<Configuration & T>;
27/**
28 * A computed banner
29 */
30export interface Banner {
31 /**
32 * Banner content
33 */
34 banner: string;
35 /**
36 * Whether or not the banner content should be asciified
37 */
38 asciify: boolean;
39 color: "black" | "red" | "green" | "yellow" | "blue" | "magenta" | "cyan" | "white" | "gray";
40}
41/**
42 * A section that should be displayed in the banner.
43 */
44export interface BannerSection {
45 title: string;
46 body: string;
47}
48/**
49 * Custom configuration you can abuse to your benefit
50 */
51export interface AnyOptions {
52 /** Abuse goes here */
53 [key: string]: any;
54}
55/**
56 * Options for an automation node.
57 */
58export interface AutomationOptions extends AnyOptions {
59 /**
60 * Automation name. If not given, the name is extracted from
61 * the package.json.
62 */
63 name?: string;
64 /**
65 * Automation version. Must be a valid semantic version,
66 * https://semver.org/. If not given, the version is extracted
67 * from the package.json.
68 */
69 version?: string;
70 /**
71 * Atomist workspaces this automation will be registered with. Must be
72 * specified if groups is not specified. Cannot be specified if
73 * groups is specified.
74 */
75 workspaceIds?: string[];
76 /**
77 * DO NOT USE. Groups this automation will be registered with.
78 * Must be specified if teams is not specified. Cannot be
79 * specified if teams is specified. Providing groups indicates
80 * this is a global automation, which can only successfully be
81 * registered by Atomist.
82 */
83 groups?: string[];
84 /**
85 * If events should be queued when the registration is not
86 * connected to the websocket, specificy "durable". "ephemeral"
87 * is suited for testing and running locally and is the default.
88 */
89 policy?: "ephemeral" | "durable";
90 /**
91 * Atomist API Key used to authenticate the user starting the client.
92 */
93 apiKey?: string;
94 /** HTTP configuration, useful for health checks */
95 http?: {
96 enabled?: boolean;
97 client?: {
98 factory?: HttpClientFactory;
99 };
100 } & Partial<ExpressServerOptions>;
101 /** websocket configuration */
102 ws?: {
103 enabled?: boolean;
104 termination?: {
105 /**
106 * if true, give in-flight transactions `gracePeriod`
107 * milliseconds to complete when shutting down
108 */
109 graceful?: boolean;
110 /** grace period in millisends */
111 gracePeriod?: number;
112 };
113 /** compress messages over websocket */
114 compress?: boolean;
115 /** timeout in milliseconds */
116 timeout?: number;
117 };
118 /** Atomist API endpoints */
119 endpoints?: {
120 graphql?: string;
121 api?: string;
122 };
123 /**
124 * Post-processors can be used to modify the configuration after
125 * all standard configuration loading has been done and before the
126 * client is started. Post-processors return a configuration
127 * promise so they can be asynchronous.
128 */
129 postProcessors?: ConfigurationPostProcessor[];
130}
131/**
132 * Options useful when running an automation client in server mode.
133 */
134export interface AutomationServerOptions extends AutomationOptions {
135 /** environment automation is running in, e.g., "production" or "testing" */
136 environment?: string;
137 /**
138 * Application identifier used for metrics send to statsd. If not
139 * set, the automation client package name with any namespace
140 * prefix removed is used.
141 */
142 application?: string;
143 /** keywords useful for discovery */
144 keywords?: string[];
145 /** Whether and where to send application start and stop events to Atomist. */
146 applicationEvents?: {
147 enabled?: boolean;
148 workspaceId?: string;
149 };
150 /**
151 * Whether and how many workers to start up. If enabled is true
152 * and workers is false, a number of workers equal to the number
153 * of available CPUs will be started.
154 */
155 cluster?: {
156 enabled?: boolean;
157 workers?: number;
158 };
159 /** Logging configuration */
160 logging?: {
161 /** Log level, default is "info" */
162 level?: "silly" | "debug" | "verbose" | "info" | "warn" | "error";
163 /**
164 * Custom log configuration, useful if your logging solution
165 * requires host, port, token, etc. configuration.
166 */
167 custom?: any;
168 /**
169 * Print welcome banner; set to an arbitrary string to display,
170 * default is name of automation-client
171 */
172 banner?: {
173 enabled?: boolean;
174 /** Message or Banner to be printed at the top of the banner */
175 message?: string | ((configuration: Configuration) => Banner);
176 /**
177 * Add content to the banner which shows up between handlers and
178 * footer
179 */
180 contributors?: Array<(configuration: Configuration) => string | BannerSection>;
181 };
182 /**
183 * Log to file; set to file path to overwrite location and name of logfile,
184 * defaults to ./log/automation-client.log in current working directory
185 */
186 file?: {
187 enabled?: boolean;
188 name?: string;
189 level?: string;
190 };
191 };
192 /** statsd config */
193 statsd?: {
194 /** Whether to send metrics statsd, default is false */
195 enabled?: boolean;
196 /**
197 * statsd host. If not set, use the host-shots default,
198 * "localhost" at the time of this writing.
199 */
200 host?: string;
201 /**
202 * statsd port. If not set, use the hot-shots default, 8125
203 * at the time of this writing.
204 */
205 port?: number;
206 };
207 /** Register a custom secret resolver */
208 secretResolver?: SecretResolver;
209 /** Register a custom AutomationMetadataProcessor */
210 metadataProcessor?: AutomationMetadataProcessor;
211}
212/**
213 * Atomist automation configuration.
214 */
215export interface Configuration extends AutomationServerOptions {
216 /**
217 * Automation commands this package provides. If empty or null,
218 * the package will be scanned for commands, which must be under a
219 * directory named "commands".
220 */
221 commands?: Array<Maker<HandleCommand>>;
222 /**
223 * Automation event handlers this package provides. If empty or
224 * null, the package will be scanned for event handlers, which
225 * must be under a directory named "events".
226 */
227 events?: Array<Maker<HandleEvent>>;
228 /** Custom event ingester */
229 ingesters?: string[];
230 /** Log and metric sinks */
231 listeners?: AutomationEventListener[];
232}
233/**
234 * User per-automation configuration
235 */
236export interface ModuleOptions extends AutomationServerOptions {
237 /** Automation name this configuration applies to. */
238 name: string;
239 /**
240 * A valid version or version range, as defined by
241 * https://www.npmjs.com/package/semver, this configuration
242 * applies to. If not provided, it applies to all versions of the
243 * named automation.
244 */
245 version?: string;
246}
247/**
248 * User-wide configuration and user per-automation configuration
249 */
250export interface UserConfig extends AutomationServerOptions {
251 modules?: ModuleOptions[];
252}
253/**
254 * Generate defaults for various configuration option values. These
255 * will only be used if values are not provided by any source. Values
256 * not provided here will be `undefined`.
257 *
258 * @return default configuration
259 */
260export declare function defaultConfiguration(): Configuration;
261/**
262 * Exposes the configuration for lookup of configuration values.
263 * This is useful for components to obtain values eg. from configuration.custom
264 * like user provided secrets etc.
265 * @param {string} path the property path evaluated against the configuration instance
266 * @returns {T}
267 */
268export declare function configurationValue<T>(path: string, defaultValue?: T): T;
269/**
270 * Return user automation client configuration path.
271 */
272export declare function userConfigPath(): string;
273/**
274 * Write user config securely, creating directories as necessary.
275 */
276export declare function writeUserConfig(cfg: UserConfig): Promise<void>;
277/**
278 * Read and return user config from UserConfigFile.
279 */
280export declare function getUserConfig(): UserConfig;
281/**
282 * Overwrite values in the former configuration with values in the
283 * latter. The start object is modified.
284 *
285 * @param obj starting configuration
286 * @param override configuration values to add/override those in start
287 * @return resulting merged configuration
288 */
289export declare function mergeConfigs(obj: Configuration, ...sources: Configuration[]): Configuration;
290/**
291 * Overwrite values in the former configuration with values in the
292 * latter. The start object is modified. Arrays are concatenated.
293 *
294 * @param obj starting configuration
295 * @param override configuration values to add/override those in start
296 * @return resulting merged configuration
297 */
298export declare function deepMergeConfigs(obj: Configuration, ...sources: Configuration[]): Configuration;
299/**
300 * Merge a user's global and proper per-module configuration, if it
301 * exists. Values from the per-module configuration take precedence
302 * over the user-wide values. Per-module configuration is gotten from
303 * the first per-module configuration that matches name and,
304 * optionally, the version is within the per-module configuration's
305 * version range. A module configuration without a version range
306 * matches the named module with any version. If no version is
307 * provided, any version range is satisfied, meaning the first
308 * per-module configuration with a matching name is used. If no name
309 * is provide, only the user configuration is loaded. The first
310 * per-module match is used. This means if you have multiple
311 * configurations for the same named module and you want to include a
312 * default configuration for that module, put a configuration without
313 * a version range _after_ all the configurations with version ranges.
314 * Note that only values from the first per-module match are used.
315 *
316 * @param userConfig the user's configuration, which may include per-module configuration
317 * @param name automation client package name to load as module config if it exists
318 * @param version automation client package version to load as module config if
319 * version satifies module config version range
320 * @return the merged module and user configuration
321 */
322export declare function resolveModuleConfig(userConfig: UserConfig, name?: string, version?: string): AutomationServerOptions;
323/**
324 * Try to read user config, overriding its values with a per-module
325 * configuration that matches this automation.
326 *
327 * @param name automation client package name to load as module config if it exists
328 * @param version automation client package version to load as module config if
329 * version satifies module config version range
330 * @return module-specific config with user config supplying defaults
331 */
332export declare function loadUserConfiguration(name?: string, version?: string): AutomationServerOptions;
333/**
334 * Load the automation configuration from the configuration object
335 * exported from cfgPath and return it. If no configuration path is
336 * provided, the package will be searched for a file named
337 * atomist.config.js. If no atomist.config.js is found, an empty
338 * object is returned. If more than one is found, an exception is
339 * thrown.
340 *
341 * @param cfgPath location of automation configuration
342 * @return automation configuration or undefined
343 */
344export declare function loadAutomationConfig(cfgPath?: string): Configuration | undefined;
345/**
346 * Load the automation configuration from the configuration objects
347 * exported and merged by all index.js files in the automation client.
348 *
349 * @return automation configuration
350 */
351export declare function loadIndexConfig(): Configuration;
352/**
353 * Load configuration from the file defined by the ATOMIST_CONFIG_PATH
354 * environment variable, if it the variable is defined and the file
355 * exists, and return it. The contents of the ATOMIST_CONFIG_PATH
356 * file should be serialized JSON of AutomationServerOptions. If the
357 * environment variable is not defined or the file path specified by
358 * its value cannot be read as JSON, an empty object is returned.
359 *
360 * @return automation server options
361 */
362export declare function loadAtomistConfigPath(): AutomationServerOptions;
363/**
364 * Load configuration from the ATOMIST_CONFIG environment variable, if
365 * it the variable is defined, and merge it into the passed in
366 * configuration. The value of the ATOMIST_CONFIG environment
367 * variable should be serialized JSON of AutomationServerOptions. The
368 * values from the environment variable will override values in the
369 * passed in configuration. If the environment variable is not
370 * defined, the passed in configuration is returned unchanged.
371 *
372 * @return automation server options
373 */
374export declare function loadAtomistConfig(): AutomationServerOptions;
375/**
376 * Examine environment, config, and cfg for Atomist workspace IDs.
377 * The ATOMIST_WORKSPACES environment variable takes precedence over
378 * the configuration "workspaceIds", which takes precedence over
379 * cfg.workspaceId, which may be undefined, null, or an empty array.
380 * If the ATOMIST_WORKSPACES environment variable is not set,
381 * workspaceIds is not set in config, and workspaceIds is falsey in
382 * cfg and teamIds is resolvable from the configuration, workspaceIds
383 * is set to teamIds.
384 *
385 * @param cfg current configuration, whose workspaceIds and teamIds
386 * properties may be modified by this function
387 * @return the resolved workspace IDs
388 */
389export declare function resolveWorkspaceIds(cfg: Configuration): string[];
390/**
391 * Resolve a value from a environment variables or configuration keys.
392 * The environment variables are checked in order and take precedence
393 * over the configuration key, which are also checked in order. If
394 * no truthy values are found, undefined is returned.
395 *
396 * @param environmentVariables environment variables to check
397 * @param configKeyPaths configuration keys, as JSON paths, to check
398 * @param defaultValue value to use if no environment variables or config keys have values
399 * @return first truthy value found, or defaultValue
400 */
401export declare function resolveConfigurationValue(environmentVariables: string[], configKeyPaths: string[], defaultValue?: string): string;
402/**
403 * Resolve the HTTP port from the environment and configuration. The
404 * PORT environment variable takes precedence over the config value.
405 */
406export declare function resolvePort(cfg: Configuration): number;
407/**
408 * Resolve ATOMIST_ environment variables and add them to config.
409 * Variables of like ATOMIST_custom_foo_bar will be converted to
410 * a json path of custom.foo.bar.
411 * @param {Configuration} cfg
412 */
413export declare function resolveEnvironmentVariables(cfg: Configuration): void;
414/**
415 * Resolve placeholders against the process.env.
416 * Placeholders should be of form ${ENV_VAR}. Placeholders support default values
417 * in case they aren't defined: ${ENV_VAR:default value}
418 * @param {Configuration} config
419 */
420export declare function resolvePlaceholders(cfg: Configuration): void;
421/**
422 * Invoke postProcessors on the provided configuration.
423 */
424export declare function invokePostProcessors(cfg: Configuration): Promise<Configuration>;
425/**
426 * Make sure final configuration has the minimum configuration it
427 * needs. It will throw an error if required properties are missing.
428 *
429 * @param cfg final configuration
430 */
431export declare function validateConfiguration(cfg: Configuration): void;
432/**
433 * Load and populate the automation configuration. The configuration
434 * is loaded from several locations with the following precedence from
435 * highest to lowest.
436 *
437 * 0. Recognized environment variables (see below)
438 * 1. The value of the ATOMIST_CONFIG environment variable, parsed as
439 * JSON and cast to AutomationServerOptions
440 * 2. The contents of the ATOMIST_CONFIG_PATH file as AutomationServerOptions
441 * 3. The contents of the user's client.config.json as UserConfig
442 * resolving user and per-module configuration into Configuration
443 * 4. The automation's index.js (or atomist.config.js) exported configuration as
444 * Configuration
445 * 5. ProductionDefaultConfiguration if ATOMIST_ENV or NODE_ENV is set
446 * to "production" or TestingDefaultConfiguration if ATOMIST_ENV or
447 * NODE_ENV is set to "staging" or "testing", with ATOMIST_ENV
448 * taking precedence over NODE_ENV.
449 * 6. LocalDefaultConfiguration
450 *
451 * If any of the sources are missing, they are ignored. Any truthy
452 * configuration values specified by sources of higher precedence
453 * cause any values provided by sources of lower precedence to be
454 * ignored. Arrays are replaced, not merged. Typically the only
455 * required values in the configuration for a successful registration
456 * are the apiKey and non-empty workspaceIds.
457 *
458 * Placeholder of the form `${ENV_VARIABLE}` in string configuration
459 * values will get resolved against the environment. The resolution
460 * happens at the very end when all configs have been merged.
461 *
462 * The configuration exported from the index.js (or atomist.config.js) is modified
463 * to contain the final configuration values and returned from this
464 * function.
465 *
466 * @param cfgPath path to file exporting the configuration object, if
467 * not provided the package is searched for one
468 * @return merged configuration object
469 */
470export declare function loadConfiguration(cfgPath?: string): Promise<Configuration>;
471/**
472 * Default configuration when running in neither testing or
473 * production.
474 */
475export declare const LocalDefaultConfiguration: Configuration;
476/**
477 * Configuration defaults for production environments.
478 */
479export declare const ProductionDefaultConfiguration: Partial<Configuration>;
480/**
481 * Configuration defaults for pre-production environments.
482 */
483export declare const TestingDefaultConfiguration: Partial<Configuration>;