Skip to content

Commit 2a75b08

Browse files
authored
adds info command (#952)
* adds info command
1 parent 8c371b1 commit 2a75b08

17 files changed

+514
-0
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@aws-amplify/backend-cli': minor
3+
---
4+
5+
adds info commands that provides troubleshooting info

.eslint_dictionary.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
"ecma",
3333
"enum",
3434
"enums",
35+
"envinfo",
3536
"esbuild",
3637
"esnext",
3738
"execa",

package-lock.json

Lines changed: 19 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/cli-core/API.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ export enum COLOR {
2727
RED = "31m"
2828
}
2929

30+
// @public
31+
export const format: {
32+
list: (lines: string[]) => string;
33+
indent: (message: string) => string;
34+
};
35+
3036
// @public (undocumented)
3137
export enum LogLevel {
3238
// (undocumented)
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import * as os from 'node:os';
2+
import * as assert from 'node:assert';
3+
import { describe, it } from 'node:test';
4+
import { format } from './format.js';
5+
6+
void describe('format', () => {
7+
void it('should create a list with each line preceded by a dash', () => {
8+
const lines = ['Item 1', 'Item 2', 'Item 3'];
9+
const expectedOutput = ` - Item 1${os.EOL} - Item 2${os.EOL} - Item 3`;
10+
const actualOutput = format.list(lines);
11+
assert.strictEqual(actualOutput, expectedOutput);
12+
});
13+
14+
void it('should indent the message by two spaces', () => {
15+
const message = `Hello${os.EOL}World`;
16+
const expectedOutput = ` Hello${os.EOL} World`;
17+
const actualOutput = format.indent(message);
18+
assert.strictEqual(actualOutput, expectedOutput);
19+
});
20+
21+
void it('should return an empty string when the input is empty', () => {
22+
const message = '';
23+
const expectedOutput = '';
24+
const actualOutput = format.indent(message);
25+
assert.strictEqual(actualOutput, expectedOutput);
26+
});
27+
28+
void it('should allow chaining indent calls to increase indentation', () => {
29+
const message = `Hello${os.EOL}World`;
30+
const expectedOutput = ` Hello${os.EOL} World`;
31+
const actualOutput = format.indent(format.indent(message));
32+
assert.strictEqual(actualOutput, expectedOutput);
33+
});
34+
});
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import * as os from 'node:os';
2+
3+
/**
4+
* Formats various inputs into single string.
5+
*/
6+
export const format = {
7+
list: (lines: string[]) =>
8+
lines.map((line: string) => ` - ${line}`).join(os.EOL),
9+
indent: (message: string) => {
10+
if (message === '') {
11+
return '';
12+
}
13+
const spaces = ' '; // Two spaces for indentation
14+
return message
15+
.split(os.EOL)
16+
.map((line) => `${spaces}${line}`)
17+
.join(os.EOL);
18+
},
19+
};

packages/cli-core/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export * from './prompter/amplify_prompts.js';
22
export { COLOR } from './colors.js';
33
export * from './printer/printer.js';
4+
export * from './format/format.js';
45
export * from './package-manager-controller/package_manager_controller_factory.js';

packages/cli/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
"@aws-sdk/region-config-resolver": "^3.465.0",
4545
"@smithy/node-config-provider": "^2.1.3",
4646
"@smithy/shared-ini-file-loader": "^2.2.5",
47+
"envinfo": "^7.11.0",
4748
"execa": "^8.0.1",
4849
"is-ci": "^3.0.1",
4950
"open": "^9.1.0",
@@ -54,6 +55,7 @@
5455
"@aws-sdk/types": "^3.465.0"
5556
},
5657
"devDependencies": {
58+
"@types/envinfo": "^7.8.3",
5759
"@types/yargs": "^17.0.24"
5860
}
5961
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import * as os from 'node:os';
2+
import { describe, it, mock } from 'node:test';
3+
import { createInfoCommand } from './info_command_factory.js';
4+
import { InfoCommand } from './info_command.js';
5+
import { EnvironmentInfoProvider } from '../../info/env_info_provider.js';
6+
import { CdkInfoProvider } from '../../info/cdk_info_provider.js';
7+
import { TestCommandRunner } from '../../test-utils/command_runner.js';
8+
import { printer } from '../../printer.js';
9+
import assert from 'node:assert';
10+
import yargs from 'yargs';
11+
12+
void describe('createInfoCommand', () => {
13+
void it('should return an instance of InfoCommand', () => {
14+
const result = createInfoCommand();
15+
assert.ok(result instanceof InfoCommand);
16+
});
17+
});
18+
19+
void describe('info command run', () => {
20+
const command = createInfoCommand();
21+
const parser = yargs().command(command);
22+
const commandRunner = new TestCommandRunner(parser);
23+
24+
void it('includes info subcommands in help output', async () => {
25+
const output = await commandRunner.runCommand(['info', '--help']);
26+
assert.match(
27+
output,
28+
/info\W*Generates information for Amplify troubleshooting/
29+
);
30+
});
31+
32+
void it('test info command run', async () => {
33+
const environmentInfoProviderMock = new EnvironmentInfoProvider();
34+
const cdkInfoProviderMock = new CdkInfoProvider();
35+
36+
const cdkMockValue = `
37+
AWS environment variables:
38+
AWS_STS_REGIONAL_ENDPOINTS = regional
39+
AWS_NODEJS_CONNECTION_REUSE_ENABLED = 1
40+
AWS_SDK_LOAD_CONFIG = 1
41+
CDK environment variables:
42+
CDK_DEBUG = true
43+
CDK_DISABLE_VERSION_CHECK = true
44+
`;
45+
46+
const environmentInfoMockValue = `
47+
System:
48+
CPU: fake
49+
Memory: fake
50+
OS: fakeOS
51+
Shell: /fake/path
52+
Binaries:
53+
Node: 0.0.0 - /fake/path
54+
npm: 0.0.0 - /fake/path
55+
pnpm: 0.0.0 - /fake/path
56+
Yarn: 0.0.0 - /fake/path
57+
NPM Packages:
58+
fake: 0.0.0
59+
`;
60+
61+
const infoMock = mock.method(
62+
environmentInfoProviderMock,
63+
'getEnvInfo',
64+
async () => environmentInfoMockValue
65+
);
66+
67+
const cdkInfoMock = mock.method(
68+
cdkInfoProviderMock,
69+
'getCdkInfo',
70+
async () => cdkMockValue
71+
);
72+
73+
const mockPrinter = mock.method(printer, 'print', () => ({}));
74+
75+
const command = new InfoCommand(
76+
environmentInfoProviderMock,
77+
cdkInfoProviderMock
78+
);
79+
const parser = yargs().command(command);
80+
const commandRunner = new TestCommandRunner(parser);
81+
await commandRunner.runCommand(['info']);
82+
83+
assert.equal(infoMock.mock.callCount(), 1);
84+
assert.equal(cdkInfoMock.mock.callCount(), 1);
85+
assert.equal(mockPrinter.mock.callCount(), 1);
86+
assert.strictEqual(
87+
mockPrinter.mock.calls[0].arguments[0],
88+
`${environmentInfoMockValue}${os.EOL}${cdkMockValue}`
89+
);
90+
});
91+
});
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import * as os from 'node:os';
2+
import { Argv, CommandModule } from 'yargs';
3+
import { CdkInfoProvider } from '../../info/cdk_info_provider.js';
4+
import { EnvironmentInfoProvider } from '../../info/env_info_provider.js';
5+
import { printer } from '../../printer.js';
6+
7+
/**
8+
* Represents the InfoCommand class.
9+
*/
10+
export class InfoCommand implements CommandModule<object> {
11+
/**
12+
* @inheritDoc
13+
*/
14+
readonly command: string;
15+
16+
/**
17+
* @inheritDoc
18+
*/
19+
readonly describe: string;
20+
21+
/**
22+
* Creates top level entry point for generate command.
23+
*/
24+
constructor(
25+
private readonly environmentInfoProvider: EnvironmentInfoProvider,
26+
private readonly cdkInfoProvider: CdkInfoProvider
27+
) {
28+
this.command = 'info';
29+
this.describe = 'Generates information for Amplify troubleshooting';
30+
}
31+
32+
/**
33+
* @inheritDoc
34+
*/
35+
handler = async (): Promise<void> => {
36+
const environmentInfo = await this.environmentInfoProvider.getEnvInfo();
37+
const cdkInfo = await this.cdkInfoProvider.getCdkInfo();
38+
39+
printer.print(`${environmentInfo}${os.EOL}${cdkInfo}`);
40+
};
41+
42+
/**
43+
* @inheritDoc
44+
*/
45+
builder = (yargs: Argv): Argv => {
46+
return yargs.version(false).help();
47+
};
48+
}

0 commit comments

Comments
 (0)