Skip to content

feat(cli): watch command now starts with a deployment#18057

Merged
mergify[bot] merged 12 commits intoaws:masterfrom
kaizencc:conroy/watchdeploy
Dec 17, 2021
Merged

feat(cli): watch command now starts with a deployment#18057
mergify[bot] merged 12 commits intoaws:masterfrom
kaizencc:conroy/watchdeploy

Conversation

@kaizencc
Copy link
Copy Markdown
Contributor

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

@kaizencc kaizencc requested a review from skinny85 December 16, 2021 17:58
@gitpod-io
Copy link
Copy Markdown

gitpod-io bot commented Dec 16, 2021

@mergify mergify bot added the contribution/core This is a PR that came from AWS. label Dec 16, 2021
@github-actions github-actions bot added the package/tools Related to AWS CDK Tools or CLI label Dec 16, 2021
@kaizencc
Copy link
Copy Markdown
Contributor Author

Don't think we need a readme for this one so I'm going to say readme-exempt, though if you disagree we can discuss :).

@kaizencc kaizencc added the pr-linter/exempt-readme The PR linter will not require README changes label Dec 16, 2021
// trigger a dummy event here to make an intial deployment,
// the values sent into workflow are dummy values.
// intentionally not awaiting the result so that we can begin to watch and queue changes.
void workflow('add', 'initial');
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hmmm. Forgetting this promise like this doesn't seem great to me. Wouldn't this trigger an "uncaught promise warning" if the initial deployment fails?

Can you try returning this promise from function, and see if that helps?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I changed it to what I think you're asking for. My thoughts here are that it feels wrong to make the 'ready' callback async because during that time we would lose track of file changes -- is my instinct there correct?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I also tested my own CDK stack (that fails deployment) with void handleFileChangeEvent and I received the same "failed to deploy" message that I was expecting:

Screen Shot 2021-12-16 at 1 42 17 PM

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

My thoughts here are that it feels wrong to make the 'ready' callback async because during that time we would lose track of file changes -- is my instinct there correct?

Can you check what the behavior is manually, and report back here? 🙂

Copy link
Copy Markdown
Contributor Author

@kaizencc kaizencc Dec 16, 2021

Choose a reason for hiding this comment

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

So here's what I found:

You can specify async callbacks, and typescript will not yell at you, but they will be ignored. see this issue. For example, even though the handleFileChangeEvent function is async, no one is actually waiting for the event to finish. Same goes for the callback at the 'ready' event.

As to any unhandled promise warning, I think we are in the clear. It seems related to how we are catching errors in invokeDeployFromWatch anyway -- we ignore them, and deploy will show them (as I have confirmed locally when using void handleFileChangeEvent()).

} catch (e) {
// just continue - deploy will show the error
}

All that to say, I think we are fine with void handleFileChangeEvent() we are also fine with return handleFileChangeEvent(). In both cases, a failed deployment will show on screen as expected, with no unhandled promises.

We could even do await handleFileChangeEvent(). That would result in the same behavior.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

With that in mind I've changed the function to be async and we await handleFileChangeEvent(). But it is an inconsequential change. Can confirm that the behavior for this is as expected on my local computer -- I get the deployment failure or success message printed out. And watch is immediately listening for file changes (not waiting for first deployment to finish).

@kaizencc kaizencc requested a review from skinny85 December 16, 2021 18:40
Copy link
Copy Markdown
Contributor

@skinny85 skinny85 left a comment

Choose a reason for hiding this comment

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

Looks great @kaizen3031593! One tiny small change, and then is ready to be merged.

latch = 'open';
debug("'watch' received the 'ready' event. From now on, all file changes will trigger a deployment");
}).on('all', async (event, filePath) => {

Copy link
Copy Markdown
Contributor

@skinny85 skinny85 Dec 17, 2021

Choose a reason for hiding this comment

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

One thing I think we can improve here is that the extracted function is too big. We need only the deployment part (when latch is 'open'), but we extracted all of it, even the parts that we don't need. That's why we have the awkwardness with the filePath having to be undefined.

So, I think the way to go is something like this:

    chokidar.watch(watchIncludes, {
      ignored: watchExcludes,
      cwd: rootDir,
      // ignoreInitial: true,
    }).on('ready', () => {
      latch = 'open';
      debug("'watch' received the 'ready' event. From now on, all file changes will trigger a deployment");
      print("Triggering initial 'cdk deploy'");
      await deployWhileObservingForFileChanges();
    }).on('all', async (event, filePath) => {
      if (latch === 'pre-ready') {
        print(`'watch' is observing ${event === 'addDir' ? 'directory' : 'the file'} '%s' for changes`, filePath);
      } else if (latch === 'open') {
        print("Detected change to '%s' (type: %s). Triggering 'cdk deploy'", filePath, event);
        await deployWhileObservingForFileChanges(); // this name TBD of course
      } else { // this means latch is either 'deploying' or 'queued'
        latch = 'queued';
        print("Detected change to '%s' (type: %s) while 'cdk deploy' is still running. " +
            'Will queue for another deployment after this one finishes', filePath, event);
      }
    });

And deployWhileObservingForFileChanges is:

      async function deployWhileObservingForFileChanges() {
        latch = 'deploying';

        await this.invokeDeployFromWatch(options);

        // If latch is still 'deploying' after the 'await', that's fine,
        // but if it's 'queued', that means we need to deploy again
        while ((latch as 'deploying' | 'queued') === 'queued') {
          // TypeScript doesn't realize latch can change between 'awaits',
          // and thinks the above 'while' condition is always 'false' without the cast
          latch = 'deploying';
          print("Detected file changes during deployment. Invoking 'cdk deploy' again");
          await this.invokeDeployFromWatch(options);
        }
        latch = 'open';
      }

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

currently named depoyAndWatch, hopefully that is reasonable.

@kaizencc kaizencc requested a review from skinny85 December 17, 2021 01:53
Copy link
Copy Markdown
Contributor

@skinny85 skinny85 left a comment

Choose a reason for hiding this comment

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

Looks perfect, thanks @kaizen3031593!

@mergify
Copy link
Copy Markdown
Contributor

mergify bot commented Dec 17, 2021

Thank you for contributing! Your pull request will be updated from master and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork).

@skinny85 skinny85 changed the title feat(cli): watch command kicks off initial deployment feat(cli): watch command now starts with a deployment Dec 17, 2021
@mergify
Copy link
Copy Markdown
Contributor

mergify bot commented Dec 17, 2021

Thank you for contributing! Your pull request will be updated from master and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork).

@aws-cdk-automation
Copy link
Copy Markdown
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: AutoBuildProject89A8053A-LhjRyN9kxr8o
  • Commit ID: 34f250c
  • Result: SUCCEEDED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@mergify mergify bot merged commit ace37a2 into aws:master Dec 17, 2021
@mergify
Copy link
Copy Markdown
Contributor

mergify bot commented Dec 17, 2021

Thank you for contributing! Your pull request will be updated from master and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork).

TikiTDO pushed a commit to TikiTDO/aws-cdk that referenced this pull request Feb 21, 2022
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 aws#17776.

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contribution/core This is a PR that came from AWS. package/tools Related to AWS CDK Tools or CLI pr-linter/exempt-readme The PR linter will not require README changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

(cli): I think cdk watch should deploy first

4 participants