Skip to content
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/cyan-impalas-protect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@aws-amplify/backend-cli': patch
---

stackname validation now allows customers to point to nested stack
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ import { beforeEach, describe, it, mock } from 'node:test';
import { GenerateOutputsCommand } from './generate_outputs_command.js';
import { ClientConfigFormat } from '@aws-amplify/client-config';
import yargs, { CommandModule } from 'yargs';
import { TestCommandRunner } from '../../../test-utils/command_runner.js';
import {
TestCommandError,
TestCommandRunner,
} from '../../../test-utils/command_runner.js';
import assert from 'node:assert';
import { AppBackendIdentifierResolver } from '../../../backend-identifier/backend_identifier_resolver.js';
import { ClientConfigGeneratorAdapter } from '../../../client-config/client_config_generator_adapter.js';
Expand Down Expand Up @@ -82,6 +85,37 @@ void describe('generate outputs command', () => {
);
});

void it('generates and writes config for stack with slashes', async () => {
await commandRunner.runCommand(
'outputs --stack parent/child --out-dir /foo/bar'
);
assert.equal(generateClientConfigMock.mock.callCount(), 1);
assert.deepEqual(generateClientConfigMock.mock.calls[0].arguments[0], {
stackName: 'parent/child',
});
});

void it('throws an error for invalid stack name', async () => {
await assert.rejects(
commandRunner.runCommand('outputs --stack 1invalid --out-dir /foo/bar'),
(error: TestCommandError) => {
assert.strictEqual(error.error.name, 'InvalidStackNameError');
assert.strictEqual(error.error.message, 'Invalid stack name: 1invalid');
return true;
}
);
});

void it('accepts valid stack name with hyphens', async () => {
await commandRunner.runCommand(
'outputs --stack valid-stack-name --out-dir /foo/bar'
);
assert.equal(generateClientConfigMock.mock.callCount(), 1);
assert.deepEqual(generateClientConfigMock.mock.calls[0].arguments[0], {
stackName: 'valid-stack-name',
});
});

void it('generates and writes config for branch', async () => {
await commandRunner.runCommand(
'outputs --branch branch_name --out-dir /foo/bar'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,19 @@ export class GenerateOutputsCommand
array: false,
choices: Object.values(ClientConfigVersionOption),
default: DEFAULT_CLIENT_CONFIG_VERSION,
})
.check((argv) => {
if (argv.stack) {
const identifierRegex = /^[a-zA-Z][-_a-zA-Z0-9/]*$/;
if (!argv.stack.match(identifierRegex)) {
throw new AmplifyUserError('InvalidStackNameError', {
message: `Invalid stack name: ${argv.stack}`,
resolution:
'Stack name must start with a letter and can only contain alphanumeric characters, hyphens, underscores and slashes.',
});
}
}
return true;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This won't solve the problem.

I ran the command with something that looks like nested stack and the validation error is coming from different place than CLI. See:

bcd0740fa72c:testapp057 sobkamil$ npx ampx generate outputs --stack ParentStackA/InsdeWhichAnotherIsInstatiatedStack --debug

ampx generate outputs

Generates Amplify backend outputs

Stack identifier
  --stack  A stack name that contains an Amplify backend                [string]

Project identifier
  --app-id  The Amplify App ID of the project                           [string]
  --branch  A git branch of the Amplify project                         [string]

Options:
  --debug            Print debug logs to the console  [boolean] [default: false]
  --help             Show help                                         [boolean]
  --profile          An AWS profile name.                               [string]
  --format           The format which the configuration should be exported into.
                  [string] [choices: "mjs", "json", "json-mobile", "ts", "dart"]
  --out-dir          A path to directory where config is written. If not provide
                     d defaults to current process working directory.   [string]
  --outputs-version  Version of the configuration. Version 0 represents classic
                     amplify-cli config file amplify-configuration and 1 represe
                     nts newer config file amplify_outputs
                     [string] [choices: "0", "1", "1.1", "1.2"] [default: "1.2"]

ValidationError: 1 validation error detected: Value 'ParentStackA/InsdeWhichAnotherIsInstatiatedStack' at 'stackName' failed to satisfy constraint: Member must satisfy regular expression pattern: [a-zA-Z][-a-zA-Z0-9]*|arn:[-a-zA-Z0-9:/._+]*
[DEBUG] 2024-10-15T22:12:17.609Z: ValidationError: 1 validation error detected: Value 'ParentStackA/InsdeWhichAnotherIsInstatiatedStack' at 'stackName' failed to satisfy constraint: Member must satisfy regular expression pattern: [a-zA-Z][-a-zA-Z0-9]*|arn:[-a-zA-Z0-9:/._+]*
    at throwDefaultError (/Users/sobkamil/git/amplify-backend/node_modules/@smithy/smithy-client/dist-cjs/index.js:844:20)
    at /Users/sobkamil/git/amplify-backend/node_modules/@smithy/smithy-client/dist-cjs/index.js:853:5
    at de_CommandError (/Users/sobkamil/git/amplify-backend/node_modules/@aws-sdk/client-cloudformation/dist-cjs/index.js:3270:14)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async /Users/sobkamil/git/amplify-backend/node_modules/@smithy/middleware-serde/dist-cjs/index.js:35:20
    at async /Users/sobkamil/git/amplify-backend/node_modules/@smithy/core/dist-cjs/index.js:165:18
    at async /Users/sobkamil/git/amplify-backend/node_modules/@smithy/middleware-retry/dist-cjs/index.js:320:38
    at async /Users/sobkamil/git/amplify-backend/node_modules/@aws-sdk/middleware-logger/dist-cjs/index.js:34:22
    at async StackMetadataBackendOutputRetrievalStrategy.fetchBackendOutput (file:///Users/sobkamil/git/amplify-backend/packages/deployed-backend-client/lib/stack_metadata_output_retrieval_strategy.js:28:37)
    at async DefaultBackendOutputClient.getOutput (file:///Users/sobkamil/git/amplify-backend/packages/deployed-backend-client/lib/backend_output_client.js:18:24)

I.e. it's coming from here https://github.com/aws-amplify/amplify-backend/blob/fb3538c58cfb25b6ae8aa5d4036be05bf1ee2181/packages/deployed-backend-client/src/stack_metadata_output_retrieval_strategy.ts#L45-L47 .

});
};
}