Skip to content

Commit ace37a2

Browse files
authored
feat(cli): watch command now starts with a deployment (#18057)
Similar to `tsc -w`, `cdk watch` should trigger an initial deployment instead of waiting for a file change event. We achieve this by pulling out the callback function for `'all'` and calling that function during the `'ready'` callback. Closes #17776. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent dcc9e59 commit ace37a2

File tree

2 files changed

+34
-18
lines changed

2 files changed

+34
-18
lines changed

packages/aws-cdk/lib/cdk-toolkit.ts

+23-15
Original file line numberDiff line numberDiff line change
@@ -295,31 +295,39 @@ export class CdkToolkit {
295295
// | | | | <------------------ | | <------------------ | | <-------------|
296296
// -------------- -------- 'cdk deploy' done -------------- 'cdk deploy' done --------------
297297
let latch: 'pre-ready' | 'open' | 'deploying' | 'queued' = 'pre-ready';
298+
299+
const deployAndWatch = async () => {
300+
latch = 'deploying';
301+
302+
await this.invokeDeployFromWatch(options);
303+
304+
// If latch is still 'deploying' after the 'await', that's fine,
305+
// but if it's 'queued', that means we need to deploy again
306+
while ((latch as 'deploying' | 'queued') === 'queued') {
307+
// TypeScript doesn't realize latch can change between 'awaits',
308+
// and thinks the above 'while' condition is always 'false' without the cast
309+
latch = 'deploying';
310+
print("Detected file changes during deployment. Invoking 'cdk deploy' again");
311+
await this.invokeDeployFromWatch(options);
312+
}
313+
latch = 'open';
314+
};
315+
298316
chokidar.watch(watchIncludes, {
299317
ignored: watchExcludes,
300318
cwd: rootDir,
301319
// ignoreInitial: true,
302-
}).on('ready', () => {
320+
}).on('ready', async () => {
303321
latch = 'open';
304322
debug("'watch' received the 'ready' event. From now on, all file changes will trigger a deployment");
305-
}).on('all', async (event, filePath) => {
323+
print("Triggering initial 'cdk deploy'");
324+
await deployAndWatch();
325+
}).on('all', async (event: 'add' | 'addDir' | 'change' | 'unlink' | 'unlinkDir', filePath?: string) => {
306326
if (latch === 'pre-ready') {
307327
print(`'watch' is observing ${event === 'addDir' ? 'directory' : 'the file'} '%s' for changes`, filePath);
308328
} else if (latch === 'open') {
309-
latch = 'deploying';
310329
print("Detected change to '%s' (type: %s). Triggering 'cdk deploy'", filePath, event);
311-
await this.invokeDeployFromWatch(options);
312-
313-
// If latch is still 'deploying' after the 'await', that's fine,
314-
// but if it's 'queued', that means we need to deploy again
315-
while ((latch as 'deploying' | 'queued') === 'queued') {
316-
// TypeScript doesn't realize latch can change between 'awaits',
317-
// and thinks the above 'while' condition is always 'false' without the cast
318-
latch = 'deploying';
319-
print("Detected file changes during deployment. Invoking 'cdk deploy' again");
320-
await this.invokeDeployFromWatch(options);
321-
}
322-
latch = 'open';
330+
await deployAndWatch();
323331
} else { // this means latch is either 'deploying' or 'queued'
324332
latch = 'queued';
325333
print("Detected change to '%s' (type: %s) while 'cdk deploy' is still running. " +

packages/aws-cdk/test/cdk-toolkit.test.ts

+11-3
Original file line numberDiff line numberDiff line change
@@ -350,13 +350,20 @@ describe('watch', () => {
350350

351351
describe("when the 'ready' event has already fired", () => {
352352
beforeEach(() => {
353+
// The ready callback triggers a deployment so each test
354+
// that uses this function will see 'cdkDeployMock' called
355+
// an additional time.
353356
fakeChokidarWatcherOn.readyCallback();
354357
});
355358

359+
test("an initial 'deploy' is triggered, without any file changes", async () => {
360+
expect(cdkDeployMock).toHaveBeenCalledTimes(1);
361+
});
362+
356363
test("does trigger a 'deploy' for a file change", async () => {
357364
await fakeChokidarWatcherOn.fileEventCallback('add', 'my-file');
358365

359-
expect(cdkDeployMock).toHaveBeenCalled();
366+
expect(cdkDeployMock).toHaveBeenCalledTimes(2);
360367
});
361368

362369
test("triggers a 'deploy' twice for two file changes", async () => {
@@ -365,17 +372,18 @@ describe('watch', () => {
365372
fakeChokidarWatcherOn.fileEventCallback('change', 'my-file2'),
366373
]);
367374

368-
expect(cdkDeployMock).toHaveBeenCalledTimes(2);
375+
expect(cdkDeployMock).toHaveBeenCalledTimes(3);
369376
});
370377

371378
test("batches file changes that happen during 'deploy'", async () => {
372379
await Promise.all([
373380
fakeChokidarWatcherOn.fileEventCallback('add', 'my-file1'),
374381
fakeChokidarWatcherOn.fileEventCallback('change', 'my-file2'),
375382
fakeChokidarWatcherOn.fileEventCallback('unlink', 'my-file3'),
383+
fakeChokidarWatcherOn.fileEventCallback('add', 'my-file4'),
376384
]);
377385

378-
expect(cdkDeployMock).toHaveBeenCalledTimes(2);
386+
expect(cdkDeployMock).toHaveBeenCalledTimes(3);
379387
});
380388
});
381389
});

0 commit comments

Comments
 (0)