Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
15 changes: 14 additions & 1 deletion packages/aws-cdk/lib/cli/cdk-toolkit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import {
} from '../commands/migrate';
import type { CloudAssembly, CloudExecutable, StackSelector } from '../cxapp';
import { DefaultSelection, environmentsFromDescriptors, globEnvironmentsFromStacks, looksLikeGlob } from '../cxapp';
import { OBSOLETE_FLAGS } from '../obsolete-flags';
import {
deserializeStructure,
formatErrorMessage,
Expand Down Expand Up @@ -195,7 +196,7 @@ export class CdkToolkit {
emojis: true,
ioHost: this.ioHost,
toolkitStackName: this.toolkitStackName,
unstableFeatures: ['refactor'],
unstableFeatures: ['refactor', 'flags'],
});
}

Expand Down Expand Up @@ -1058,6 +1059,8 @@ export class CdkToolkit {
): Promise<any> {
const stacks = await this.selectStacksForDiff(stackNames, exclusively, autoValidate);

await displayFlagsMessage(this.toolkit, this.props.cloudExecutable, this.ioHost.asIoHelper());

// if we have a single stack, print it to STDOUT
if (stacks.stackCount === 1) {
if (!quiet) {
Expand Down Expand Up @@ -2108,6 +2111,16 @@ async function askUserConfirmation(
}
});
}
export async function displayFlagsMessage(toolkit: InternalToolkit, cloudExecutable: CloudExecutable,
ioHelper: IoHelper): Promise<void> {
let numUnconfigured = (await toolkit.flags(cloudExecutable))
.filter(flag => !OBSOLETE_FLAGS.includes(flag.name))
.filter(flag => flag.userValue === undefined).length;

if (numUnconfigured > 0) {
await ioHelper.defaults.info(`You currently have ${numUnconfigured} unconfigured feature flags that may require attention to keep your application up-to-date. Run 'cdk flags' to learn more.`);
}
}

/**
* Logger for processing stack metadata
Expand Down
1 change: 0 additions & 1 deletion packages/aws-cdk/lib/cli/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,6 @@ export async function exec(args: string[], synthesizer?: Synthesizer): Promise<n
if (!configuration.settings.get(['unstable']).includes('flags')) {
throw new ToolkitError('Unstable feature use: \'flags\' is unstable. It must be opted in via \'--unstable\', e.g. \'cdk flags --unstable=flags\'');
}

const toolkit = new Toolkit({
ioHost,
toolkitStackName,
Expand Down
5 changes: 5 additions & 0 deletions packages/aws-cdk/lib/obsolete-flags.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const OBSOLETE_FLAGS = [
'@aws-cdk/core:enableStackNameDuplicates',
'@aws-cdk/aws-s3:grantWriteWithoutAcl',
'@aws-cdk/aws-kms:defaultKeyPolicies',
];
83 changes: 83 additions & 0 deletions packages/aws-cdk/test/cli/display-flags-message.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { Toolkit } from '@aws-cdk/toolkit-lib';
import { asIoHelper } from '../../lib/api-private';
import { displayFlagsMessage } from '../../lib/cli/cdk-toolkit';
import { TestIoHost } from '../_helpers/io-host';

describe('displayFlagsMessage', () => {
let ioHost: TestIoHost;
let ioHelper: any;
let mockToolkit: jest.Mocked<Toolkit>;
let mockCloudExecutable: any;

beforeEach(() => {
ioHost = new TestIoHost();
ioHelper = asIoHelper(ioHost, 'synth');
mockCloudExecutable = {};

mockToolkit = {
flags: jest.fn(),
} as any;

jest.spyOn(Toolkit.prototype, 'flags').mockImplementation(mockToolkit.flags);
});

afterEach(() => {
jest.restoreAllMocks();
});

test('displays message with correct count of unconfigured flags, filtering out obsolete flags', async () => {
const mockFlagsData = [
{
name: '@aws-cdk/core:testFlag',
userValue: undefined,
recommendedValue: 'true',
explanation: 'Test flag',
module: 'aws-cdk-lib',
},
{
name: '@aws-cdk/s3:anotherFlag',
userValue: 'false',
recommendedValue: 'false',
explanation: 'Another test flag',
module: 'aws-cdk-lib',
},
{
name: '@aws-cdk/core:enableStackNameDuplicates',
userValue: undefined,
recommendedValue: 'true',
explanation: 'Obsolete flag',
module: 'aws-cdk-lib',
},
];

mockToolkit.flags.mockResolvedValue(mockFlagsData);

await displayFlagsMessage(mockToolkit as any, mockCloudExecutable, ioHelper);

expect(mockToolkit.flags).toHaveBeenCalledWith(mockCloudExecutable);
expect(ioHost.notifySpy).toHaveBeenCalledWith(
expect.objectContaining({
message: 'You currently have 1 unconfigured feature flags that may require attention to keep your application up-to-date. Run \'cdk flags\' to learn more.',
level: 'info',
}),
);
});
test('does not display a message when user has no unconfigured flags', async () => {
const mockFlagsData = [
{
name: '@aws-cdk/s3:anotherFlag',
userValue: 'false',
recommendedValue: 'false',
explanation: 'Another test flag',
module: 'aws-cdk-lib',
},
];
mockToolkit.flags.mockResolvedValue(mockFlagsData);

await displayFlagsMessage(mockToolkit as any, mockCloudExecutable, ioHelper);

expect(mockToolkit.flags).toHaveBeenCalledWith(mockCloudExecutable);
expect(ioHost.notifySpy).not.toHaveBeenCalled();
});
});

Loading