@codifycli/plugin-core 1.1.0-beta20 → 1.1.0-beta22

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.
@@ -3,6 +3,7 @@ export declare class ApplyValidationError extends Error {
3
3
  resourceType: string;
4
4
  resourceName?: string;
5
5
  plan: Plan<any>;
6
- constructor(plan: Plan<any>);
6
+ logs: string[];
7
+ constructor(plan: Plan<any>, logs?: string[]);
7
8
  private static prettyPrintPlan;
8
9
  }
@@ -2,11 +2,13 @@ export class ApplyValidationError extends Error {
2
2
  resourceType;
3
3
  resourceName;
4
4
  plan;
5
- constructor(plan) {
5
+ logs;
6
+ constructor(plan, logs = []) {
6
7
  super(`Failed to apply changes to resource: "${plan.resourceId}". Additional changes are needed to complete apply.\nChanges remaining:\n${ApplyValidationError.prettyPrintPlan(plan)}`);
7
8
  this.resourceType = plan.coreParameters.type;
8
9
  this.resourceName = plan.coreParameters.name;
9
10
  this.plan = plan;
11
+ this.logs = logs;
10
12
  }
11
13
  static prettyPrintPlan(plan) {
12
14
  const { operation, parameters } = plan.toResponse();
@@ -128,7 +128,7 @@ export class MessageHandler {
128
128
  errorPayload = {
129
129
  errorType: 'apply_validation',
130
130
  message: e.message,
131
- data: { plan: e.plan.toResponse() },
131
+ data: { plan: e.plan.toResponse(), logs: e.logs },
132
132
  };
133
133
  }
134
134
  else {
@@ -168,7 +168,11 @@ export class Plugin {
168
168
  if (!resource) {
169
169
  throw new Error('Malformed plan with resource that cannot be found');
170
170
  }
171
- await ptyLocalStorage.run(new SequentialPty(), async () => resource.apply(plan));
171
+ let applyLogs = [];
172
+ await ptyLocalStorage.run(new SequentialPty(), async () => {
173
+ await resource.apply(plan);
174
+ applyLogs = getPty().getLogs();
175
+ });
172
176
  // Validate using desired/desired. If the apply was successful, no changes should be reported back.
173
177
  // Default back desired back to current if it is not defined (for destroys only)
174
178
  const validationPlan = await ptyLocalStorage.run(new BackgroundPty(), async () => {
@@ -177,7 +181,7 @@ export class Plugin {
177
181
  return result;
178
182
  });
179
183
  if (validationPlan.requiresChanges()) {
180
- throw new ApplyValidationError(validationPlan);
184
+ throw new ApplyValidationError(validationPlan, applyLogs);
181
185
  }
182
186
  }
183
187
  async setVerbosityLevel(data) {
@@ -17,5 +17,6 @@ export declare class BackgroundPty implements IPty {
17
17
  signal?: number | undefined;
18
18
  }>;
19
19
  private initialize;
20
+ getLogs(): string[];
20
21
  private getDefaultShell;
21
22
  }
@@ -121,6 +121,9 @@ export class BackgroundPty {
121
121
  });
122
122
  });
123
123
  }
124
+ getLogs() {
125
+ return [];
126
+ }
124
127
  getDefaultShell() {
125
128
  return process.env.SHELL;
126
129
  }
@@ -47,5 +47,6 @@ export interface IPty {
47
47
  exitCode: number;
48
48
  signal?: number | undefined;
49
49
  }>;
50
+ getLogs(): string[];
50
51
  }
51
52
  export declare function getPty(): IPty;
@@ -6,6 +6,10 @@ import { IPty, SpawnOptions, SpawnResult } from './index.js';
6
6
  * without a tty (or even a stdin) attached so interactive commands will not work.
7
7
  */
8
8
  export declare class SequentialPty implements IPty {
9
+ private logBuffer;
10
+ private static readonly MAX_LOG_LINES;
11
+ getLogs(): string[];
12
+ private appendLog;
9
13
  spawn(cmd: string | string[], options?: SpawnOptions): Promise<SpawnResult>;
10
14
  spawnSafe(cmd: string | string[], options?: SpawnOptions): Promise<SpawnResult>;
11
15
  kill(): Promise<{
@@ -19,6 +19,18 @@ const validateSudoRequestResponse = ajv.compile(CommandRequestResponseDataSchema
19
19
  * without a tty (or even a stdin) attached so interactive commands will not work.
20
20
  */
21
21
  export class SequentialPty {
22
+ logBuffer = [];
23
+ static MAX_LOG_LINES = 30;
24
+ getLogs() {
25
+ return [...this.logBuffer];
26
+ }
27
+ appendLog(data) {
28
+ const lines = data.split('\n');
29
+ this.logBuffer.push(...lines);
30
+ if (this.logBuffer.length > SequentialPty.MAX_LOG_LINES) {
31
+ this.logBuffer = this.logBuffer.slice(-SequentialPty.MAX_LOG_LINES);
32
+ }
33
+ }
22
34
  async spawn(cmd, options) {
23
35
  const spawnResult = await this.spawnSafe(cmd, options);
24
36
  if (spawnResult.status !== 'success') {
@@ -35,7 +47,9 @@ export class SequentialPty {
35
47
  if (options?.stdin || options?.requiresRoot || options?.requiresSudoAskpass) {
36
48
  return this.externalSpawn(cmd, options);
37
49
  }
38
- console.log(`Running command: ${Array.isArray(cmd) ? cmd.join('\\\n') : cmd}` + (options?.cwd ? `(${options?.cwd})` : ''));
50
+ const cmdLine = `Running command: ${Array.isArray(cmd) ? cmd.join('\\\n') : cmd}` + (options?.cwd ? `(${options?.cwd})` : '');
51
+ console.log(cmdLine);
52
+ this.appendLog(cmdLine);
39
53
  return new Promise((resolve) => {
40
54
  const output = [];
41
55
  const historyIgnore = Utils.getShell() === Shell.ZSH ? { HISTORY_IGNORE: '*' } : { HISTIGNORE: '*' };
@@ -65,6 +79,7 @@ export class SequentialPty {
65
79
  process.stdout.write(data);
66
80
  }
67
81
  output.push(data.toString());
82
+ this.appendLog(data.toString());
68
83
  });
69
84
  const resizeListener = () => {
70
85
  const { columns, rows } = process.stdout;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codifycli/plugin-core",
3
- "version": "1.1.0-beta20",
3
+ "version": "1.1.0-beta22",
4
4
  "description": "TypeScript library for building Codify plugins to manage system resources (applications, CLI tools, settings) through infrastructure-as-code",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
@@ -4,13 +4,15 @@ export class ApplyValidationError extends Error {
4
4
  resourceType: string;
5
5
  resourceName?: string;
6
6
  plan: Plan<any>;
7
+ logs: string[];
7
8
 
8
- constructor(plan: Plan<any>) {
9
+ constructor(plan: Plan<any>, logs: string[] = []) {
9
10
  super(`Failed to apply changes to resource: "${plan.resourceId}". Additional changes are needed to complete apply.\nChanges remaining:\n${ApplyValidationError.prettyPrintPlan(plan)}`);
10
11
 
11
12
  this.resourceType = plan.coreParameters.type;
12
13
  this.resourceName = plan.coreParameters.name;
13
14
  this.plan = plan;
15
+ this.logs = logs;
14
16
  }
15
17
 
16
18
  private static prettyPrintPlan(plan: Plan<any>): string {
@@ -14,9 +14,9 @@ import {
14
14
  MatchRequestDataSchema,
15
15
  MatchResponseDataSchema,
16
16
  MessageStatus,
17
- PluginErrorData,
18
17
  PlanRequestDataSchema,
19
18
  PlanResponseDataSchema,
19
+ PluginErrorData,
20
20
  ResourceSchema,
21
21
  SetVerbosityRequestDataSchema,
22
22
  ValidateRequestDataSchema,
@@ -174,7 +174,7 @@ export class MessageHandler {
174
174
  errorPayload = {
175
175
  errorType: 'apply_validation',
176
176
  message: e.message,
177
- data: { plan: e.plan.toResponse() },
177
+ data: { plan: e.plan.toResponse(), logs: e.logs },
178
178
  };
179
179
  } else {
180
180
  const isDebug = process.env.DEBUG?.includes('*') ?? false;
@@ -250,7 +250,11 @@ export class Plugin {
250
250
  throw new Error('Malformed plan with resource that cannot be found');
251
251
  }
252
252
 
253
- await ptyLocalStorage.run(new SequentialPty(), async () => resource.apply(plan))
253
+ let applyLogs: string[] = [];
254
+ await ptyLocalStorage.run(new SequentialPty(), async () => {
255
+ await resource.apply(plan);
256
+ applyLogs = getPty().getLogs();
257
+ });
254
258
 
255
259
  // Validate using desired/desired. If the apply was successful, no changes should be reported back.
256
260
  // Default back desired back to current if it is not defined (for destroys only)
@@ -268,7 +272,7 @@ export class Plugin {
268
272
  })
269
273
 
270
274
  if (validationPlan.requiresChanges()) {
271
- throw new ApplyValidationError(validationPlan);
275
+ throw new ApplyValidationError(validationPlan, applyLogs);
272
276
  }
273
277
  }
274
278
 
@@ -148,6 +148,10 @@ export class BackgroundPty implements IPty {
148
148
  })
149
149
  }
150
150
 
151
+ getLogs(): string[] {
152
+ return [];
153
+ }
154
+
151
155
  private getDefaultShell(): string {
152
156
  return process.env.SHELL!;
153
157
  }
package/src/pty/index.ts CHANGED
@@ -60,6 +60,8 @@ export interface IPty {
60
60
  spawnSafe(cmd: string | string[], options?: SpawnOptions): Promise<SpawnResult>
61
61
 
62
62
  kill(): Promise<{ exitCode: number, signal?: number | undefined }>
63
+
64
+ getLogs(): string[]
63
65
  }
64
66
 
65
67
  export function getPty(): IPty {
@@ -28,6 +28,21 @@ const validateSudoRequestResponse = ajv.compile(CommandRequestResponseDataSchema
28
28
  * without a tty (or even a stdin) attached so interactive commands will not work.
29
29
  */
30
30
  export class SequentialPty implements IPty {
31
+ private logBuffer: string[] = [];
32
+ private static readonly MAX_LOG_LINES = 30;
33
+
34
+ getLogs(): string[] {
35
+ return [...this.logBuffer];
36
+ }
37
+
38
+ private appendLog(data: string): void {
39
+ const lines = data.split('\n');
40
+ this.logBuffer.push(...lines);
41
+ if (this.logBuffer.length > SequentialPty.MAX_LOG_LINES) {
42
+ this.logBuffer = this.logBuffer.slice(-SequentialPty.MAX_LOG_LINES);
43
+ }
44
+ }
45
+
31
46
  async spawn(cmd: string | string[], options?: SpawnOptions): Promise<SpawnResult> {
32
47
  const spawnResult = await this.spawnSafe(cmd, options);
33
48
 
@@ -50,7 +65,9 @@ export class SequentialPty implements IPty {
50
65
  return this.externalSpawn(cmd, options);
51
66
  }
52
67
 
53
- console.log(`Running command: ${Array.isArray(cmd) ? cmd.join('\\\n') : cmd}` + (options?.cwd ? `(${options?.cwd})` : ''))
68
+ const cmdLine = `Running command: ${Array.isArray(cmd) ? cmd.join('\\\n') : cmd}` + (options?.cwd ? `(${options?.cwd})` : '');
69
+ console.log(cmdLine);
70
+ this.appendLog(cmdLine);
54
71
 
55
72
  return new Promise((resolve) => {
56
73
  const output: string[] = [];
@@ -87,6 +104,7 @@ export class SequentialPty implements IPty {
87
104
  }
88
105
 
89
106
  output.push(data.toString());
107
+ this.appendLog(data.toString());
90
108
  })
91
109
 
92
110
  const resizeListener = () => {