codify-plugin-lib 1.0.77 → 1.0.78

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -31,6 +31,16 @@ export declare class ChangeSet<T extends StringIndexedObject> {
31
31
  static create<T extends StringIndexedObject>(desired: Partial<T>): ChangeSet<T>;
32
32
  static destroy<T extends StringIndexedObject>(current: Partial<T>): ChangeSet<T>;
33
33
  static calculateModification<T extends StringIndexedObject>(desired: Partial<T>, current: Partial<T>, parameterSettings?: Partial<Record<keyof T, ParameterSetting>>): ChangeSet<T>;
34
+ /**
35
+ * Calculates the differences between the desired and current parameters,
36
+ * and returns a list of parameter changes that describe what needs to be added,
37
+ * removed, or modified to match the desired state.
38
+ *
39
+ * @param {Partial<T>} desiredParameters - The desired target state of the parameters.
40
+ * @param {Partial<T>} currentParameters - The current state of the parameters.
41
+ * @param {Partial<Record<keyof T, ParameterSetting>>} [parameterOptions] - Optional settings used when comparing parameters.
42
+ * @return {ParameterChange<T>[]} A list of changes required to transition from the current state to the desired state.
43
+ */
34
44
  private static calculateParameterChanges;
35
45
  private static combineResourceOperations;
36
46
  private static isSame;
@@ -67,6 +67,16 @@ export class ChangeSet {
67
67
  }, ResourceOperation.NOOP);
68
68
  return new ChangeSet(resourceOperation, pc);
69
69
  }
70
+ /**
71
+ * Calculates the differences between the desired and current parameters,
72
+ * and returns a list of parameter changes that describe what needs to be added,
73
+ * removed, or modified to match the desired state.
74
+ *
75
+ * @param {Partial<T>} desiredParameters - The desired target state of the parameters.
76
+ * @param {Partial<T>} currentParameters - The current state of the parameters.
77
+ * @param {Partial<Record<keyof T, ParameterSetting>>} [parameterOptions] - Optional settings used when comparing parameters.
78
+ * @return {ParameterChange<T>[]} A list of changes required to transition from the current state to the desired state.
79
+ */
70
80
  static calculateParameterChanges(desiredParameters, currentParameters, parameterOptions) {
71
81
  const parameterChangeSet = new Array();
72
82
  // Filter out null and undefined values or else the diff below will not work
@@ -1,6 +1,7 @@
1
1
  import { Ajv } from 'ajv';
2
2
  import { ParameterOperation, ResourceOperation } from 'codify-schemas';
3
3
  import { Plan } from '../plan/plan.js';
4
+ import { splitUserConfig } from '../utils/utils.js';
4
5
  import { ConfigParser } from './config-parser.js';
5
6
  import { ParsedResourceSettings } from './parsed-resource-settings.js';
6
7
  export class ResourceController {
@@ -193,9 +194,10 @@ ${JSON.stringify(refresh, null, 2)}
193
194
  desired[key] = await inputTransformation(desired[key]);
194
195
  }
195
196
  if (this.settings.inputTransformation) {
196
- const transformed = await this.settings.inputTransformation(desired);
197
+ const { parameters, coreParameters } = splitUserConfig(desired);
198
+ const transformed = await this.settings.inputTransformation(parameters);
197
199
  Object.keys(desired).forEach((k) => delete desired[k]);
198
- Object.assign(desired, transformed);
200
+ Object.assign(desired, transformed, coreParameters);
199
201
  }
200
202
  }
201
203
  addDefaultValues(desired) {
@@ -86,7 +86,7 @@ export interface DefaultParameterSetting {
86
86
  *
87
87
  * @param input The original parameter value from the desired config.
88
88
  */
89
- inputTransformation?: (input: any) => Promise<any> | unknown;
89
+ inputTransformation?: (input: any) => Promise<any> | any;
90
90
  /**
91
91
  * Customize the equality comparison for a parameter. This is used in the diffing algorithm for generating the plan.
92
92
  * This value will override the pre-set equality function from the type. Return true if the desired value is
@@ -0,0 +1,5 @@
1
+ import { Shell } from 'zx';
2
+ export declare class ShellContext implements Shell {
3
+ zx: Shell;
4
+ static create(): ShellContext;
5
+ }
@@ -0,0 +1,7 @@
1
+ import { $ } from 'zx';
2
+ export class ShellContext {
3
+ zx = $({ shell: true });
4
+ static create() {
5
+ return new ShellContext();
6
+ }
7
+ }
@@ -0,0 +1,29 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ import { SpawnOptions } from 'node:child_process';
3
+ export declare enum SpawnStatus {
4
+ SUCCESS = "success",
5
+ ERROR = "error"
6
+ }
7
+ export interface SpawnResult {
8
+ status: SpawnStatus;
9
+ data: string;
10
+ }
11
+ type CodifySpawnOptions = {
12
+ cwd?: string;
13
+ throws?: boolean;
14
+ requiresRoot?: boolean;
15
+ } & Omit<SpawnOptions, 'detached' | 'shell' | 'stdio'>;
16
+ /**
17
+ *
18
+ * @param cmd Command to run. Ex: `rm -rf`
19
+ * @param opts Standard options for node spawn. Additional argument:
20
+ * throws determines if a shell will throw a JS error. Defaults to true
21
+ *
22
+ * @see promiseSpawn
23
+ * @see spawn
24
+ *
25
+ * @returns SpawnResult { status: SUCCESS | ERROR; data: string }
26
+ */
27
+ export declare function $(cmd: string, opts?: CodifySpawnOptions): Promise<SpawnResult>;
28
+ export declare function isDebug(): boolean;
29
+ export {};
@@ -0,0 +1,124 @@
1
+ import { Ajv } from 'ajv';
2
+ import { MessageCmd, SudoRequestResponseDataSchema } from 'codify-schemas';
3
+ import { spawn } from 'node:child_process';
4
+ import { SudoError } from '../errors.js';
5
+ const ajv = new Ajv({
6
+ strict: true,
7
+ });
8
+ const validateSudoRequestResponse = ajv.compile(SudoRequestResponseDataSchema);
9
+ export var SpawnStatus;
10
+ (function (SpawnStatus) {
11
+ SpawnStatus["SUCCESS"] = "success";
12
+ SpawnStatus["ERROR"] = "error";
13
+ })(SpawnStatus || (SpawnStatus = {}));
14
+ /**
15
+ *
16
+ * @param cmd Command to run. Ex: `rm -rf`
17
+ * @param opts Standard options for node spawn. Additional argument:
18
+ * throws determines if a shell will throw a JS error. Defaults to true
19
+ *
20
+ * @see promiseSpawn
21
+ * @see spawn
22
+ *
23
+ * @returns SpawnResult { status: SUCCESS | ERROR; data: string }
24
+ */
25
+ export async function $(cmd, opts) {
26
+ const throws = opts?.throws ?? true;
27
+ console.log(`Running command: ${cmd}`);
28
+ try {
29
+ // TODO: Need to benchmark the effects of using sh vs zsh for shell.
30
+ // Seems like zsh shells run slower
31
+ let result;
32
+ if (!opts?.requiresRoot) {
33
+ result = await internalSpawn(cmd, opts ?? {});
34
+ }
35
+ else {
36
+ result = await externalSpawnWithSudo(cmd, opts);
37
+ }
38
+ if (result.status !== SpawnStatus.SUCCESS) {
39
+ throw new Error(result.data);
40
+ }
41
+ return result;
42
+ }
43
+ catch (error) {
44
+ if (isDebug()) {
45
+ console.error(`CodifySpawn error for command ${cmd}`, error);
46
+ }
47
+ if (error.message?.startsWith('sudo:')) {
48
+ throw new SudoError(cmd);
49
+ }
50
+ if (throws) {
51
+ throw error;
52
+ }
53
+ if (error instanceof Error) {
54
+ return {
55
+ status: SpawnStatus.ERROR,
56
+ data: error.message,
57
+ };
58
+ }
59
+ return {
60
+ status: SpawnStatus.ERROR,
61
+ data: error + '',
62
+ };
63
+ }
64
+ }
65
+ async function internalSpawn(cmd, opts) {
66
+ return new Promise((resolve, reject) => {
67
+ const output = [];
68
+ // Source start up shells to emulate a users environment vs. a non-interactive non-login shell script
69
+ // Ignore all stdin
70
+ const _process = spawn(`source ~/.zshrc; ${cmd}`, [], {
71
+ ...opts,
72
+ stdio: ['ignore', 'pipe', 'pipe'],
73
+ shell: 'zsh',
74
+ });
75
+ const { stdout, stderr, stdin } = _process;
76
+ stdout.setEncoding('utf8');
77
+ stderr.setEncoding('utf8');
78
+ stdout.on('data', (data) => {
79
+ output.push(data.toString());
80
+ });
81
+ stderr.on('data', (data) => {
82
+ output.push(data.toString());
83
+ });
84
+ _process.on('error', (data) => {
85
+ });
86
+ // please node that this is not a full replacement for 'inherit'
87
+ // the child process can and will detect if stdout is a pty and change output based on it
88
+ // the terminal context is lost & ansi information (coloring) etc will be lost
89
+ if (stdout && stderr) {
90
+ stdout.pipe(process.stdout);
91
+ stderr.pipe(process.stderr);
92
+ }
93
+ _process.on('close', (code) => {
94
+ resolve({
95
+ status: code === 0 ? SpawnStatus.SUCCESS : SpawnStatus.ERROR,
96
+ data: output.join('\n'),
97
+ });
98
+ });
99
+ });
100
+ }
101
+ async function externalSpawnWithSudo(cmd, opts) {
102
+ return await new Promise((resolve) => {
103
+ const listener = (data) => {
104
+ if (data.cmd === MessageCmd.SUDO_REQUEST + '_Response') {
105
+ process.removeListener('message', listener);
106
+ if (!validateSudoRequestResponse(data.data)) {
107
+ throw new Error(`Invalid response for sudo request: ${JSON.stringify(validateSudoRequestResponse.errors, null, 2)}`);
108
+ }
109
+ resolve(data.data);
110
+ }
111
+ };
112
+ process.on('message', listener);
113
+ process.send({
114
+ cmd: MessageCmd.SUDO_REQUEST,
115
+ data: {
116
+ command: cmd,
117
+ options: opts ?? {},
118
+ }
119
+ });
120
+ });
121
+ }
122
+ export function isDebug() {
123
+ return process.env.DEBUG != null && process.env.DEBUG.includes('codify'); // TODO: replace with debug library
124
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codify-plugin-lib",
3
- "version": "1.0.77",
3
+ "version": "1.0.78",
4
4
  "description": "Library plugin library",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
@@ -116,6 +116,16 @@ export class ChangeSet<T extends StringIndexedObject> {
116
116
  return new ChangeSet<T>(resourceOperation, pc);
117
117
  }
118
118
 
119
+ /**
120
+ * Calculates the differences between the desired and current parameters,
121
+ * and returns a list of parameter changes that describe what needs to be added,
122
+ * removed, or modified to match the desired state.
123
+ *
124
+ * @param {Partial<T>} desiredParameters - The desired target state of the parameters.
125
+ * @param {Partial<T>} currentParameters - The current state of the parameters.
126
+ * @param {Partial<Record<keyof T, ParameterSetting>>} [parameterOptions] - Optional settings used when comparing parameters.
127
+ * @return {ParameterChange<T>[]} A list of changes required to transition from the current state to the desired state.
128
+ */
119
129
  private static calculateParameterChanges<T extends StringIndexedObject>(
120
130
  desiredParameters: Partial<T>,
121
131
  currentParameters: Partial<T>,
@@ -10,6 +10,7 @@ import {
10
10
  import { ParameterChange } from '../plan/change-set.js';
11
11
  import { Plan } from '../plan/plan.js';
12
12
  import { CreatePlan, DestroyPlan, ModifyPlan } from '../plan/plan-types.js';
13
+ import { splitUserConfig } from '../utils/utils.js';
13
14
  import { ConfigParser } from './config-parser.js';
14
15
  import { ParsedResourceSettings } from './parsed-resource-settings.js';
15
16
  import { Resource } from './resource.js';
@@ -250,7 +251,7 @@ ${JSON.stringify(refresh, null, 2)}
250
251
  }
251
252
  }
252
253
 
253
- private async applyTransformParameters(desired: Partial<T> | null): Promise<void> {
254
+ private async applyTransformParameters(desired: Partial<T> & ResourceConfig | null): Promise<void> {
254
255
  if (!desired) {
255
256
  return;
256
257
  }
@@ -264,9 +265,11 @@ ${JSON.stringify(refresh, null, 2)}
264
265
  }
265
266
 
266
267
  if (this.settings.inputTransformation) {
267
- const transformed = await this.settings.inputTransformation(desired)
268
+ const { parameters, coreParameters } = splitUserConfig(desired);
269
+
270
+ const transformed = await this.settings.inputTransformation(parameters)
268
271
  Object.keys(desired).forEach((k) => delete desired[k])
269
- Object.assign(desired, transformed);
272
+ Object.assign(desired, transformed, coreParameters);
270
273
  }
271
274
  }
272
275
 
@@ -115,7 +115,7 @@ export interface DefaultParameterSetting {
115
115
  *
116
116
  * @param input The original parameter value from the desired config.
117
117
  */
118
- inputTransformation?: (input: any) => Promise<any> | unknown;
118
+ inputTransformation?: (input: any) => Promise<any> | any;
119
119
 
120
120
  /**
121
121
  * Customize the equality comparison for a parameter. This is used in the diffing algorithm for generating the plan.