Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into rollback
Browse files Browse the repository at this point in the history
  • Loading branch information
edwardfoyle committed Apr 5, 2024
2 parents e078281 + ffb358e commit 134b42e
Show file tree
Hide file tree
Showing 36 changed files with 299 additions and 75 deletions.
8 changes: 8 additions & 0 deletions .changeset/beige-dragons-boil.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@aws-amplify/integration-tests': patch
'create-amplify': patch
'@aws-amplify/client-config': patch
'@aws-amplify/backend-cli': patch
---

chore: rename the new client config file name
5 changes: 5 additions & 0 deletions .changeset/modern-fans-return.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@aws-amplify/backend-cli': patch
---

Added normalization logic for AWS environment variables
6 changes: 6 additions & 0 deletions .changeset/silent-rules-obey.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@aws-amplify/integration-tests': patch
'create-amplify': patch
---

fix: order of array in test assert in gitignored tests
7 changes: 7 additions & 0 deletions .changeset/tough-bananas-mix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@aws-amplify/cli-core': patch
'@aws-amplify/sandbox': patch
'@aws-amplify/backend-cli': patch
---

chore: Adds a log message to inform the name of the sandbox being created/initialized
3 changes: 3 additions & 0 deletions packages/cli-core/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,11 @@ export const format: {
error: (message: string) => string;
note: (message: string) => string;
command: (command: string) => string;
highlight: (command: string) => string;
success: (message: string) => string;
sectionHeader: (header: string) => string;
bold: (message: string) => string;
dim: (message: string) => string;
link: (link: string) => string;
list: (lines: string[]) => string;
indent: (message: string) => string;
Expand Down
14 changes: 13 additions & 1 deletion packages/cli-core/src/format/format.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
import * as os from 'node:os';
import { blue, bold, cyan, green, grey, red, underline } from 'kleur/colors';
import {
blue,
bold,
cyan,
dim,
green,
grey,
red,
underline,
} from 'kleur/colors';

/**
* Formats various inputs into single string.
Expand All @@ -16,8 +25,11 @@ export const format = {
error: (message: string) => red(message),
note: (message: string) => grey(message),
command: (command: string) => cyan(command),
highlight: (command: string) => cyan(command),
success: (message: string) => green(message),
sectionHeader: (header: string) => bold(blue(header)),
bold: (message: string) => bold(message),
dim: (message: string) => dim(message),
link: (link: string) => underline(blue(link)),
list: (lines: string[]) =>
lines.map((line: string) => ` - ${line}`).join(os.EOL),
Expand Down
81 changes: 78 additions & 3 deletions packages/cli/src/command_middleware.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import assert from 'node:assert';
import { after, before, beforeEach, describe, it } from 'node:test';
import { after, before, beforeEach, describe, it, mock } from 'node:test';
import { CommandMiddleware } from './command_middleware.js';
import { EOL } from 'node:os';
import { DEFAULT_PROFILE } from '@smithy/shared-ini-file-loader';
import fs from 'fs/promises';
import path from 'path';
import { ArgumentsCamelCase } from 'yargs';
import { Printer } from '@aws-amplify/cli-core';

const restoreEnv = (restoreVal: string | undefined, envVar: string) => {
if (restoreVal) {
Expand All @@ -17,7 +18,10 @@ const restoreEnv = (restoreVal: string | undefined, envVar: string) => {

void describe('commandMiddleware', () => {
void describe('ensureAwsCredentialAndRegion', () => {
const commandMiddleware = new CommandMiddleware();
const printerMock = { log: mock.fn() };
const commandMiddleware = new CommandMiddleware(
printerMock as unknown as Printer
);
const testAccessKeyId = '124';
const testSecretAccessKey = '667';
const testProfile = 'profileA';
Expand All @@ -28,11 +32,13 @@ void describe('commandMiddleware', () => {
let configFilePath: string;

const currentProfile = process.env.AWS_PROFILE;
const currentDefaultProfile = process.env.AWS_DEFAULT_PROFILE;
const currentConfigFile = process.env.AWS_CONFIG_FILE;
const currentCredentialFile = process.env.AWS_SHARED_CREDENTIALS_FILE;
const currentAccessKeyId = process.env.AWS_ACCESS_KEY_ID;
const currentSecretAccessKey = process.env.AWS_SECRET_ACCESS_KEY;
const currentRegion = process.env.AWS_REGION;
const currentDefaultRegion = process.env.AWS_DEFAULT_REGION;

before(async () => {
testDir = await fs.mkdtemp('profile_middleware_test');
Expand All @@ -45,11 +51,13 @@ void describe('commandMiddleware', () => {
after(async () => {
await fs.rm(testDir, { recursive: true, force: true });
restoreEnv(currentProfile, 'AWS_PROFILE');
restoreEnv(currentDefaultProfile, 'AWS_DEFAULT_PROFILE');
restoreEnv(currentConfigFile, 'AWS_CONFIG_FILE');
restoreEnv(currentCredentialFile, 'AWS_SHARED_CREDENTIALS_FILE');
restoreEnv(currentAccessKeyId, 'AWS_ACCESS_KEY_ID');
restoreEnv(currentSecretAccessKey, 'AWS_SECRET_ACCESS_KEY');
restoreEnv(currentRegion, 'AWS_REGION');
restoreEnv(currentDefaultRegion, 'AWS_DEFAULT_REGION');
});

void describe('from environment variables', () => {
Expand All @@ -58,6 +66,7 @@ void describe('commandMiddleware', () => {
process.env.AWS_SECRET_ACCESS_KEY = testSecretAccessKey;
process.env.AWS_REGION = testRegion;
delete process.env.AWS_PROFILE;
printerMock.log.mock.resetCalls();
});

void it('loads credentials', async () => {
Expand All @@ -68,8 +77,38 @@ void describe('commandMiddleware', () => {
);
});

void it('throws error if absent region environment variable', async () => {
void it('maps AWS_DEFAULT_REGION to AWS_REGION', async () => {
delete process.env.AWS_REGION;
process.env.AWS_DEFAULT_REGION = testRegion;
await assert.doesNotReject(() =>
commandMiddleware.ensureAwsCredentialAndRegion(
{} as ArgumentsCamelCase<{ profile: string | undefined }>
)
);
assert.equal(process.env.AWS_REGION, testRegion);
assert.match(
printerMock.log.mock.calls[0].arguments[0],
/Legacy environment variable/
);
});

void it('prefers AWS_REGION to AWS_DEFAULT_REGION', async () => {
process.env.AWS_DEFAULT_REGION = 'testDefaultRegion';
await assert.doesNotReject(() =>
commandMiddleware.ensureAwsCredentialAndRegion(
{} as ArgumentsCamelCase<{ profile: string | undefined }>
)
);
assert.equal(process.env.AWS_REGION, testRegion);
assert.match(
printerMock.log.mock.calls[0].arguments[0],
/Using 'AWS_REGION'/
);
});

void it('throws error if absent region environment variables', async () => {
delete process.env.AWS_REGION;
delete process.env.AWS_DEFAULT_REGION;
try {
await commandMiddleware.ensureAwsCredentialAndRegion(
{} as ArgumentsCamelCase<{ profile: string | undefined }>
Expand Down Expand Up @@ -110,6 +149,7 @@ void describe('commandMiddleware', () => {
await fs.writeFile(credFilePath, '', 'utf-8');
await fs.writeFile(configFilePath, '', 'utf-8');
delete process.env.AWS_PROFILE;
printerMock.log.mock.resetCalls();
});

const writeProfileCredential = async (
Expand Down Expand Up @@ -208,6 +248,41 @@ void describe('commandMiddleware', () => {
} as ArgumentsCamelCase<{ profile: string | undefined }>)
);
});

void it('maps AWS_DEFAULT_PROFILE to AWS_PROFILE', async () => {
process.env.AWS_DEFAULT_PROFILE = testProfile;
await writeProfileRegion(testProfile);
await writeProfileCredential(testProfile);

await assert.doesNotReject(() =>
commandMiddleware.ensureAwsCredentialAndRegion(
{} as ArgumentsCamelCase<{ profile: string | undefined }>
)
);
assert.equal(process.env.AWS_PROFILE, testProfile);
assert.match(
printerMock.log.mock.calls[0].arguments[0],
/Legacy environment variable/
);
});

void it('prefers AWS_PROFILE over AWS_DEFAULT_PROFILE', async () => {
process.env.AWS_DEFAULT_PROFILE = 'testDefaultProfile';
process.env.AWS_PROFILE = testProfile;
await writeProfileRegion(testProfile);
await writeProfileCredential(testProfile);

await assert.doesNotReject(() =>
commandMiddleware.ensureAwsCredentialAndRegion(
{} as ArgumentsCamelCase<{ profile: string | undefined }>
)
);
assert.equal(process.env.AWS_PROFILE, testProfile);
assert.match(
printerMock.log.mock.calls[0].arguments[0],
/Using 'AWS_PROFILE'/
);
});
});
});
});
37 changes: 37 additions & 0 deletions packages/cli/src/command_middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,19 @@ import { fromNodeProviderChain } from '@aws-sdk/credential-providers';
import { loadConfig } from '@smithy/node-config-provider';
import { NODE_REGION_CONFIG_OPTIONS } from '@aws-sdk/region-config-resolver';
import { AmplifyUserError } from '@aws-amplify/platform-core';
import { Printer } from '@aws-amplify/cli-core';

export const profileSetupInstruction = `To configure a new Amplify profile, use "npx amplify configure profile".`;

/**
* Contains middleware functions.
*/
export class CommandMiddleware {
/**
* Creates command middleware.
*/
constructor(private readonly printer: Printer) {}

/**
* Ensure AWS credentials and region of the input profile (or 'default' if undefined) are available in the provider chain.
* If the input profile is defined, the environment variable AWS_PROFILE will be set accordingly.
Expand All @@ -19,6 +25,15 @@ export class CommandMiddleware {
>(
argv: ArgumentsCamelCase<T>
) => {
/**
* The AWS CDK respects older CLI v1 variable names that are no longer supported in the
* latest AWS SDK. Developers that use the older variables and switch between Amplify
* and CDK tools will experience region mismatch failures when using Amplify tools. Variable
* names known to cause such failures are mapped here for a better developer experience.
*/
this.mapEnvironmentVariables('AWS_DEFAULT_REGION', 'AWS_REGION');
this.mapEnvironmentVariables('AWS_DEFAULT_PROFILE', 'AWS_PROFILE');

if (argv.profile) {
process.env.AWS_PROFILE = argv.profile;
}
Expand Down Expand Up @@ -61,4 +76,26 @@ export class CommandMiddleware {
);
}
};

/**
* Maps one environment variable name to the other
*/
private mapEnvironmentVariables(
legacyName: string,
preferredName: string
): void {
if (!process.env[legacyName]) {
return;
}
if (process.env[preferredName]) {
this.printer.log(
`Both the legacy '${legacyName}' and preferred '${preferredName}' environment variables detected. Using '${preferredName}'`
);
return;
}
this.printer.log(
`Legacy environment variable '${legacyName}' detected. Mapping to '${preferredName}'`
);
process.env[preferredName] = process.env[legacyName];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ export class GenerateConfigCommand
})
.option('config-version', {
describe:
'Version of the client config. Version 0 represents classic amplify-cli client config amplify-configuration (Default) and 1 represents new unified client config amplify-outputs',
'Version of the client config. Version 0 represents classic amplify-cli client config amplify-configuration (Default) and 1 represents new unified client config amplify_outputs',
type: 'string',
array: false,
choices: Object.values(ClientConfigVersionOption),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { AppBackendIdentifierResolver } from '../../backend-identifier/backend_i
import { GenerateSchemaCommand } from './schema-from-database/generate_schema_command.js';
import { getSecretClient } from '@aws-amplify/backend-secret';
import { SchemaGenerator } from '@aws-amplify/schema-generator';
import { printer } from '@aws-amplify/cli-core';

/**
* Creates wired generate command.
Expand Down Expand Up @@ -63,7 +64,7 @@ export const createGenerateCommand = (): CommandModule => {
new SchemaGenerator()
);

const commandMiddleware = new CommandMiddleware();
const commandMiddleware = new CommandMiddleware(printer);

return new GenerateCommand(
generateConfigCommand,
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/commands/sandbox/option_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export type SandboxCommandGlobalOptions = {
*/
profile?: string;
/**
* Optional name to use to distinguish multiple sandboxes
* Optional identifier to use to distinguish multiple sandboxes.
*/
name?: string;
identifier?: string;
};
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ void describe('sandbox delete command', () => {
let commandRunner: TestCommandRunner;
let sandboxDeleteMock = mock.fn();

const commandMiddleware = new CommandMiddleware();
const commandMiddleware = new CommandMiddleware(printer);
const mockHandleProfile = mock.method(
commandMiddleware,
'ensureAwsCredentialAndRegion',
Expand Down Expand Up @@ -60,7 +60,7 @@ void describe('sandbox delete command', () => {

assert.equal(sandboxDeleteMock.mock.callCount(), 1);
assert.deepStrictEqual(sandboxDeleteMock.mock.calls[0].arguments[0], {
name: undefined,
identifier: undefined,
});
assert.equal(mockHandleProfile.mock.callCount(), 1);
assert.equal(
Expand All @@ -69,15 +69,15 @@ void describe('sandbox delete command', () => {
);
});

void it('deletes sandbox with user provided name', async (contextual) => {
void it('deletes sandbox with user provided identifier', async (contextual) => {
contextual.mock.method(AmplifyPrompter, 'yesOrNo', () =>
Promise.resolve(true)
);
await commandRunner.runCommand('sandbox delete --name test-App-Name');
await commandRunner.runCommand('sandbox delete --identifier test-App-Name');

assert.equal(sandboxDeleteMock.mock.callCount(), 1);
assert.deepStrictEqual(sandboxDeleteMock.mock.calls[0].arguments[0], {
name: 'test-App-Name',
identifier: 'test-App-Name',
});
});

Expand All @@ -98,7 +98,7 @@ void describe('sandbox delete command', () => {
void it('shows available options in help output', async () => {
const output = await commandRunner.runCommand('sandbox delete --help');
assert.match(output, /--yes/);
assert.match(output, /--name/);
assert.match(output, /--identifier/);
assert.doesNotMatch(output, /--exclude/);
assert.doesNotMatch(output, /--dir-to-watch/);
assert.equal(mockHandleProfile.mock.callCount(), 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export class SandboxDeleteCommand
if (isConfirmed) {
await (
await this.sandboxFactory.getInstance()
).delete({ name: args.name });
).delete({ identifier: args.identifier });
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ void describe('sandbox secret get command', () => {
);

const sandboxIdResolver: SandboxBackendIdResolver = {
resolve: (nameOverride?: string) =>
resolve: (identifier?: string) =>
Promise.resolve({
namespace: testBackendId,
name: nameOverride || testSandboxName,
name: identifier || testSandboxName,
type: 'sandbox',
}),
} as SandboxBackendIdResolver;
Expand Down Expand Up @@ -79,7 +79,9 @@ void describe('sandbox secret get command', () => {
});

void it('gets secret for named sandbox', async () => {
await commandRunner.runCommand(`get ${testSecretName} --name anotherName`);
await commandRunner.runCommand(
`get ${testSecretName} --identifier anotherName`
);

assert.equal(secretGetMock.mock.callCount(), 1);
assert.deepStrictEqual(secretGetMock.mock.calls[0].arguments, [
Expand Down
Loading

0 comments on commit 134b42e

Please sign in to comment.