diff --git a/packages/aws-cdk/bin/cdk.ts b/packages/aws-cdk/bin/cdk.ts index e96084d5c2c3b..2563ba389d260 100644 --- a/packages/aws-cdk/bin/cdk.ts +++ b/packages/aws-cdk/bin/cdk.ts @@ -3,6 +3,7 @@ import 'source-map-support/register'; import * as cxapi from '@aws-cdk/cx-api'; import * as colors from 'colors/safe'; +import * as fs from 'fs-extra'; import * as path from 'path'; import * as yargs from 'yargs'; @@ -17,7 +18,7 @@ import { availableInitLanguages, cliInit, printAvailableTemplates } from '../lib import { data, debug, error, print, setVerbose, success } from '../lib/logging'; import { PluginHost } from '../lib/plugin'; import { serializeStructure } from '../lib/serialize'; -import { Configuration, Settings } from '../lib/settings'; +import { Configuration, PROJECT_CONFIG, Settings } from '../lib/settings'; import * as version from '../lib/version'; // tslint:disable:no-shadowed-variable max-line-length @@ -103,6 +104,16 @@ async function initCommandLine() { if (argv.verbose) { setVerbose(); } + + const projectRootDirectory = await lookupProjectRoot(); + + if (projectRootDirectory) { + // We change to the project root directory to maintian the behavior prior to (1.21.0) this feature, where + // cdk commands had to be executed from the project root. + // Note that if we cannot locate the project root, then we proceed anyway because the user might have the "app" configured in some other way. + process.chdir(projectRootDirectory); + } + debug('CDK toolkit version:', version.DISPLAY_VERSION); debug('Command line arguments:', argv); @@ -382,6 +393,31 @@ async function initCommandLine() { } } +async function lookupProjectRoot(): Promise { + + async function ascend(directory: string): Promise { + + const filePath = path.join(directory, PROJECT_CONFIG); + + if (await fs.pathExists(filePath)) { + return directory; + } + + const parentDir = path.dirname(directory); + + if (parentDir === directory) { + // We reached the file system root, give up. + return undefined; + } + + return ascend(parentDir); + + } + + return ascend(path.resolve(process.cwd())); + +} + initCommandLine() .then(value => { if (value == null) { return; } diff --git a/packages/aws-cdk/test/integ/cli/test-cdk-from-inner-directory.sh b/packages/aws-cdk/test/integ/cli/test-cdk-from-inner-directory.sh new file mode 100755 index 0000000000000..2d9305d19e24e --- /dev/null +++ b/packages/aws-cdk/test/integ/cli/test-cdk-from-inner-directory.sh @@ -0,0 +1,12 @@ +#!/bin/bash +set -euo pipefail +scriptdir=$(cd $(dirname $0) && pwd) +source ${scriptdir}/common.bash +# ---------------------------------------------------------- + +setup + +pushd docker +cdk diff ${STACK_NAME_PREFIX}-test-1 + +echo "✅ success" diff --git a/packages/aws-cdk/test/integ/cli/test-cdk-from-outer-directory.sh b/packages/aws-cdk/test/integ/cli/test-cdk-from-outer-directory.sh new file mode 100755 index 0000000000000..cc9bbe934ad22 --- /dev/null +++ b/packages/aws-cdk/test/integ/cli/test-cdk-from-outer-directory.sh @@ -0,0 +1,26 @@ +#!/bin/bash +set -euo pipefail +scriptdir=$(cd $(dirname $0) && pwd) +source ${scriptdir}/common.bash +# ---------------------------------------------------------- + +setup + +temp_dir=$(mktemp -d) + +function cleanup() { + rm -rf ${temp_dir} +} + +trap cleanup EXIT INT + +pushd ${temp_dir} +set +e +output="$(cdk diff ${STACK_NAME_PREFIX}-test-1 2>&1)" +set -e + +if [[ "${output}" != *"--app is required"* ]]; then + fail "unexpected output when running 'cdk diff' from outer directory: ${output}" +fi + +echo "✅ success"