From c1c4ea023f4ee62c4096617aade81a7f0371da62 Mon Sep 17 00:00:00 2001 From: Neta Nir Date: Fri, 8 May 2020 02:18:12 -0700 Subject: [PATCH 01/59] chore: fix CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2585012956fdd..26222487d58a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,7 +23,7 @@ All notable changes to this project will be documented in this file. See [standa ### Bug Fixes -* **assets): fix(assets:** invalid fingerprint when 'exclude' captures root directory name ([#7719](https://github.com/aws/aws-cdk/issues/7719)) ([a5c06a3](https://github.com/aws/aws-cdk/commit/a5c06a3a27b39a5315d0cfd0d34b3c1b25cfc464)), closes [#7718](https://github.com/aws/aws-cdk/issues/7718) +* **assets:** invalid fingerprint when 'exclude' captures root directory name ([#7719](https://github.com/aws/aws-cdk/issues/7719)) ([a5c06a3](https://github.com/aws/aws-cdk/commit/a5c06a3a27b39a5315d0cfd0d34b3c1b25cfc464)), closes [#7718](https://github.com/aws/aws-cdk/issues/7718) * **aws-batch:** gpuCount was ignored in JobDefinition creation ([#7587](https://github.com/aws/aws-cdk/issues/7587)) ([0f1bf23](https://github.com/aws/aws-cdk/commit/0f1bf23817774eb94505a6c68f1daa8a117bbd42)) * **cli:** parameter value reuse is not configurable ([44310c9](https://github.com/aws/aws-cdk/commit/44310c93af939f8aaf9ca4245c944b5c93f61ab7)), closes [#7041](https://github.com/aws/aws-cdk/issues/7041) * **core:** docs refer to "createNamingScheme" which was renamed to "allocateLogicalId" ([#7840](https://github.com/aws/aws-cdk/issues/7840)) ([d79595d](https://github.com/aws/aws-cdk/commit/d79595d854adf160c0a6395a5f535ee270bbdf69)), closes [#7527](https://github.com/aws/aws-cdk/issues/7527) From 7a9f9d3d68c13f675242ec3e71bb0fd3bad555dd Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 8 May 2020 19:39:37 +0000 Subject: [PATCH 02/59] chore(deps): bump aws-sdk from 2.672.0 to 2.673.0 (#7877) Bumps [aws-sdk](https://github.com/aws/aws-sdk-js) from 2.672.0 to 2.673.0. - [Release notes](https://github.com/aws/aws-sdk-js/releases) - [Changelog](https://github.com/aws/aws-sdk-js/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-js/compare/v2.672.0...v2.673.0) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- packages/@aws-cdk/aws-cloudfront/package.json | 2 +- packages/@aws-cdk/aws-cloudtrail/package.json | 2 +- packages/@aws-cdk/aws-codebuild/package.json | 2 +- packages/@aws-cdk/aws-codecommit/package.json | 2 +- packages/@aws-cdk/aws-dynamodb/package.json | 2 +- packages/@aws-cdk/aws-eks/package.json | 2 +- packages/@aws-cdk/aws-events-targets/package.json | 2 +- packages/@aws-cdk/aws-lambda/package.json | 2 +- packages/@aws-cdk/aws-route53/package.json | 2 +- packages/@aws-cdk/aws-sqs/package.json | 2 +- packages/@aws-cdk/custom-resources/package.json | 2 +- packages/aws-cdk/package.json | 2 +- packages/cdk-assets/package.json | 2 +- yarn.lock | 8 ++++---- 14 files changed, 17 insertions(+), 17 deletions(-) diff --git a/packages/@aws-cdk/aws-cloudfront/package.json b/packages/@aws-cdk/aws-cloudfront/package.json index 3df3ce2f1a7c3..eb554afc15080 100644 --- a/packages/@aws-cdk/aws-cloudfront/package.json +++ b/packages/@aws-cdk/aws-cloudfront/package.json @@ -64,7 +64,7 @@ "devDependencies": { "@aws-cdk/assert": "0.0.0", "@types/nodeunit": "^0.0.30", - "aws-sdk": "^2.672.0", + "aws-sdk": "^2.673.0", "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", "cfn2ts": "0.0.0", diff --git a/packages/@aws-cdk/aws-cloudtrail/package.json b/packages/@aws-cdk/aws-cloudtrail/package.json index 18ceeb21c90c2..e4cf33594a1ef 100644 --- a/packages/@aws-cdk/aws-cloudtrail/package.json +++ b/packages/@aws-cdk/aws-cloudtrail/package.json @@ -64,7 +64,7 @@ "devDependencies": { "@aws-cdk/assert": "0.0.0", "@types/nodeunit": "^0.0.30", - "aws-sdk": "^2.672.0", + "aws-sdk": "^2.673.0", "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", "cfn2ts": "0.0.0", diff --git a/packages/@aws-cdk/aws-codebuild/package.json b/packages/@aws-cdk/aws-codebuild/package.json index 23017a58a7430..d3ef09d175bb2 100644 --- a/packages/@aws-cdk/aws-codebuild/package.json +++ b/packages/@aws-cdk/aws-codebuild/package.json @@ -70,7 +70,7 @@ "@aws-cdk/aws-sns": "0.0.0", "@aws-cdk/aws-sqs": "0.0.0", "@types/nodeunit": "^0.0.30", - "aws-sdk": "^2.672.0", + "aws-sdk": "^2.673.0", "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", "cfn2ts": "0.0.0", diff --git a/packages/@aws-cdk/aws-codecommit/package.json b/packages/@aws-cdk/aws-codecommit/package.json index affeceeeca8a9..d739e8d2ea141 100644 --- a/packages/@aws-cdk/aws-codecommit/package.json +++ b/packages/@aws-cdk/aws-codecommit/package.json @@ -70,7 +70,7 @@ "@aws-cdk/assert": "0.0.0", "@aws-cdk/aws-sns": "0.0.0", "@types/nodeunit": "^0.0.30", - "aws-sdk": "^2.672.0", + "aws-sdk": "^2.673.0", "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", "cfn2ts": "0.0.0", diff --git a/packages/@aws-cdk/aws-dynamodb/package.json b/packages/@aws-cdk/aws-dynamodb/package.json index 1f5ab51d07702..71afe8caf78ef 100644 --- a/packages/@aws-cdk/aws-dynamodb/package.json +++ b/packages/@aws-cdk/aws-dynamodb/package.json @@ -64,7 +64,7 @@ "devDependencies": { "@aws-cdk/assert": "0.0.0", "@types/jest": "^25.2.1", - "aws-sdk": "^2.672.0", + "aws-sdk": "^2.673.0", "aws-sdk-mock": "^5.1.0", "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", diff --git a/packages/@aws-cdk/aws-eks/package.json b/packages/@aws-cdk/aws-eks/package.json index d856da674f46b..4a8a9afa8062b 100644 --- a/packages/@aws-cdk/aws-eks/package.json +++ b/packages/@aws-cdk/aws-eks/package.json @@ -64,7 +64,7 @@ "devDependencies": { "@aws-cdk/assert": "0.0.0", "@types/nodeunit": "^0.0.30", - "aws-sdk": "^2.672.0", + "aws-sdk": "^2.673.0", "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", "cfn2ts": "0.0.0", diff --git a/packages/@aws-cdk/aws-events-targets/package.json b/packages/@aws-cdk/aws-events-targets/package.json index fa91e4b0e62b2..736634d702e7e 100644 --- a/packages/@aws-cdk/aws-events-targets/package.json +++ b/packages/@aws-cdk/aws-events-targets/package.json @@ -84,7 +84,7 @@ "devDependencies": { "@aws-cdk/assert": "0.0.0", "@aws-cdk/aws-codecommit": "0.0.0", - "aws-sdk": "^2.672.0", + "aws-sdk": "^2.673.0", "aws-sdk-mock": "^5.1.0", "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", diff --git a/packages/@aws-cdk/aws-lambda/package.json b/packages/@aws-cdk/aws-lambda/package.json index 16bd0c10c7cc4..eb32c84c67335 100644 --- a/packages/@aws-cdk/aws-lambda/package.json +++ b/packages/@aws-cdk/aws-lambda/package.json @@ -71,7 +71,7 @@ "@types/lodash": "^4.14.150", "@types/nodeunit": "^0.0.30", "@types/sinon": "^9.0.0", - "aws-sdk": "^2.672.0", + "aws-sdk": "^2.673.0", "aws-sdk-mock": "^5.1.0", "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", diff --git a/packages/@aws-cdk/aws-route53/package.json b/packages/@aws-cdk/aws-route53/package.json index ec1b1de7f9d38..e5ca0acf7bb7d 100644 --- a/packages/@aws-cdk/aws-route53/package.json +++ b/packages/@aws-cdk/aws-route53/package.json @@ -64,7 +64,7 @@ "devDependencies": { "@aws-cdk/assert": "0.0.0", "@types/nodeunit": "^0.0.30", - "aws-sdk": "^2.672.0", + "aws-sdk": "^2.673.0", "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", "cfn2ts": "0.0.0", diff --git a/packages/@aws-cdk/aws-sqs/package.json b/packages/@aws-cdk/aws-sqs/package.json index 7efc1e85684bd..0d7ce0815307d 100644 --- a/packages/@aws-cdk/aws-sqs/package.json +++ b/packages/@aws-cdk/aws-sqs/package.json @@ -65,7 +65,7 @@ "@aws-cdk/assert": "0.0.0", "@aws-cdk/aws-s3": "0.0.0", "@types/nodeunit": "^0.0.30", - "aws-sdk": "^2.672.0", + "aws-sdk": "^2.673.0", "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", "cfn2ts": "0.0.0", diff --git a/packages/@aws-cdk/custom-resources/package.json b/packages/@aws-cdk/custom-resources/package.json index 785cf8831bb91..53752cb629c08 100644 --- a/packages/@aws-cdk/custom-resources/package.json +++ b/packages/@aws-cdk/custom-resources/package.json @@ -73,7 +73,7 @@ "@types/aws-lambda": "^8.10.39", "@types/fs-extra": "^8.1.0", "@types/sinon": "^9.0.0", - "aws-sdk": "^2.672.0", + "aws-sdk": "^2.673.0", "aws-sdk-mock": "^5.1.0", "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", diff --git a/packages/aws-cdk/package.json b/packages/aws-cdk/package.json index d614cbda6f6a9..be9d796e9d69c 100644 --- a/packages/aws-cdk/package.json +++ b/packages/aws-cdk/package.json @@ -70,7 +70,7 @@ "@aws-cdk/cloud-assembly-schema": "0.0.0", "@aws-cdk/region-info": "0.0.0", "archiver": "^4.0.1", - "aws-sdk": "^2.672.0", + "aws-sdk": "^2.673.0", "camelcase": "^6.0.0", "cdk-assets": "0.0.0", "colors": "^1.4.0", diff --git a/packages/cdk-assets/package.json b/packages/cdk-assets/package.json index 43fb3d0e0e2ad..b50c1bf13490f 100644 --- a/packages/cdk-assets/package.json +++ b/packages/cdk-assets/package.json @@ -44,7 +44,7 @@ "dependencies": { "@aws-cdk/cdk-assets-schema": "0.0.0", "archiver": "^4.0.1", - "aws-sdk": "^2.672.0", + "aws-sdk": "^2.673.0", "glob": "^7.1.6", "yargs": "^15.3.1" }, diff --git a/yarn.lock b/yarn.lock index d178b94631757..250fb9580af66 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2116,10 +2116,10 @@ aws-sdk-mock@^5.1.0: sinon "^9.0.1" traverse "^0.6.6" -aws-sdk@^2.637.0, aws-sdk@^2.672.0: - version "2.672.0" - resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.672.0.tgz#1aa321f40be878efec225f83184a15be6ab753a4" - integrity sha512-ANxBUPTx5KvNX4OJhIkGT6IoNhBPuh63YvAGuf6tV55MsXNNpZXWuPtvIHxst+M5GrQFA2qDpfcuENzC3OzumQ== +aws-sdk@^2.637.0, aws-sdk@^2.673.0: + version "2.673.0" + resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.673.0.tgz#5848900c90cfd8939602019df16fdc38d2a1cdbf" + integrity sha512-OoEPqTLmA5+4uSFf/k4ZLb8cEdx+CwlzovqGf6/gKvb8VrUxe5B5/d2RGlGM777Ke9TmuFhJtTIDugpgc2jo/Q== dependencies: buffer "4.9.1" events "1.1.1" From e47b40377db6c03d035e60aef659c5283ca96d9d Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 8 May 2020 20:53:33 +0000 Subject: [PATCH 03/59] chore(deps-dev): bump conventional-changelog-cli from 2.0.31 to 2.0.34 (#7881) Bumps [conventional-changelog-cli](https://github.com/conventional-changelog/conventional-changelog) from 2.0.31 to 2.0.34. - [Release notes](https://github.com/conventional-changelog/conventional-changelog/releases) - [Commits](https://github.com/conventional-changelog/conventional-changelog/compare/conventional-changelog-cli@2.0.31...conventional-changelog-cli@2.0.34) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 302 ++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 285 insertions(+), 19 deletions(-) diff --git a/package.json b/package.json index 38ceba57ec54e..6ef00495be0b1 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "build-all": "tsc -b" }, "devDependencies": { - "conventional-changelog-cli": "^2.0.31", + "conventional-changelog-cli": "^2.0.34", "fs-extra": "^8.1.0", "jsii-diff": "^1.5.0", "jsii-pacmak": "^1.5.0", diff --git a/yarn.lock b/yarn.lock index 250fb9580af66..b8c21946d7b72 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1527,6 +1527,11 @@ resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== +"@types/minimist@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.0.tgz#69a23a3ad29caf0097f06eda59b361ee2f0639f6" + integrity sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY= + "@types/mock-fs@^4.10.0": version "4.10.0" resolved "https://registry.yarnpkg.com/@types/mock-fs/-/mock-fs-4.10.0.tgz#460061b186993d76856f669d5317cda8a007c24b" @@ -2041,6 +2046,11 @@ arrify@^1.0.1: resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= +arrify@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" + integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== + asap@^2.0.0: version "2.0.6" resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" @@ -2476,6 +2486,15 @@ camelcase-keys@^4.0.0: map-obj "^2.0.0" quick-lru "^1.0.0" +camelcase-keys@^6.2.2: + version "6.2.2" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-6.2.2.tgz#5e755d6ba51aa223ec7d3d52f25778210f9dc3c0" + integrity sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg== + dependencies: + camelcase "^5.3.1" + map-obj "^4.0.0" + quick-lru "^4.0.1" + camelcase@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" @@ -2824,6 +2843,14 @@ contains-path@^0.1.0: resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" integrity sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo= +conventional-changelog-angular@^5.0.10: + version "5.0.10" + resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-5.0.10.tgz#5cf7b00dd315b6a6a558223c80d5ef24ddb34205" + integrity sha512-k7RPPRs0vp8+BtPsM9uDxRl6KcgqtCJmzRD1wRtgqmhQ96g8ifBGo9O/TZBG23jqlXS/rg8BKRDELxfnQQGiaA== + dependencies: + compare-func "^1.3.1" + q "^1.5.1" + conventional-changelog-angular@^5.0.3, conventional-changelog-angular@^5.0.6: version "5.0.6" resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-5.0.6.tgz#269540c624553aded809c29a3508fdc2b544c059" @@ -2839,15 +2866,22 @@ conventional-changelog-atom@^2.0.3: dependencies: q "^1.5.1" -conventional-changelog-cli@^2.0.31: - version "2.0.31" - resolved "https://registry.yarnpkg.com/conventional-changelog-cli/-/conventional-changelog-cli-2.0.31.tgz#3345581170fbb540416946e460fef519a64aef43" - integrity sha512-nMINylKAamBLM3OmD7/44d9TPZ3V58IDTXoGC/QtXxve+1Sj37BQTzIEW3TNaviZ2ZV/b5Dqg0eSk4DNP5fBdA== +conventional-changelog-atom@^2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/conventional-changelog-atom/-/conventional-changelog-atom-2.0.7.tgz#221575253a04f77a2fd273eb2bf29a138f710abf" + integrity sha512-7dOREZwzB+tCEMjRTDfen0OHwd7vPUdmU0llTy1eloZgtOP4iSLVzYIQqfmdRZEty+3w5Jz+AbhfTJKoKw1JeQ== + dependencies: + q "^1.5.1" + +conventional-changelog-cli@^2.0.34: + version "2.0.34" + resolved "https://registry.yarnpkg.com/conventional-changelog-cli/-/conventional-changelog-cli-2.0.34.tgz#3d9da6011aaaf24f331b606ddc5087a6b811464b" + integrity sha512-HDDIhhpsMKiiAfH/mbj7wApgN7uA33Nk4hISY3/7ijlfqXc/bmP3v4o3Yialoxz0iTBibc94xi6kfTH7XIvwDw== dependencies: add-stream "^1.0.0" - conventional-changelog "^3.1.18" + conventional-changelog "^3.1.21" lodash "^4.17.15" - meow "^5.0.0" + meow "^7.0.0" tempfile "^3.0.0" conventional-changelog-codemirror@^2.0.3: @@ -2857,6 +2891,13 @@ conventional-changelog-codemirror@^2.0.3: dependencies: q "^1.5.1" +conventional-changelog-codemirror@^2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/conventional-changelog-codemirror/-/conventional-changelog-codemirror-2.0.7.tgz#d6b6a8ce2707710c5a036e305037547fb9e15bfb" + integrity sha512-Oralk1kiagn3Gb5cR5BffenWjVu59t/viE6UMD/mQa1hISMPkMYhJIqX+CMeA1zXgVBO+YHQhhokEj99GP5xcg== + dependencies: + q "^1.5.1" + conventional-changelog-config-spec@2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/conventional-changelog-config-spec/-/conventional-changelog-config-spec-2.1.0.tgz#874a635287ef8b581fd8558532bf655d4fb59f2d" @@ -2871,6 +2912,15 @@ conventional-changelog-conventionalcommits@4.2.3, conventional-changelog-convent lodash "^4.17.15" q "^1.5.1" +conventional-changelog-conventionalcommits@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-4.3.0.tgz#c4205a659f7ca9d7881f29ee78a4e7d6aeb8b3c2" + integrity sha512-oYHydvZKU+bS8LnGqTMlNrrd7769EsuEHKy4fh1oMdvvDi7fem8U+nvfresJ1IDB8K00Mn4LpiA/lR+7Gs6rgg== + dependencies: + compare-func "^1.3.1" + lodash "^4.17.15" + q "^1.5.1" + conventional-changelog-core@^3.1.6: version "3.2.3" resolved "https://registry.yarnpkg.com/conventional-changelog-core/-/conventional-changelog-core-3.2.3.tgz#b31410856f431c847086a7dcb4d2ca184a7d88fb" @@ -2910,6 +2960,27 @@ conventional-changelog-core@^4.1.4: read-pkg-up "^3.0.0" through2 "^3.0.0" +conventional-changelog-core@^4.1.7: + version "4.1.7" + resolved "https://registry.yarnpkg.com/conventional-changelog-core/-/conventional-changelog-core-4.1.7.tgz#6b5cdadda4430895cc4a75a73dd8b36e322ab346" + integrity sha512-UBvSrQR2RdKbSQKh7RhueiiY4ZAIOW3+CSWdtKOwRv+KxIMNFKm1rOcGBFx0eA8AKhGkkmmacoTWJTqyz7Q0VA== + dependencies: + add-stream "^1.0.0" + conventional-changelog-writer "^4.0.16" + conventional-commits-parser "^3.1.0" + dateformat "^3.0.0" + get-pkg-repo "^1.0.0" + git-raw-commits "2.0.0" + git-remote-origin-url "^2.0.0" + git-semver-tags "^4.0.0" + lodash "^4.17.15" + normalize-package-data "^2.3.5" + q "^1.5.1" + read-pkg "^3.0.0" + read-pkg-up "^3.0.0" + shelljs "^0.8.3" + through2 "^3.0.0" + conventional-changelog-ember@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/conventional-changelog-ember/-/conventional-changelog-ember-2.0.4.tgz#c29b78e4af7825cbecb6c3fd6086ca5c09471ac1" @@ -2917,6 +2988,13 @@ conventional-changelog-ember@^2.0.4: dependencies: q "^1.5.1" +conventional-changelog-ember@^2.0.8: + version "2.0.8" + resolved "https://registry.yarnpkg.com/conventional-changelog-ember/-/conventional-changelog-ember-2.0.8.tgz#f0f04eb7ff3c885af97db100865ab95dcfa9917f" + integrity sha512-JEMEcUAMg4Q9yxD341OgWlESQ4gLqMWMXIWWUqoQU8yvTJlKnrvcui3wk9JvnZQyONwM2g1MKRZuAjKxr8hAXA== + dependencies: + q "^1.5.1" + conventional-changelog-eslint@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/conventional-changelog-eslint/-/conventional-changelog-eslint-3.0.4.tgz#8f4736a23e0cd97e890e76fccc287db2f205f2ff" @@ -2924,6 +3002,13 @@ conventional-changelog-eslint@^3.0.4: dependencies: q "^1.5.1" +conventional-changelog-eslint@^3.0.8: + version "3.0.8" + resolved "https://registry.yarnpkg.com/conventional-changelog-eslint/-/conventional-changelog-eslint-3.0.8.tgz#f8b952b7ed7253ea0ac0b30720bb381f4921b46c" + integrity sha512-5rTRltgWG7TpU1PqgKHMA/2ivjhrB+E+S7OCTvj0zM/QGg4vmnVH67Vq/EzvSNYtejhWC+OwzvDrLk3tqPry8A== + dependencies: + q "^1.5.1" + conventional-changelog-express@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/conventional-changelog-express/-/conventional-changelog-express-2.0.1.tgz#fea2231d99a5381b4e6badb0c1c40a41fcacb755" @@ -2931,6 +3016,20 @@ conventional-changelog-express@^2.0.1: dependencies: q "^1.5.1" +conventional-changelog-express@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/conventional-changelog-express/-/conventional-changelog-express-2.0.5.tgz#6e93705acdad374516ca125990012a48e710f8de" + integrity sha512-pW2hsjKG+xNx/Qjof8wYlAX/P61hT5gQ/2rZ2NsTpG+PgV7Rc8RCfITvC/zN9K8fj0QmV6dWmUefCteD9baEAw== + dependencies: + q "^1.5.1" + +conventional-changelog-jquery@^3.0.10: + version "3.0.10" + resolved "https://registry.yarnpkg.com/conventional-changelog-jquery/-/conventional-changelog-jquery-3.0.10.tgz#fe8eb6aff322aa980af5eb68497622a5f6257ce7" + integrity sha512-QCW6wF8QgPkq2ruPaxc83jZxoWQxLkt/pNxIDn/oYjMiVgrtqNdd7lWe3vsl0hw5ENHNf/ejXuzDHk6suKsRpg== + dependencies: + q "^1.5.1" + conventional-changelog-jquery@^3.0.6: version "3.0.6" resolved "https://registry.yarnpkg.com/conventional-changelog-jquery/-/conventional-changelog-jquery-3.0.6.tgz#460236ad8fb1d29ff932a14fe4e3a45379b63c5e" @@ -2946,11 +3045,24 @@ conventional-changelog-jshint@^2.0.3: compare-func "^1.3.1" q "^1.5.1" +conventional-changelog-jshint@^2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/conventional-changelog-jshint/-/conventional-changelog-jshint-2.0.7.tgz#955a69266951cd31e8afeb3f1c55e0517fdca943" + integrity sha512-qHA8rmwUnLiIxANJbz650+NVzqDIwNtc0TcpIa0+uekbmKHttidvQ1dGximU3vEDdoJVKFgR3TXFqYuZmYy9ZQ== + dependencies: + compare-func "^1.3.1" + q "^1.5.1" + conventional-changelog-preset-loader@^2.1.1, conventional-changelog-preset-loader@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.3.0.tgz#580fa8ab02cef22c24294d25e52d7ccd247a9a6a" integrity sha512-/rHb32J2EJnEXeK4NpDgMaAVTFZS3o1ExmjKMtYVgIC4MQn0vkNSbYpdGRotkfGGRWiqk3Ri3FBkiZGbAfIfOQ== +conventional-changelog-preset-loader@^2.3.4: + version "2.3.4" + resolved "https://registry.yarnpkg.com/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.3.4.tgz#14a855abbffd59027fd602581f1f34d9862ea44c" + integrity sha512-GEKRWkrSAZeTq5+YjUZOYxdHq+ci4dNwHvpaBC3+ENalzFWuCWa9EZXSuZBpkr72sMdKB+1fyDV4takK1Lf58g== + conventional-changelog-writer@^4.0.11, conventional-changelog-writer@^4.0.6: version "4.0.11" resolved "https://registry.yarnpkg.com/conventional-changelog-writer/-/conventional-changelog-writer-4.0.11.tgz#9f56d2122d20c96eb48baae0bf1deffaed1edba4" @@ -2967,7 +3079,23 @@ conventional-changelog-writer@^4.0.11, conventional-changelog-writer@^4.0.6: split "^1.0.0" through2 "^3.0.0" -conventional-changelog@3.1.18, conventional-changelog@^3.1.18: +conventional-changelog-writer@^4.0.16: + version "4.0.16" + resolved "https://registry.yarnpkg.com/conventional-changelog-writer/-/conventional-changelog-writer-4.0.16.tgz#ca10f2691a8ea6d3c2eb74bd35bcf40aa052dda5" + integrity sha512-jmU1sDJDZpm/dkuFxBeRXvyNcJQeKhGtVcFFkwTphUAzyYWcwz2j36Wcv+Mv2hU3tpvLMkysOPXJTLO55AUrYQ== + dependencies: + compare-func "^1.3.1" + conventional-commits-filter "^2.0.6" + dateformat "^3.0.0" + handlebars "^4.7.6" + json-stringify-safe "^5.0.1" + lodash "^4.17.15" + meow "^7.0.0" + semver "^6.0.0" + split "^1.0.0" + through2 "^3.0.0" + +conventional-changelog@3.1.18: version "3.1.18" resolved "https://registry.yarnpkg.com/conventional-changelog/-/conventional-changelog-3.1.18.tgz#7da0a5ab34a604b920b8bf71c6cf5d952f0e805e" integrity sha512-aN6a3rjgV8qwAJj3sC/Lme2kvswWO7fFSGQc32gREcwIOsaiqBaO6f2p0NomFaPDnTqZ+mMZFLL3hlzvEnZ0mQ== @@ -2984,6 +3112,23 @@ conventional-changelog@3.1.18, conventional-changelog@^3.1.18: conventional-changelog-jshint "^2.0.3" conventional-changelog-preset-loader "^2.3.0" +conventional-changelog@^3.1.21: + version "3.1.21" + resolved "https://registry.yarnpkg.com/conventional-changelog/-/conventional-changelog-3.1.21.tgz#4a774e6bf503acfd7e4685bb750da8c0eccf1e0d" + integrity sha512-ZGecVZPEo3aC75VVE4nu85589dDhpMyqfqgUM5Myq6wfKWiNqhDJLSDMsc8qKXshZoY7dqs1hR0H/15kI/G2jQ== + dependencies: + conventional-changelog-angular "^5.0.10" + conventional-changelog-atom "^2.0.7" + conventional-changelog-codemirror "^2.0.7" + conventional-changelog-conventionalcommits "^4.3.0" + conventional-changelog-core "^4.1.7" + conventional-changelog-ember "^2.0.8" + conventional-changelog-eslint "^3.0.8" + conventional-changelog-express "^2.0.5" + conventional-changelog-jquery "^3.0.10" + conventional-changelog-jshint "^2.0.7" + conventional-changelog-preset-loader "^2.3.4" + conventional-commits-filter@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/conventional-commits-filter/-/conventional-commits-filter-2.0.2.tgz#f122f89fbcd5bb81e2af2fcac0254d062d1039c1" @@ -2992,6 +3137,14 @@ conventional-commits-filter@^2.0.2: lodash.ismatch "^4.4.0" modify-values "^1.0.0" +conventional-commits-filter@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/conventional-commits-filter/-/conventional-commits-filter-2.0.6.tgz#0935e1240c5ca7698329affee1b6a46d33324c4c" + integrity sha512-4g+sw8+KA50/Qwzfr0hL5k5NWxqtrOVw4DDk3/h6L85a9Gz0/Eqp3oP+CWCNfesBvZZZEFHF7OTEbRe+yYSyKw== + dependencies: + lodash.ismatch "^4.4.0" + modify-values "^1.0.0" + conventional-commits-parser@^3.0.3, conventional-commits-parser@^3.0.8: version "3.0.8" resolved "https://registry.yarnpkg.com/conventional-commits-parser/-/conventional-commits-parser-3.0.8.tgz#23310a9bda6c93c874224375e72b09fb275fe710" @@ -3005,6 +3158,19 @@ conventional-commits-parser@^3.0.3, conventional-commits-parser@^3.0.8: through2 "^3.0.0" trim-off-newlines "^1.0.0" +conventional-commits-parser@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/conventional-commits-parser/-/conventional-commits-parser-3.1.0.tgz#10140673d5e7ef5572633791456c5d03b69e8be4" + integrity sha512-RSo5S0WIwXZiRxUGTPuYFbqvrR4vpJ1BDdTlthFgvHt5kEdnd1+pdvwWphWn57/oIl4V72NMmOocFqqJ8mFFhA== + dependencies: + JSONStream "^1.0.4" + is-text-path "^1.0.1" + lodash "^4.17.15" + meow "^7.0.0" + split2 "^2.0.0" + through2 "^3.0.0" + trim-off-newlines "^1.0.0" + conventional-recommended-bump@6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/conventional-recommended-bump/-/conventional-recommended-bump-6.0.5.tgz#be7ec24b43bef57108042ea1d49758b58beabc03" @@ -3247,7 +3413,7 @@ debuglog@^1.0.1: resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI= -decamelize-keys@^1.0.0: +decamelize-keys@^1.0.0, decamelize-keys@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9" integrity sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk= @@ -4432,6 +4598,14 @@ git-semver-tags@^2.0.3: meow "^4.0.0" semver "^6.0.0" +git-semver-tags@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/git-semver-tags/-/git-semver-tags-4.0.0.tgz#a9dd58a0dd3561a4a9898b7e9731cf441c98fc38" + integrity sha512-LajaAWLYVBff+1NVircURJFL8TQ3EMIcLAfHisWYX/nPoMwnTYfWAznQDmMujlLqoD12VtLmoSrF1sQ5MhimEQ== + dependencies: + meow "^7.0.0" + semver "^6.0.0" + git-up@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/git-up/-/git-up-4.0.1.tgz#cb2ef086653640e721d2042fe3104857d89007c0" @@ -4484,7 +4658,7 @@ glob-to-regexp@^0.3.0: resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs= -glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@~7.1.4: +glob@^7.0.0, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@~7.1.4: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== @@ -4547,7 +4721,7 @@ growly@^1.3.0: resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE= -handlebars@^4.4.0: +handlebars@^4.4.0, handlebars@^4.7.6: version "4.7.6" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.6.tgz#d4c05c1baf90e9945f77aa68a7a219aa4a7df74e" integrity sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA== @@ -4572,6 +4746,11 @@ har-validator@~5.1.3: ajv "^6.5.5" har-schema "^2.0.0" +hard-rejection@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" + integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA== + has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" @@ -4891,6 +5070,11 @@ inquirer@^7.0.0: strip-ansi "^6.0.0" through "^2.3.6" +interpret@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.2.0.tgz#d5061a6224be58e8083985f5014d844359576296" + integrity sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw== + invert-kv@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" @@ -6362,6 +6546,11 @@ map-obj@^2.0.0: resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-2.0.0.tgz#a65cd29087a92598b8791257a523e021222ac1f9" integrity sha1-plzSkIepJZi4eRJXpSPgISIqwfk= +map-obj@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.1.0.tgz#b91221b542734b9f14256c0132c897c5d7256fd5" + integrity sha512-glc9y00wgtwcDmp7GaE/0b0OnxpNJsVf3ael/An6Fe2Q51LLwN1er6sdomLRzz5h0+yMpiYLhWYF5R7HeqVd4g== + map-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" @@ -6429,6 +6618,24 @@ meow@^5.0.0: trim-newlines "^2.0.0" yargs-parser "^10.0.0" +meow@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/meow/-/meow-7.0.0.tgz#2d21adc6df0e1b4659b2b2ce63852d954e5c440a" + integrity sha512-He6nRo6zYQtzdm0rUKRjpc+V2uvfUnz76i2zxosiLrAvKhk9dSRqWabL/3fNZv9hpb3PQIJNym0M0pzPZa0pvw== + dependencies: + "@types/minimist" "^1.2.0" + arrify "^2.0.1" + camelcase-keys "^6.2.2" + decamelize-keys "^1.1.0" + hard-rejection "^2.1.0" + minimist-options "^4.0.2" + normalize-package-data "^2.5.0" + read-pkg-up "^7.0.1" + redent "^3.0.0" + trim-newlines "^3.0.0" + type-fest "^0.13.1" + yargs-parser "^18.1.3" + merge-descriptors@~1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" @@ -6500,6 +6707,11 @@ mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== +min-indent@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.0.tgz#cfc45c37e9ec0d8f0a0ec3dd4ef7f7c3abe39256" + integrity sha1-z8RcN+nsDY8KDsPdTvf3w6vjklY= + minimatch@>=3.0, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" @@ -6515,6 +6727,14 @@ minimist-options@^3.0.1: arrify "^1.0.1" is-plain-obj "^1.1.0" +minimist-options@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.0.2.tgz#29c4021373ded40d546186725e57761e4b1984a7" + integrity sha512-seq4hpWkYSUh1y7NXxzucwAN9yVlBc3Upgdjz8vLCP97jG8kaOmzYrVH/m7tQ1NYD1wdtZbSLfdy4zFmRWuc/w== + dependencies: + arrify "^1.0.1" + is-plain-obj "^1.1.0" + minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5, minimist@~1.2.0: version "1.2.5" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" @@ -7721,6 +7941,11 @@ quick-lru@^1.0.0: resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-1.1.0.tgz#4360b17c61136ad38078397ff11416e186dcfbb8" integrity sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g= +quick-lru@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" + integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== + raw-body@^2.2.0: version "2.4.1" resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.1.tgz#30ac82f98bb5ae8c152e67149dac8d55153b168c" @@ -7896,6 +8121,13 @@ realpath-native@^2.0.0: resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-2.0.0.tgz#7377ac429b6e1fd599dc38d08ed942d0d7beb866" integrity sha512-v1SEYUOXXdbBZK8ZuNgO4TBjamPsiSgcFr0aP+tEKpQZK8vooEUqV6nm6Cv502mX4NF2EfsnVqtNAHG+/6Ur1Q== +rechoir@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" + integrity sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q= + dependencies: + resolve "^1.1.6" + redent@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" @@ -7912,6 +8144,14 @@ redent@^2.0.0: indent-string "^3.0.0" strip-indent "^2.0.0" +redent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" + integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg== + dependencies: + indent-string "^4.0.0" + strip-indent "^3.0.0" + regenerator-runtime@^0.13.4: version "0.13.5" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697" @@ -8068,6 +8308,13 @@ resolve@1.1.7: resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= +resolve@^1.1.6, resolve@^1.17.0: + version "1.17.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" + integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== + dependencies: + path-parse "^1.0.6" + resolve@^1.10.0, resolve@^1.11.1, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.3.2: version "1.16.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.16.1.tgz#49fac5d8bacf1fd53f200fa51247ae736175832c" @@ -8075,13 +8322,6 @@ resolve@^1.10.0, resolve@^1.11.1, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.3 dependencies: path-parse "^1.0.6" -resolve@^1.17.0: - version "1.17.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" - integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== - dependencies: - path-parse "^1.0.6" - restore-cursor@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" @@ -8297,6 +8537,15 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== +shelljs@^0.8.3: + version "0.8.4" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.4.tgz#de7684feeb767f8716b326078a8a00875890e3c2" + integrity sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ== + dependencies: + glob "^7.0.0" + interpret "^1.0.0" + rechoir "^0.6.2" + shellwords@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" @@ -8805,6 +9054,13 @@ strip-indent@^2.0.0: resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68" integrity sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g= +strip-indent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" + integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== + dependencies: + min-indent "^1.0.0" + strip-json-comments@^3.0.1: version "3.1.0" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.0.tgz#7638d31422129ecf4457440009fba03f9f9ac180" @@ -9183,6 +9439,11 @@ trim-newlines@^2.0.0: resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-2.0.0.tgz#b403d0b91be50c331dfc4b82eeceb22c3de16d20" integrity sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA= +trim-newlines@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.0.tgz#79726304a6a898aa8373427298d54c2ee8b1cb30" + integrity sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA== + trim-off-newlines@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz#9f9ba9d9efa8764c387698bcbfeb2c848f11adb3" @@ -9307,6 +9568,11 @@ type-fest@^0.11.0: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== +type-fest@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934" + integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg== + type-fest@^0.3.0: version "0.3.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.3.1.tgz#63d00d204e059474fe5e1b7c011112bbd1dc29e1" @@ -9852,7 +10118,7 @@ yapool@^1.0.0: resolved "https://registry.yarnpkg.com/yapool/-/yapool-1.0.0.tgz#f693f29a315b50d9a9da2646a7a6645c96985b6a" integrity sha1-9pPymjFbUNmp2iZGp6ZkXJaYW2o= -yargs-parser@18.x, yargs-parser@^18.1.1: +yargs-parser@18.x, yargs-parser@^18.1.1, yargs-parser@^18.1.3: version "18.1.3" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== From 685a4bf76e65fa2fdcc2af6dcb5c539b0137386b Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 9 May 2020 09:53:19 +0000 Subject: [PATCH 04/59] chore(deps): bump ts-jest from 25.5.0 to 25.5.1 (#7888) Bumps [ts-jest](https://github.com/kulshekhar/ts-jest) from 25.5.0 to 25.5.1. - [Release notes](https://github.com/kulshekhar/ts-jest/releases) - [Changelog](https://github.com/kulshekhar/ts-jest/blob/master/CHANGELOG.md) - [Commits](https://github.com/kulshekhar/ts-jest/compare/v25.5.0...v25.5.1) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- packages/@aws-cdk/assert/package.json | 2 +- packages/@aws-cdk/aws-dynamodb/package.json | 2 +- packages/@aws-cdk/aws-sam/package.json | 2 +- packages/@aws-cdk/cloudformation-diff/package.json | 2 +- packages/@monocdk-experiment/assert/package.json | 2 +- packages/aws-cdk/package.json | 2 +- tools/cdk-build-tools/package.json | 2 +- yarn.lock | 8 ++++---- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/@aws-cdk/assert/package.json b/packages/@aws-cdk/assert/package.json index 2bad5d64de9cb..8c9139acdf97b 100644 --- a/packages/@aws-cdk/assert/package.json +++ b/packages/@aws-cdk/assert/package.json @@ -33,7 +33,7 @@ "cdk-build-tools": "0.0.0", "jest": "^25.5.4", "pkglint": "0.0.0", - "ts-jest": "^25.5.0" + "ts-jest": "^25.5.1" }, "dependencies": { "@aws-cdk/cloudformation-diff": "0.0.0", diff --git a/packages/@aws-cdk/aws-dynamodb/package.json b/packages/@aws-cdk/aws-dynamodb/package.json index 71afe8caf78ef..48cd26999ceab 100644 --- a/packages/@aws-cdk/aws-dynamodb/package.json +++ b/packages/@aws-cdk/aws-dynamodb/package.json @@ -72,7 +72,7 @@ "jest": "^25.5.4", "pkglint": "0.0.0", "sinon": "^9.0.2", - "ts-jest": "^25.5.0" + "ts-jest": "^25.5.1" }, "dependencies": { "@aws-cdk/aws-applicationautoscaling": "0.0.0", diff --git a/packages/@aws-cdk/aws-sam/package.json b/packages/@aws-cdk/aws-sam/package.json index 3190eb0afba88..4fb1d0a628f36 100644 --- a/packages/@aws-cdk/aws-sam/package.json +++ b/packages/@aws-cdk/aws-sam/package.json @@ -69,7 +69,7 @@ "cfn2ts": "0.0.0", "jest": "^25.5.4", "pkglint": "0.0.0", - "ts-jest": "^25.5.0" + "ts-jest": "^25.5.1" }, "dependencies": { "@aws-cdk/core": "0.0.0", diff --git a/packages/@aws-cdk/cloudformation-diff/package.json b/packages/@aws-cdk/cloudformation-diff/package.json index 6b9c9cf2a39fe..988c4bdc82a63 100644 --- a/packages/@aws-cdk/cloudformation-diff/package.json +++ b/packages/@aws-cdk/cloudformation-diff/package.json @@ -45,7 +45,7 @@ "fast-check": "^1.24.2", "jest": "^25.5.4", "pkglint": "0.0.0", - "ts-jest": "^25.5.0" + "ts-jest": "^25.5.1" }, "repository": { "url": "https://github.com/aws/aws-cdk.git", diff --git a/packages/@monocdk-experiment/assert/package.json b/packages/@monocdk-experiment/assert/package.json index 7b4ef8901ac8e..85868034ef5a4 100644 --- a/packages/@monocdk-experiment/assert/package.json +++ b/packages/@monocdk-experiment/assert/package.json @@ -47,7 +47,7 @@ "cdk-build-tools": "0.0.0", "jest": "^25.5.4", "pkglint": "0.0.0", - "ts-jest": "^25.5.0", + "ts-jest": "^25.5.1", "@monocdk-experiment/rewrite-imports": "0.0.0", "monocdk-experiment": "0.0.0", "constructs": "^3.0.2" diff --git a/packages/aws-cdk/package.json b/packages/aws-cdk/package.json index be9d796e9d69c..3111428f74fab 100644 --- a/packages/aws-cdk/package.json +++ b/packages/aws-cdk/package.json @@ -60,7 +60,7 @@ "mockery": "^2.1.0", "pkglint": "0.0.0", "sinon": "^9.0.2", - "ts-jest": "^25.5.0", + "ts-jest": "^25.5.1", "ts-mock-imports": "^1.2.6" }, "dependencies": { diff --git a/tools/cdk-build-tools/package.json b/tools/cdk-build-tools/package.json index 6cb490e10ca70..22c22d0d71efc 100644 --- a/tools/cdk-build-tools/package.json +++ b/tools/cdk-build-tools/package.json @@ -53,7 +53,7 @@ "jsii-pacmak": "^1.5.0", "nodeunit": "^0.11.3", "nyc": "^15.0.1", - "ts-jest": "^25.5.0", + "ts-jest": "^25.5.1", "tslint": "^5.20.1", "typescript": "~3.8.3", "yargs": "^15.3.1", diff --git a/yarn.lock b/yarn.lock index b8c21946d7b72..92add188a545c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9454,10 +9454,10 @@ trivial-deferred@^1.0.1: resolved "https://registry.yarnpkg.com/trivial-deferred/-/trivial-deferred-1.0.1.tgz#376d4d29d951d6368a6f7a0ae85c2f4d5e0658f3" integrity sha1-N21NKdlR1jaKb3oK6FwvTV4GWPM= -ts-jest@^25.5.0: - version "25.5.0" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-25.5.0.tgz#f56f039b6caa15d9e32d064b0a8902886ab2fe72" - integrity sha512-govrjbOk1UEzcJ5cX5k8X8IUtFuP3lp3mrF3ZuKtCdAOQzdeCM7qualhb/U8s8SWFwEDutOqfF5PLkJ+oaYD4w== +ts-jest@^25.5.1: + version "25.5.1" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-25.5.1.tgz#2913afd08f28385d54f2f4e828be4d261f4337c7" + integrity sha512-kHEUlZMK8fn8vkxDjwbHlxXRB9dHYpyzqKIGDNxbzs+Rz+ssNDSDNusEK8Fk/sDd4xE6iKoQLfFkFVaskmTJyw== dependencies: bs-logger "0.x" buffer-from "1.x" From d8eec54071c1ab0455c0567a511cae943c50a107 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Mon, 11 May 2020 12:00:53 +0200 Subject: [PATCH 05/59] chore: CDK apps generate asset manifest Introducing new `StackSynthesizer` objects which can be attached to `Stacks`. StackSynthesizer controls how a stack synthesizes itself to a Cloud Assembly, and specifically how it treats assets. The most important Stack Synthesizers in this change are: * `LegacyStackSynthesizer`, the "old" stack synthesis behavior, synthesizing assets to parameters. * `DefaultStackSynthesizer`, new behavior which goes hand-in-hand with bootstrapping v2, which synthesizes well-known locations and an asset manifest for assets. The feature flag `@aws-cdk/core:newStyleStackSynthesis` controls whether an unconfigured stack gets the new or old synthesis object. ALSO IN THIS PR * Placeholder replacements for account, region and partition are used both by the CLI and the `cdk-assets` tool. Move the functionality for both into `cx-api` so as to not have to duplicate it. * Artifact properties were an untyped property bag which should have gone into the Cloud Assembly Schema. Move them there. * Split out the `schema.ts` file into smaller files to prevent it from getting too big. --- allowed-breaking-changes.txt | 6 +- .../aws-ecr-assets/lib/image-asset.ts | 2 +- packages/@aws-cdk/aws-lambda/lib/code.ts | 3 +- packages/@aws-cdk/aws-s3-assets/lib/asset.ts | 2 +- .../cdk-assets-schema/lib/aws-destination.ts | 20 - .../lib/artifact-schema.ts | 90 +++++ .../cloud-assembly-schema/lib/index.ts | 2 + .../cloud-assembly-schema/lib/manifest.ts | 3 +- .../lib/metadata-schema.ts | 215 +++++++++++ .../cloud-assembly-schema/lib/schema.ts | 219 +---------- .../schema/cloud-assembly.schema.json | 89 ++++- .../test/manifest.test.ts | 2 +- packages/@aws-cdk/core/lib/index.ts | 1 + packages/@aws-cdk/core/lib/nested-stack.ts | 6 +- .../core/lib/stack-synthesizers/_shared.ts | 118 ++++++ .../stack-synthesizers/default-synthesizer.ts | 341 ++++++++++++++++++ .../core/lib/stack-synthesizers/index.ts | 4 + .../core/lib/stack-synthesizers/legacy.ts | 177 +++++++++ .../core/lib/stack-synthesizers/nested.ts | 35 ++ .../core/lib/stack-synthesizers/types.ts | 36 ++ packages/@aws-cdk/core/lib/stack.ts | 225 +++--------- packages/@aws-cdk/core/package.json | 7 +- packages/@aws-cdk/core/test/evaluate-cfn.ts | 10 + .../test.new-style-synthesis.ts | 104 ++++++ packages/@aws-cdk/core/test/test.app.ts | 1 + .../@aws-cdk/core/test/test.runtime-info.ts | 1 + .../cx-api/lib/asset-manifest-artifact.ts | 30 ++ .../@aws-cdk/cx-api/lib/cloud-artifact.ts | 10 +- .../cx-api/lib/cloudformation-artifact.ts | 38 +- packages/@aws-cdk/cx-api/lib/features.ts | 8 + packages/@aws-cdk/cx-api/lib/index.ts | 2 + packages/@aws-cdk/cx-api/lib/placeholders.ts | 123 +++++++ .../cx-api/lib/tree-cloud-artifact.ts | 2 +- .../cx-api/test/cloud-assembly.test.ts | 8 +- .../fixtures/asset-manifest/asset-dir/foo.txt | 1 + .../test/fixtures/asset-manifest/assets.json | 3 + .../asset-manifest/docker-asset/Dockerfile | 1 + .../fixtures/asset-manifest/manifest.json | 21 ++ .../fixtures/asset-manifest/template.json | 7 + .../@aws-cdk/cx-api/test/placeholders.test.ts | 27 ++ .../lib/api/cloudformation-deployments.ts | 91 ++++- packages/aws-cdk/lib/api/deploy-stack.ts | 9 +- packages/aws-cdk/lib/api/toolkit-info.ts | 8 +- packages/aws-cdk/lib/cdk-toolkit.ts | 1 + .../api/cloudformation-deployments.test.ts | 79 ++++ .../aws-cdk/test/api/deploy-stack.test.ts | 31 +- packages/aws-cdk/test/util.ts | 9 +- packages/aws-cdk/test/util/mock-sdk.ts | 13 +- .../lib/private/handlers/container-images.ts | 4 +- .../cdk-assets/lib/private/placeholders.ts | 67 +--- packages/cdk-assets/package.json | 1 + packages/cdk-assets/test/mock-aws.ts | 4 +- 52 files changed, 1806 insertions(+), 511 deletions(-) create mode 100644 packages/@aws-cdk/cloud-assembly-schema/lib/artifact-schema.ts create mode 100644 packages/@aws-cdk/cloud-assembly-schema/lib/metadata-schema.ts create mode 100644 packages/@aws-cdk/core/lib/stack-synthesizers/_shared.ts create mode 100644 packages/@aws-cdk/core/lib/stack-synthesizers/default-synthesizer.ts create mode 100644 packages/@aws-cdk/core/lib/stack-synthesizers/index.ts create mode 100644 packages/@aws-cdk/core/lib/stack-synthesizers/legacy.ts create mode 100644 packages/@aws-cdk/core/lib/stack-synthesizers/nested.ts create mode 100644 packages/@aws-cdk/core/lib/stack-synthesizers/types.ts create mode 100644 packages/@aws-cdk/core/test/stack-synthesis/test.new-style-synthesis.ts create mode 100644 packages/@aws-cdk/cx-api/lib/asset-manifest-artifact.ts create mode 100644 packages/@aws-cdk/cx-api/lib/placeholders.ts create mode 100644 packages/@aws-cdk/cx-api/test/fixtures/asset-manifest/asset-dir/foo.txt create mode 100644 packages/@aws-cdk/cx-api/test/fixtures/asset-manifest/assets.json create mode 100644 packages/@aws-cdk/cx-api/test/fixtures/asset-manifest/docker-asset/Dockerfile create mode 100644 packages/@aws-cdk/cx-api/test/fixtures/asset-manifest/manifest.json create mode 100644 packages/@aws-cdk/cx-api/test/fixtures/asset-manifest/template.json create mode 100644 packages/@aws-cdk/cx-api/test/placeholders.test.ts create mode 100644 packages/aws-cdk/test/api/cloudformation-deployments.test.ts diff --git a/allowed-breaking-changes.txt b/allowed-breaking-changes.txt index 8a8b54410795f..0e03d2ce09560 100644 --- a/allowed-breaking-changes.txt +++ b/allowed-breaking-changes.txt @@ -49,6 +49,8 @@ incompatible-argument:@aws-cdk/aws-iam.PrincipalPolicyFragment. changed-type:@aws-cdk/aws-iam.FederatedPrincipal.conditions changed-type:@aws-cdk/aws-iam.PrincipalPolicyFragment.conditions changed-type:@aws-cdk/aws-iam.PrincipalWithConditions.conditions -# Changing untyped property blob into typed property blob -change-return-type:@aws-cdk/cloud-assembly-schema.Manifest.load +removed:@aws-cdk/cdk-assets-schema.Placeholders +# Following two are because we're turning: properties: {string=>any} into a union of typed interfaces +# Needs to be removed after next release. incompatible-argument:@aws-cdk/cloud-assembly-schema.Manifest.save +change-return-type:@aws-cdk/cloud-assembly-schema.Manifest.load diff --git a/packages/@aws-cdk/aws-ecr-assets/lib/image-asset.ts b/packages/@aws-cdk/aws-ecr-assets/lib/image-asset.ts index 6e84d63dad8d6..09c4a033a8476 100644 --- a/packages/@aws-cdk/aws-ecr-assets/lib/image-asset.ts +++ b/packages/@aws-cdk/aws-ecr-assets/lib/image-asset.ts @@ -140,7 +140,7 @@ export class DockerImageAsset extends Construct implements assets.IAsset { this.sourceHash = staging.sourceHash; const stack = Stack.of(this); - const location = stack.addDockerImageAsset({ + const location = stack.synthesizer.addDockerImageAsset({ directoryName: staging.stagedPath, dockerBuildArgs: props.buildArgs, dockerBuildTarget: props.target, diff --git a/packages/@aws-cdk/aws-lambda/lib/code.ts b/packages/@aws-cdk/aws-lambda/lib/code.ts index f9df534834195..263e67f415bd4 100644 --- a/packages/@aws-cdk/aws-lambda/lib/code.ts +++ b/packages/@aws-cdk/aws-lambda/lib/code.ts @@ -36,7 +36,8 @@ export abstract class Code { } /** - * Loads the function code from a local disk asset. + * Loads the function code from a local disk path. + * * @param path Either a directory with the Lambda code bundle or a .zip file */ public static fromAsset(path: string, options?: s3_assets.AssetOptions): AssetCode { diff --git a/packages/@aws-cdk/aws-s3-assets/lib/asset.ts b/packages/@aws-cdk/aws-s3-assets/lib/asset.ts index 68484fcc4d985..f6b08bee2d132 100644 --- a/packages/@aws-cdk/aws-s3-assets/lib/asset.ts +++ b/packages/@aws-cdk/aws-s3-assets/lib/asset.ts @@ -110,7 +110,7 @@ export class Asset extends cdk.Construct implements assets.IAsset { const stack = cdk.Stack.of(this); - const location = stack.addFileAsset({ + const location = stack.synthesizer.addFileAsset({ packaging, sourceHash: this.sourceHash, fileName: staging.stagedPath, diff --git a/packages/@aws-cdk/cdk-assets-schema/lib/aws-destination.ts b/packages/@aws-cdk/cdk-assets-schema/lib/aws-destination.ts index e4b00ed4d308d..f419fde03c56d 100644 --- a/packages/@aws-cdk/cdk-assets-schema/lib/aws-destination.ts +++ b/packages/@aws-cdk/cdk-assets-schema/lib/aws-destination.ts @@ -22,24 +22,4 @@ export interface AwsDestination { * @default - No ExternalId will be supplied */ readonly assumeRoleExternalId?: string; -} - -/** - * Placeholders which can be used in the destinations - */ -export class Placeholders { - /** - * Insert this into the destination fields to be replaced with the current region - */ - public static readonly CURRENT_REGION = '${AWS::Region}'; - - /** - * Insert this into the destination fields to be replaced with the current account - */ - public static readonly CURRENT_ACCOUNT = '${AWS::AccountId}'; - - /** - * Insert this into the destination fields to be replaced with the current partition - */ - public static readonly CURRENT_PARTITION = '${AWS::Partition}'; } \ No newline at end of file diff --git a/packages/@aws-cdk/cloud-assembly-schema/lib/artifact-schema.ts b/packages/@aws-cdk/cloud-assembly-schema/lib/artifact-schema.ts new file mode 100644 index 0000000000000..866a1a6553c38 --- /dev/null +++ b/packages/@aws-cdk/cloud-assembly-schema/lib/artifact-schema.ts @@ -0,0 +1,90 @@ + +/** + * Artifact properties for CloudFormation stacks. + */ +export interface AwsCloudFormationStackProperties { + /** + * A file relative to the assembly root which contains the CloudFormation template for this stack. + */ + readonly templateFile: string; + + /** + * Values for CloudFormation stack parameters that should be passed when the stack is deployed. + * + * @default - No parameters + */ + readonly parameters?: { [id: string]: string }; + + /** + * The name to use for the CloudFormation stack. + * @default - name derived from artifact ID + */ + readonly stackName?: string; + + /** + * Whether to enable termination protection for this stack. + * + * @default false + */ + readonly terminationProtection?: boolean; + + /** + * The role that needs to be assumed to deploy the stack + * + * @default - No role is assumed (current credentials are used) + */ + readonly assumeRoleArn?: string; + + /** + * The role that is passed to CloudFormation to execute the change set + * + * @default - No role is passed (currently assumed role/credentials are used) + */ + readonly cloudFormationExecutionRoleArn?: string; + + /** + * If the stack template has already been included in the asset manifest, its asset URL + * + * @default - Not uploaded yet, upload just before deploying + */ + readonly stackTemplateAssetObjectUrl?: string; + + /** + * Version of bootstrap stack required to deploy this stack + * + * @default - No bootstrap stack required + */ + readonly requiresBootstrapStackVersion?: number; +} + +/** + * Artifact properties for the Asset Manifest + */ +export interface AssetManifestProperties { + /** + * Filename of the asset manifest + */ + readonly file: string; + + /** + * Version of bootstrap stack required to deploy this stack + * + * @default - Version 1 (basic modern bootstrap stack) + */ + readonly requiresBootstrapStackVersion?: number; +} + +/** + * Artifact properties for the Construct Tree Artifact + */ +export interface TreeArtifactProperties { + /** + * Filename of the tree artifact + */ + readonly file: string; +} + +/** + * Properties for manifest artifacts + */ +export type ArtifactProperties = AwsCloudFormationStackProperties | AssetManifestProperties | TreeArtifactProperties; \ No newline at end of file diff --git a/packages/@aws-cdk/cloud-assembly-schema/lib/index.ts b/packages/@aws-cdk/cloud-assembly-schema/lib/index.ts index 746eaa2ffb2ce..e9e9aa6a5863d 100644 --- a/packages/@aws-cdk/cloud-assembly-schema/lib/index.ts +++ b/packages/@aws-cdk/cloud-assembly-schema/lib/index.ts @@ -1,3 +1,5 @@ export * from './manifest'; export * from './schema'; +export * from './metadata-schema'; +export * from './artifact-schema'; export * from './context-queries'; diff --git a/packages/@aws-cdk/cloud-assembly-schema/lib/manifest.ts b/packages/@aws-cdk/cloud-assembly-schema/lib/manifest.ts index caa08bff0d01d..17b53ca499a50 100644 --- a/packages/@aws-cdk/cloud-assembly-schema/lib/manifest.ts +++ b/packages/@aws-cdk/cloud-assembly-schema/lib/manifest.ts @@ -1,6 +1,7 @@ import * as fs from 'fs'; import * as jsonschema from 'jsonschema'; import * as semver from 'semver'; +import { ArtifactMetadataEntryType } from './metadata-schema'; import * as assembly from './schema'; // this prefix is used by the CLI to identify this specific error. @@ -100,7 +101,7 @@ export class Manifest { if (artifact.type === assembly.ArtifactType.AWS_CLOUDFORMATION_STACK) { for (const metadataEntries of Object.values(artifact.metadata || [])) { for (const metadataEntry of metadataEntries) { - if (metadataEntry.type === assembly.ArtifactMetadataEntryType.STACK_TAGS && metadataEntry.data) { + if (metadataEntry.type === ArtifactMetadataEntryType.STACK_TAGS && metadataEntry.data) { const metadataAny = metadataEntry as any; diff --git a/packages/@aws-cdk/cloud-assembly-schema/lib/metadata-schema.ts b/packages/@aws-cdk/cloud-assembly-schema/lib/metadata-schema.ts new file mode 100644 index 0000000000000..54cabf83554fc --- /dev/null +++ b/packages/@aws-cdk/cloud-assembly-schema/lib/metadata-schema.ts @@ -0,0 +1,215 @@ +/** + * Common properties for asset metadata. + */ +interface BaseAssetMetadataEntry { + /** + * Requested packaging style + */ + readonly packaging: string; + + /** + * Logical identifier for the asset + */ + readonly id: string; + + /** + * The hash of the asset source. + */ + readonly sourceHash: string; + + /** + * Path on disk to the asset + */ + readonly path: string; +} + +/** + * Metadata Entry spec for files. + */ +export interface FileAssetMetadataEntry extends BaseAssetMetadataEntry { + /** + * Requested packaging style + */ + readonly packaging: 'zip' | 'file'; + + /** + * Name of parameter where S3 bucket should be passed in + */ + readonly s3BucketParameter: string; + + /** + * Name of parameter where S3 key should be passed in + */ + readonly s3KeyParameter: string; + + /** + * The name of the parameter where the hash of the bundled asset should be passed in. + */ + readonly artifactHashParameter: string; +} + +/** + * Metadata Entry spec for stack tag. + */ +export interface Tag { + /** + * Tag key. + */ + readonly key: string + + /** + * Tag value. + */ + readonly value: string +} + +/** + * Metadata Entry spec for container images. + */ +export interface ContainerImageAssetMetadataEntry extends BaseAssetMetadataEntry { + /** + * Type of asset + */ + readonly packaging: 'container-image'; + + /** + * ECR Repository name and repo digest (separated by "@sha256:") where this + * image is stored. + * + * @default undefined If not specified, `repositoryName` and `imageTag` are + * required because otherwise how will the stack know where to find the asset, + * ha? + * @deprecated specify `repositoryName` and `imageTag` instead, and then you + * know where the image will go. + */ + readonly imageNameParameter?: string; + + /** + * ECR repository name, if omitted a default name based on the asset's ID is + * used instead. Specify this property if you need to statically address the + * image, e.g. from a Kubernetes Pod. Note, this is only the repository name, + * without the registry and the tag parts. + * + * @default - this parameter is REQUIRED after 1.21.0 + */ + readonly repositoryName?: string; + + /** + * The docker image tag to use for tagging pushed images. This field is + * required if `imageParameterName` is ommited (otherwise, the app won't be + * able to find the image). + * + * @default - this parameter is REQUIRED after 1.21.0 + */ + readonly imageTag?: string; + + /** + * Build args to pass to the `docker build` command + * + * @default no build args are passed + */ + readonly buildArgs?: { [key: string]: string }; + + /** + * Docker target to build to + * + * @default no build target + */ + readonly target?: string; + + /** + * Path to the Dockerfile (relative to the directory). + * + * @default - no file is passed + */ + readonly file?: string; +} + +/** + * @see ArtifactMetadataEntryType.ASSET + */ +export type AssetMetadataEntry = FileAssetMetadataEntry | ContainerImageAssetMetadataEntry; + +// Type aliases for metadata entries. +// Used simply to assign names to data types for more clarity. + +/** + * @see ArtifactMetadataEntryType.INFO + * @see ArtifactMetadataEntryType.WARN + * @see ArtifactMetadataEntryType.ERROR + */ +export type LogMessageMetadataEntry = string; + +/** + * @see ArtifactMetadataEntryType.LOGICAL_ID + */ +export type LogicalIdMetadataEntry = string; + +/** + * @see ArtifactMetadataEntryType.STACK_TAGS + */ +export type StackTagsMetadataEntry = Tag[]; + +/** + * Union type for all metadata entries that might exist in the manifest. + */ +export type MetadataEntryData = AssetMetadataEntry | LogMessageMetadataEntry | LogicalIdMetadataEntry | StackTagsMetadataEntry; + +/** + * Type of artifact metadata entry. + */ +export enum ArtifactMetadataEntryType { + /** + * Asset in metadata. + */ + ASSET = 'aws:cdk:asset', + + /** + * Metadata key used to print INFO-level messages by the toolkit when an app is syntheized. + */ + INFO = 'aws:cdk:info', + + /** + * Metadata key used to print WARNING-level messages by the toolkit when an app is syntheized. + */ + WARN = 'aws:cdk:warning', + + /** + * Metadata key used to print ERROR-level messages by the toolkit when an app is syntheized. + */ + ERROR = 'aws:cdk:error', + + /** + * Represents the CloudFormation logical ID of a resource at a certain path. + */ + LOGICAL_ID = 'aws:cdk:logicalId', + + /** + * Represents tags of a stack. + */ + STACK_TAGS = 'aws:cdk:stack-tags' +} + +/** + * A metadata entry in a cloud assembly artifact. + */ +export interface MetadataEntry { + /** + * The type of the metadata entry. + */ + readonly type: string; + + /** + * The data. + * + * @default - no data. + */ + readonly data?: MetadataEntryData; + + /** + * A stack trace for when the entry was created. + * + * @default - no trace. + */ + readonly trace?: string[]; +} diff --git a/packages/@aws-cdk/cloud-assembly-schema/lib/schema.ts b/packages/@aws-cdk/cloud-assembly-schema/lib/schema.ts index 3369c16e93b4c..1c4efd0cded5d 100644 --- a/packages/@aws-cdk/cloud-assembly-schema/lib/schema.ts +++ b/packages/@aws-cdk/cloud-assembly-schema/lib/schema.ts @@ -1,196 +1,6 @@ +import { ArtifactProperties } from './artifact-schema'; import { ContextProvider, ContextQueryProperties } from './context-queries'; - -/** - * Common properties for asset metadata. - */ -interface BaseAssetMetadataEntry { - /** - * Requested packaging style - */ - readonly packaging: string; - - /** - * Logical identifier for the asset - */ - readonly id: string; - - /** - * The hash of the asset source. - */ - readonly sourceHash: string; - - /** - * Path on disk to the asset - */ - readonly path: string; -} - -/** - * Metadata Entry spec for files. - */ -export interface FileAssetMetadataEntry extends BaseAssetMetadataEntry { - /** - * Requested packaging style - */ - readonly packaging: 'zip' | 'file'; - - /** - * Name of parameter where S3 bucket should be passed in - */ - readonly s3BucketParameter: string; - - /** - * Name of parameter where S3 key should be passed in - */ - readonly s3KeyParameter: string; - - /** - * The name of the parameter where the hash of the bundled asset should be passed in. - */ - readonly artifactHashParameter: string; -} - -/** - * Metadata Entry spec for container images. - */ -export interface ContainerImageAssetMetadataEntry extends BaseAssetMetadataEntry { - /** - * Type of asset - */ - readonly packaging: 'container-image'; - - /** - * ECR Repository name and repo digest (separated by "@sha256:") where this - * image is stored. - * - * @default undefined If not specified, `repositoryName` and `imageTag` are - * required because otherwise how will the stack know where to find the asset, - * ha? - * @deprecated specify `repositoryName` and `imageTag` instead, and then you - * know where the image will go. - */ - readonly imageNameParameter?: string; - - /** - * ECR repository name, if omitted a default name based on the asset's ID is - * used instead. Specify this property if you need to statically address the - * image, e.g. from a Kubernetes Pod. Note, this is only the repository name, - * without the registry and the tag parts. - * - * @default - this parameter is REQUIRED after 1.21.0 - */ - readonly repositoryName?: string; - - /** - * The docker image tag to use for tagging pushed images. This field is - * required if `imageParameterName` is ommited (otherwise, the app won't be - * able to find the image). - * - * @default - this parameter is REQUIRED after 1.21.0 - */ - readonly imageTag?: string; - - /** - * Build args to pass to the `docker build` command - * - * @default no build args are passed - */ - readonly buildArgs?: { [key: string]: string }; - - /** - * Docker target to build to - * - * @default no build target - */ - readonly target?: string; - - /** - * Path to the Dockerfile (relative to the directory). - * - * @default - no file is passed - */ - readonly file?: string; -} - -/** - * Metadata Entry spec for stack tag. - */ -export interface Tag { - /** - * Tag key. - */ - readonly key: string - - /** - * Tag value. - */ - readonly value: string -} - -/** - * @see ArtifactMetadataEntryType.ASSET - */ -export type AssetMetadataEntry = FileAssetMetadataEntry | ContainerImageAssetMetadataEntry; - -// Type aliases for metadata entries. -// Used simply to assign names to data types for more clearity. - -/** - * @see ArtifactMetadataEntryType.INFO - * @see ArtifactMetadataEntryType.WARN - * @see ArtifactMetadataEntryType.ERROR - */ -export type LogMessageMetadataEntry = string; - -/** - * @see ArtifactMetadataEntryType.LOGICAL_ID - */ -export type LogicalIdMetadataEntry = string; - -/** - * @see ArtifactMetadataEntryType.STACK_TAGS - */ -export type StackTagsMetadataEntry = Tag[]; - -/** - * Union type for all metadata entries that might exist in the manifest. - */ -export type MetadataEntryData = AssetMetadataEntry | LogMessageMetadataEntry | LogicalIdMetadataEntry | StackTagsMetadataEntry; - -/** - * Type of artifact metadata entry. - */ -export enum ArtifactMetadataEntryType { - /** - * Asset in metadata. - */ - ASSET = 'aws:cdk:asset', - - /** - * Metadata key used to print INFO-level messages by the toolkit when an app is syntheized. - */ - INFO = 'aws:cdk:info', - - /** - * Metadata key used to print WARNING-level messages by the toolkit when an app is syntheized. - */ - WARN = 'aws:cdk:warning', - - /** - * Metadata key used to print ERROR-level messages by the toolkit when an app is syntheized. - */ - ERROR = 'aws:cdk:error', - - /** - * Represents the CloudFormation logical ID of a resource at a certain path. - */ - LOGICAL_ID = 'aws:cdk:logicalId', - - /** - * Represents tags of a stack. - */ - STACK_TAGS = 'aws:cdk:stack-tags' -} +import { MetadataEntry } from './metadata-schema'; /** * Type of cloud artifact. @@ -210,30 +20,11 @@ export enum ArtifactType { * The artifact contains the CDK application's construct tree. */ CDK_TREE = 'cdk:tree', -} - -/** - * A metadata entry in a cloud assembly artifact. - */ -export interface MetadataEntry { - /** - * The type of the metadata entry. - */ - readonly type: string; /** - * The data. - * - * @default - no data. - */ - readonly data?: MetadataEntryData; - - /** - * A stack trace for when the entry was created. - * - * @default - no trace. + * Manifest for all assets in the Cloud Assembly */ - readonly trace?: string[]; + ASSET_MANIFEST = 'cdk:asset-manifest', } /** @@ -301,7 +92,7 @@ export interface ArtifactManifest { * * @default - no properties. */ - readonly properties?: { [name: string]: any }; + readonly properties?: ArtifactProperties; } /** diff --git a/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json b/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json index 885af9359b4d4..399b5e6c7e2b7 100644 --- a/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json +++ b/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json @@ -63,8 +63,17 @@ }, "properties": { "description": "The set of properties for this artifact (depends on type) (Default - no properties.)", - "type": "object", - "additionalProperties": {} + "anyOf": [ + { + "$ref": "#/definitions/AwsCloudFormationStackProperties" + }, + { + "$ref": "#/definitions/AssetManifestProperties" + }, + { + "$ref": "#/definitions/TreeArtifactProperties" + } + ] } }, "required": [ @@ -75,6 +84,7 @@ "description": "Type of cloud artifact.", "enum": [ "aws:cloudformation:stack", + "cdk:asset-manifest", "cdk:tree", "none" ], @@ -246,6 +256,81 @@ "value" ] }, + "AwsCloudFormationStackProperties": { + "description": "Artifact properties for CloudFormation stacks.", + "type": "object", + "properties": { + "templateFile": { + "description": "A file relative to the assembly root which contains the CloudFormation template for this stack.", + "type": "string" + }, + "parameters": { + "description": "Values for CloudFormation stack parameters that should be passed when the stack is deployed. (Default - No parameters)", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "stackName": { + "description": "The name to use for the CloudFormation stack. (Default - name derived from artifact ID)", + "type": "string" + }, + "terminationProtection": { + "description": "Whether to enable termination protection for this stack.", + "default": false, + "type": "boolean" + }, + "assumeRoleArn": { + "description": "The role that needs to be assumed to deploy the stack (Default - No role is assumed (current credentials are used))", + "type": "string" + }, + "cloudFormationExecutionRoleArn": { + "description": "The role that is passed to CloudFormation to execute the change set (Default - No role is passed (currently assumed role/credentials are used))", + "type": "string" + }, + "stackTemplateAssetObjectUrl": { + "description": "If the stack template has already been included in the asset manifest, its asset URL (Default - Not uploaded yet, upload just before deploying)", + "type": "string" + }, + "requiresBootstrapStackVersion": { + "description": "Version of bootstrap stack required to deploy this stack (Default - No bootstrap stack required)", + "type": "number" + } + }, + "required": [ + "templateFile" + ] + }, + "AssetManifestProperties": { + "description": "Artifact properties for the Asset Manifest", + "type": "object", + "properties": { + "file": { + "description": "Filename of the asset manifest", + "type": "string" + }, + "requiresBootstrapStackVersion": { + "description": "Version of bootstrap stack required to deploy this stack (Default - Version 1 (basic modern bootstrap stack))", + "type": "number" + } + }, + "required": [ + "file" + ] + }, + "TreeArtifactProperties": { + "description": "Artifact properties for the Construct Tree Artifact", + "type": "object", + "properties": { + "file": { + "description": "Filename of the tree artifact", + "type": "string" + } + }, + "required": [ + "file" + ] + }, "MissingContext": { "description": "Represents a missing piece of context.", "type": "object", diff --git a/packages/@aws-cdk/cloud-assembly-schema/test/manifest.test.ts b/packages/@aws-cdk/cloud-assembly-schema/test/manifest.test.ts index bd697e19e3b94..a90c2e411a39c 100644 --- a/packages/@aws-cdk/cloud-assembly-schema/test/manifest.test.ts +++ b/packages/@aws-cdk/cloud-assembly-schema/test/manifest.test.ts @@ -50,7 +50,7 @@ test('manifest save', () => { }); -test('cloud-assembly.json.schema is correct', () => { +test('if this test fails, run "yarn update-schema"', () => { // when we compare schemas we ignore changes the // description that is generated from the ts docstrings. diff --git a/packages/@aws-cdk/core/lib/index.ts b/packages/@aws-cdk/core/lib/index.ts index 201de0947af84..a4690f41dc898 100644 --- a/packages/@aws-cdk/core/lib/index.ts +++ b/packages/@aws-cdk/core/lib/index.ts @@ -8,6 +8,7 @@ export * from './lazy'; export * from './tag-manager'; export * from './dependency'; export * from './string-fragments'; +export * from './stack-synthesizers'; export * from './reference'; export * from './cfn-condition'; diff --git a/packages/@aws-cdk/core/lib/nested-stack.ts b/packages/@aws-cdk/core/lib/nested-stack.ts index 647b5fc014720..4d87c148958f0 100644 --- a/packages/@aws-cdk/core/lib/nested-stack.ts +++ b/packages/@aws-cdk/core/lib/nested-stack.ts @@ -9,6 +9,7 @@ import { Duration } from './duration'; import { Lazy } from './lazy'; import { IResolveContext } from './resolvable'; import { Stack } from './stack'; +import { NestedStackSynthesizer } from './stack-synthesizers'; import { Token } from './token'; const NESTED_STACK_SYMBOL = Symbol.for('@aws-cdk/core.NestedStack'); @@ -96,7 +97,10 @@ export class NestedStack extends Stack { constructor(scope: Construct, id: string, props: NestedStackProps = { }) { const parentStack = findParentStack(scope); - super(scope, id, { env: { account: parentStack.account, region: parentStack.region } }); + super(scope, id, { + env: { account: parentStack.account, region: parentStack.region }, + synthesizer: new NestedStackSynthesizer(parentStack.synthesizer), + }); this._parentStack = parentStack; diff --git a/packages/@aws-cdk/core/lib/stack-synthesizers/_shared.ts b/packages/@aws-cdk/core/lib/stack-synthesizers/_shared.ts new file mode 100644 index 0000000000000..9228492135ec4 --- /dev/null +++ b/packages/@aws-cdk/core/lib/stack-synthesizers/_shared.ts @@ -0,0 +1,118 @@ +import * as cxschema from '@aws-cdk/cloud-assembly-schema'; +import * as crypto from 'crypto'; +import { ConstructNode, IConstruct, ISynthesisSession } from '../construct-compat'; +import { Stack } from '../stack'; + +/** + * Shared logic of writing stack artifact to the Cloud Assembly + * + * This logic is shared between StackSyntheses. + * + * It could have been a protected method on a base class, but it + * uses `Partial` in the + * parameters (which is convenient so I can remain typesafe without + * copy/pasting), and jsii will choke on this type. + */ +export function addStackArtifactToAssembly( + session: ISynthesisSession, + stack: Stack, + stackProps: Partial, + additionalStackDependencies: string[]) { + + // nested stack tags are applied at the AWS::CloudFormation::Stack resource + // level and are not needed in the cloud assembly. + // TODO: move these to the cloud assembly artifact properties instead of metadata + if (stack.tags.hasTags()) { + stack.node.addMetadata(cxschema.ArtifactMetadataEntryType.STACK_TAGS, stack.tags.renderTags()); + } + + const deps = [ + ...stack.dependencies.map(s => s.artifactId), + ...additionalStackDependencies, + ]; + const meta = collectStackMetadata(stack); + + // backwards compatibility since originally artifact ID was always equal to + // stack name the stackName attribute is optional and if it is not specified + // the CLI will use the artifact ID as the stack name. we *could have* + // always put the stack name here but wanted to minimize the risk around + // changes to the assembly manifest. so this means that as long as stack + // name and artifact ID are the same, the cloud assembly manifest will not + // change. + const stackNameProperty = stack.stackName === stack.artifactId + ? { } + : { stackName: stack.stackName }; + + const properties: cxschema.AwsCloudFormationStackProperties = { + templateFile: stack.templateFile, + ...stackProps, + ...stackNameProperty, + }; + + // add an artifact that represents this stack + session.assembly.addArtifact(stack.artifactId, { + type: cxschema.ArtifactType.AWS_CLOUDFORMATION_STACK, + environment: stack.environment, + properties, + dependencies: deps.length > 0 ? deps : undefined, + metadata: Object.keys(meta).length > 0 ? meta : undefined, + }); +} + +/** + * Collect the metadata from a stack + */ +function collectStackMetadata(stack: Stack) { + const output: { [id: string]: cxschema.MetadataEntry[] } = { }; + + visit(stack); + + return output; + + function visit(node: IConstruct) { + // break off if we reached a node that is not a child of this stack + const parent = findParentStack(node); + if (parent !== stack) { + return; + } + + if (node.node.metadata.length > 0) { + // Make the path absolute + output[ConstructNode.PATH_SEP + node.node.path] = node.node.metadata.map(md => stack.resolve(md) as cxschema.MetadataEntry); + } + + for (const child of node.node.children) { + visit(child); + } + } + + function findParentStack(node: IConstruct): Stack | undefined { + if (node instanceof Stack && node.nestedStackParent === undefined) { + return node; + } + + if (!node.node.scope) { + return undefined; + } + + return findParentStack(node.node.scope); + } +} + +/** + * Hash a string + */ +export function contentHash(content: string) { + return crypto.createHash('sha256').update(content).digest('hex'); +} + +/** + * Throw an error message about binding() if we don't have a value for x. + * + * This replaces the ! assertions we would need everywhere otherwise. + */ +export function assertBound(x: A | undefined): asserts x is NonNullable { + if (x === null && x === undefined) { + throw new Error('You must call bindStack() first'); + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/core/lib/stack-synthesizers/default-synthesizer.ts b/packages/@aws-cdk/core/lib/stack-synthesizers/default-synthesizer.ts new file mode 100644 index 0000000000000..ca2f9b0b7e3fe --- /dev/null +++ b/packages/@aws-cdk/core/lib/stack-synthesizers/default-synthesizer.ts @@ -0,0 +1,341 @@ +import * as asset_schema from '@aws-cdk/cdk-assets-schema'; +import * as cxschema from '@aws-cdk/cloud-assembly-schema'; +import * as cxapi from '@aws-cdk/cx-api'; +import * as fs from 'fs'; +import * as path from 'path'; +import { DockerImageAssetLocation, DockerImageAssetSource, FileAssetLocation, FileAssetPackaging, FileAssetSource } from '../assets'; +import { Fn } from '../cfn-fn'; +import { ISynthesisSession } from '../construct-compat'; +import { Stack } from '../stack'; +import { Token } from '../token'; +import { addStackArtifactToAssembly, assertBound, contentHash } from './_shared'; +import { IStackSynthesizer } from './types'; + +/** + * Configuration properties for DefaultStackSynthesizer + */ +export interface DefaultStackSynthesizerProps { + /** + * Name of the S3 bucket to hold file assets + * + * You must supply this if you have given a non-standard name to the staging bucket. + * + * The placeholders `${Qualifier}`, `${AWS::AccountId}` and `${AWS::Region}` will + * be replaced with the values of qualifier and the stack's account and region, + * respectively. + * + * @default DefaultStackSynthesizer.DEFAULT_FILE_ASSETS_BUCKET_NAME + */ + readonly fileAssetsBucketName?: string; + + /** + * Name of the ECR repository to hold Docker Image assets + * + * You must supply this if you have given a non-standard name to the ECR repository. + * + * The placeholders `${Qualifier}`, `${AWS::AccountId}` and `${AWS::Region}` will + * be replaced with the values of qualifier and the stack's account and region, + * respectively. + * + * @default DefaultStackSynthesizer.DEFAULT_IMAGE_ASSETS_REPOSITORY_NAME + */ + readonly imageAssetsRepositoryName?: string; + + /** + * The role to use to publish assets to this environment + * + * You must supply this if you have given a non-standard name to the publishing role. + * + * The placeholders `${Qualifier}`, `${AWS::AccountId}` and `${AWS::Region}` will + * be replaced with the values of qualifier and the stack's account and region, + * respectively. + * + * @default DefaultStackSynthesizer.DEFAULT_ASSET_PUBLISHING_ROLE_ARN + */ + readonly assetPublishingRoleArn?: string; + + /** + * External ID to use when assuming role for asset publishing + * + * @default - No external ID + */ + readonly assetPublishingExternalId?: string; + + /** + * The role to assume to initiate a deployment in this environment + * + * You must supply this if you have given a non-standard name to the publishing role. + * + * The placeholders `${Qualifier}`, `${AWS::AccountId}` and `${AWS::Region}` will + * be replaced with the values of qualifier and the stack's account and region, + * respectively. + * + * @default DefaultStackSynthesizer.DEFAULT_DEPLOY_ACTION_ROLE_ARN + */ + readonly deployActionRoleArn?: string; + + /** + * The role CloudFormation will assume when deploying the Stack + * + * You must supply this if you have given a non-standard name to the execution role. + * + * The placeholders `${Qualifier}`, `${AWS::AccountId}` and `${AWS::Region}` will + * be replaced with the values of qualifier and the stack's account and region, + * respectively. + * + * @default DefaultStackSynthesizer.DEFAULT_CLOUDFORMATION_ROLE_ARN + */ + readonly cloudFormationExecutionRole?: string; + + /** + * Qualifier to disambiguate multiple environments in the same account + * + * You can use this and leave the other naming properties empty if you have deployed + * the bootstrap environment with standard names but only differnet qualifiers. + * + * @default DefaultStackSynthesizer.DEFAULT_QUALIFIER + */ + readonly qualifier?: string; +} + +/** + * Uses conventionally named roles and reify asset storage locations + * + * This synthesizer is the only StackSynthesizer that generates + * an asset manifest, and is required to deploy CDK applications using the + * `@aws-cdk/app-delivery` CI/CD library. + * + * Requires the environment to have been bootstrapped with Bootstrap Stack V2. + */ +export class DefaultStackSynthesizer implements IStackSynthesizer { + /** + * Default ARN qualifier + */ + public static readonly DEFAULT_QUALIFIER = 'hnb659fds'; + + /** + * Default CloudFormation role ARN. + */ + public static readonly DEFAULT_CLOUDFORMATION_ROLE_ARN = 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-bootstrap-cfn-exec-role-${AWS::AccountId}-${AWS::Region}'; + + /** + * Default deploy action role ARN. + */ + public static readonly DEFAULT_DEPLOY_ACTION_ROLE_ARN = 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-bootstrap-deploy-action-role-${AWS::AccountId}-${AWS::Region}'; + + /** + * Default asset publishing role ARN. + */ + public static readonly DEFAULT_ASSET_PUBLISHING_ROLE_ARN = 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-bootstrap-publishing-role-${AWS::AccountId}-${AWS::Region}'; + + /** + * Default image assets repository name + */ + public static readonly DEFAULT_IMAGE_ASSETS_REPOSITORY_NAME = 'cdk-bootstrap-${Qualifier}-container-assets-${AWS::AccountId}-${AWS::Region}'; + + /** + * Default file assets bucket name + */ + public static readonly DEFAULT_FILE_ASSETS_BUCKET_NAME = 'cdk-bootstrap-${Qualifier}-assets-${AWS::AccountId}-${AWS::Region}'; + + private stack?: Stack; + private bucketName?: string; + private repositoryName?: string; + private deployActionRoleArn?: string; + private cloudFormationExecutionRoleArn?: string; + private assetPublishingRoleArn?: string; + + private readonly files: NonNullable = {}; + private readonly dockerImages: NonNullable = {}; + + constructor(private readonly props: DefaultStackSynthesizerProps = {}) { + } + + public bind(stack: Stack): void { + this.stack = stack; + + const qualifier = this.props.qualifier ?? DefaultStackSynthesizer.DEFAULT_QUALIFIER; + + // Function to replace placeholders in the input string as much as possible + // + // We replace: + // - ${Qualifier}: always + // - ${AWS::AccountId}, ${AWS::Region}: only if we have the actual values available + // - ${AWS::Partition}: never, since we never have the actual partition value. + const specialize = (s: string) => { + s = replaceAll(s, '${Qualifier}', qualifier); + return cxapi.EnvironmentPlaceholders.replace(s, { + region: resolvedOr(stack.region, cxapi.EnvironmentPlaceholders.CURRENT_REGION), + accountId: resolvedOr(stack.account, cxapi.EnvironmentPlaceholders.CURRENT_ACCOUNT), + partition: cxapi.EnvironmentPlaceholders.CURRENT_PARTITION, + }); + }; + + // tslint:disable:max-line-length + this.bucketName = specialize(this.props.fileAssetsBucketName ?? DefaultStackSynthesizer.DEFAULT_FILE_ASSETS_BUCKET_NAME); + this.repositoryName = specialize(this.props.imageAssetsRepositoryName ?? DefaultStackSynthesizer.DEFAULT_IMAGE_ASSETS_REPOSITORY_NAME); + this.deployActionRoleArn = specialize(this.props.deployActionRoleArn ?? DefaultStackSynthesizer.DEFAULT_DEPLOY_ACTION_ROLE_ARN); + this.cloudFormationExecutionRoleArn = specialize(this.props.cloudFormationExecutionRole ?? DefaultStackSynthesizer.DEFAULT_CLOUDFORMATION_ROLE_ARN); + this.assetPublishingRoleArn = specialize(this.props.assetPublishingRoleArn ?? DefaultStackSynthesizer.DEFAULT_ASSET_PUBLISHING_ROLE_ARN); + // tslint:enable:max-line-length + } + + public addFileAsset(asset: FileAssetSource): FileAssetLocation { + assertBound(this.stack); + assertBound(this.bucketName); + + const objectKey = asset.sourceHash + (asset.packaging === FileAssetPackaging.ZIP_DIRECTORY ? '.zip' : ''); + + // Add to manifest + this.files[asset.sourceHash] = { + source: { + path: asset.fileName, + packaging: asset.packaging, + }, + destinations: { + [this.manifestEnvName]: { + bucketName: this.bucketName, + objectKey, + region: resolvedOr(this.stack.region, undefined), + assumeRoleArn: this.assetPublishingRoleArn, + assumeRoleExternalId: this.props.assetPublishingExternalId, + }, + }, + }; + + // Return CFN expression + return { + bucketName: cfnify(this.bucketName), + objectKey, + s3Url: cfnify(`https://s3.${this.stack.region}.${this.stack.urlSuffix}/${this.bucketName}/${objectKey}`), + }; + } + + public addDockerImageAsset(asset: DockerImageAssetSource): DockerImageAssetLocation { + assertBound(this.stack); + assertBound(this.repositoryName); + + const imageTag = asset.sourceHash; + + // Add to manifest + this.dockerImages[asset.sourceHash] = { + source: { + directory: asset.directoryName, + dockerBuildArgs: asset.dockerBuildArgs, + dockerBuildTarget: asset.dockerBuildTarget, + dockerFile: asset.dockerFile, + }, + destinations: { + [this.manifestEnvName]: { + repositoryName: this.repositoryName, + imageTag, + region: resolvedOr(this.stack.region, undefined), + assumeRoleArn: this.assetPublishingRoleArn, + assumeRoleExternalId: this.props.assetPublishingExternalId, + }, + }, + }; + + // Return CFN expression + return { + repositoryName: cfnify(this.repositoryName), + imageUri: cfnify(`${this.stack.account}.dkr.ecr.${this.stack.region}.${this.stack.urlSuffix}/${this.repositoryName}:${imageTag}`), + }; + } + + public synthesizeStackArtifacts(session: ISynthesisSession): void { + assertBound(this.stack); + + // Add the stack's template to the artifact manifest + const templateAsset = this.addStackTemplateToAssetManifest(session); + + const artifactId = this.writeAssetManifest(session); + + addStackArtifactToAssembly(session, this.stack, { + assumeRoleArn: this.deployActionRoleArn, + cloudFormationExecutionRoleArn: this.cloudFormationExecutionRoleArn, + stackTemplateAssetObjectUrl: templateAsset.s3Url, + requiresBootstrapStackVersion: 1, + }, [artifactId]); + } + + /** + * Add the stack's template as one of the manifest assets + * + * This will make it get uploaded to S3 automatically by S3-assets. Return + * the URL. + */ + private addStackTemplateToAssetManifest(session: ISynthesisSession) { + assertBound(this.stack); + + const templatePath = path.join(session.assembly.outdir, this.stack.templateFile); + const template = fs.readFileSync(templatePath, { encoding: 'utf-8' }); + + return this.addFileAsset({ + fileName: this.stack.templateFile, + packaging: FileAssetPackaging.FILE, + sourceHash: contentHash(template), + }); + } + + /** + * Write an asset manifest to the Cloud Assembly, return the artifact IDs written + */ + private writeAssetManifest(session: ISynthesisSession): string { + assertBound(this.stack); + + const artifactId = `${this.stack.artifactId}.assets`; + const manifestFile = `${artifactId}.json`; + const outPath = path.join(session.assembly.outdir, manifestFile); + + const manifest: asset_schema.ManifestFile = { + version: asset_schema.AssetManifestSchema.currentVersion(), + files: this.files, + dockerImages: this.dockerImages, + }; + + fs.writeFileSync(outPath, JSON.stringify(manifest, undefined, 2)); + session.assembly.addArtifact(artifactId, { + type: cxschema.ArtifactType.ASSET_MANIFEST, + properties: { + file: manifestFile, + requiresBootstrapStackVersion: 1, + }, + }); + + return artifactId; + } + + private get manifestEnvName(): string { + assertBound(this.stack); + + return [ + resolvedOr(this.stack.account, 'current_account'), + resolvedOr(this.stack.region, 'current_region'), + ].join('-'); + } +} + +/** + * Return the given value if resolved or fall back to a default + */ +function resolvedOr(x: string, def: A): string | A { + return Token.isUnresolved(x) ? def : x; +} + +/** + * A "replace-all" function that doesn't require us escaping a literal string to a regex + */ +function replaceAll(s: string, search: string, replace: string) { + return s.split(search).join(replace); +} + +/** + * If the string still contains placeholders, wrap it in a Fn::Sub so they will be substituted at CFN deploymen time + * + * (This happens to work because the placeholders we picked map directly onto CFN + * placeholders. If they didn't we'd have to do a transformation here). + */ +function cfnify(s: string): string { + return s.indexOf('${') > -1 ? Fn.sub(s) : s; +} diff --git a/packages/@aws-cdk/core/lib/stack-synthesizers/index.ts b/packages/@aws-cdk/core/lib/stack-synthesizers/index.ts new file mode 100644 index 0000000000000..5920f19bae2c9 --- /dev/null +++ b/packages/@aws-cdk/core/lib/stack-synthesizers/index.ts @@ -0,0 +1,4 @@ +export * from './types'; +export * from './default-synthesizer'; +export * from './legacy'; +export * from './nested'; \ No newline at end of file diff --git a/packages/@aws-cdk/core/lib/stack-synthesizers/legacy.ts b/packages/@aws-cdk/core/lib/stack-synthesizers/legacy.ts new file mode 100644 index 0000000000000..7d46d19544fba --- /dev/null +++ b/packages/@aws-cdk/core/lib/stack-synthesizers/legacy.ts @@ -0,0 +1,177 @@ +import * as cxschema from '@aws-cdk/cloud-assembly-schema'; +import * as cxapi from '@aws-cdk/cx-api'; +import { DockerImageAssetLocation, DockerImageAssetSource, FileAssetLocation, FileAssetSource } from '../assets'; +import { Fn } from '../cfn-fn'; +import { Construct, ISynthesisSession } from '../construct-compat'; +import { FileAssetParameters } from '../private/asset-parameters'; +import { Stack } from '../stack'; +import { addStackArtifactToAssembly, assertBound } from './_shared'; +import { IStackSynthesizer } from './types'; + +/** + * The well-known name for the docker image asset ECR repository. All docker + * image assets will be pushed into this repository with an image tag based on + * the source hash. + */ +const ASSETS_ECR_REPOSITORY_NAME = 'aws-cdk/assets'; + +/** + * This allows users to work around the fact that the ECR repository is + * (currently) not configurable by setting this context key to their desired + * repository name. The CLI will auto-create this ECR repository if it's not + * already created. + */ +const ASSETS_ECR_REPOSITORY_NAME_OVERRIDE_CONTEXT_KEY = 'assets-ecr-repository-name'; + +/** + * Use the original deployment environment + * + * This deployment environment is restricted in cross-environment deployments, + * CI/CD deployments, and will use up CloudFormation parameters in your template. + * + * This is the only StackSynthesizer that supports customizing asset behavior + * by overriding `Stack.addFileAsset()` and `Stack.addDockerImageAsset()`. + */ +export class LegacyStackSynthesizer implements IStackSynthesizer { + private stack?: Stack; + private cycle = false; + + /** + * Includes all parameters synthesized for assets (lazy). + */ + private _assetParameters?: Construct; + + /** + * The image ID of all the docker image assets that were already added to this + * stack (to avoid duplication). + */ + private readonly addedImageAssets = new Set(); + + public bind(stack: Stack): void { + this.stack = stack; + } + + public addFileAsset(asset: FileAssetSource): FileAssetLocation { + assertBound(this.stack); + + // Backwards compatibility hack. We have a number of conflicting goals here: + // + // - We want put the actual logic in this class + // - We ALSO want to keep supporting people overriding Stack.addFileAsset (for backwards compatibility, + // because that mechanism is currently used to make CI/CD scenarios work) + // - We ALSO want to allow both entry points from user code (our own framework + // code will always call stack.deploymentMechanism.addFileAsset() but existing users + // may still be calling `stack.addFileAsset()` directly. + // + // Solution: delegate call to the stack, but if the stack delegates back to us again + // then do the actual logic. + if (this.cycle) { + return this.doAddFileAsset(asset); + } + this.cycle = true; + try { + return this.stack.addFileAsset(asset); + } finally { + this.cycle = false; + } + } + + public addDockerImageAsset(asset: DockerImageAssetSource): DockerImageAssetLocation { + assertBound(this.stack); + + // See `addFileAsset` for explanation. + if (this.cycle) { + return this.doAddDockerImageAsset(asset); + } + this.cycle = true; + try { + return this.stack.addDockerImageAsset(asset); + } finally { + this.cycle = false; + } + } + + public synthesizeStackArtifacts(session: ISynthesisSession): void { + assertBound(this.stack); + + // Just do the default stuff, nothing special + addStackArtifactToAssembly(session, this.stack, {}, []); + } + + private doAddDockerImageAsset(asset: DockerImageAssetSource): DockerImageAssetLocation { + assertBound(this.stack); + + // check if we have an override from context + const repositoryNameOverride = this.stack.node.tryGetContext(ASSETS_ECR_REPOSITORY_NAME_OVERRIDE_CONTEXT_KEY); + const repositoryName = asset.repositoryName ?? repositoryNameOverride ?? ASSETS_ECR_REPOSITORY_NAME; + const imageTag = asset.sourceHash; + const assetId = asset.sourceHash; + + // only add every image (identified by source hash) once for each stack that uses it. + if (!this.addedImageAssets.has(assetId)) { + const metadata: cxschema.ContainerImageAssetMetadataEntry = { + repositoryName, + imageTag, + id: assetId, + packaging: 'container-image', + path: asset.directoryName, + sourceHash: asset.sourceHash, + buildArgs: asset.dockerBuildArgs, + target: asset.dockerBuildTarget, + file: asset.dockerFile, + }; + + this.stack.node.addMetadata(cxschema.ArtifactMetadataEntryType.ASSET, metadata); + this.addedImageAssets.add(assetId); + } + + return { + imageUri: `${this.stack.account}.dkr.ecr.${this.stack.region}.${this.stack.urlSuffix}/${repositoryName}:${imageTag}`, + repositoryName, + }; + } + + private doAddFileAsset(asset: FileAssetSource): FileAssetLocation { + assertBound(this.stack); + + let params = this.assetParameters.node.tryFindChild(asset.sourceHash) as FileAssetParameters; + if (!params) { + params = new FileAssetParameters(this.assetParameters, asset.sourceHash); + + const metadata: cxschema.FileAssetMetadataEntry = { + path: asset.fileName, + id: asset.sourceHash, + packaging: asset.packaging, + sourceHash: asset.sourceHash, + + s3BucketParameter: params.bucketNameParameter.logicalId, + s3KeyParameter: params.objectKeyParameter.logicalId, + artifactHashParameter: params.artifactHashParameter.logicalId, + }; + + this.stack.node.addMetadata(cxschema.ArtifactMetadataEntryType.ASSET, metadata); + } + + const bucketName = params.bucketNameParameter.valueAsString; + + // key is prefix|postfix + const encodedKey = params.objectKeyParameter.valueAsString; + + const s3Prefix = Fn.select(0, Fn.split(cxapi.ASSET_PREFIX_SEPARATOR, encodedKey)); + const s3Filename = Fn.select(1, Fn.split(cxapi.ASSET_PREFIX_SEPARATOR, encodedKey)); + const objectKey = `${s3Prefix}${s3Filename}`; + + const s3Url = `https://s3.${this.stack.region}.${this.stack.urlSuffix}/${bucketName}/${objectKey}`; + + return { bucketName, objectKey, s3Url }; + } + + private get assetParameters() { + assertBound(this.stack); + + if (!this._assetParameters) { + this._assetParameters = new Construct(this.stack, 'AssetParameters'); + } + return this._assetParameters; + } +} diff --git a/packages/@aws-cdk/core/lib/stack-synthesizers/nested.ts b/packages/@aws-cdk/core/lib/stack-synthesizers/nested.ts new file mode 100644 index 0000000000000..8841618823aa9 --- /dev/null +++ b/packages/@aws-cdk/core/lib/stack-synthesizers/nested.ts @@ -0,0 +1,35 @@ +import { DockerImageAssetLocation, DockerImageAssetSource, FileAssetLocation, FileAssetSource } from '../assets'; +import { ISynthesisSession } from '../construct-compat'; +import { Stack } from '../stack'; +import { IStackSynthesizer } from './types'; + +/** + * Deployment environment for a nested stack + * + * Interoperates with the StackSynthesizer of the parent stack. + */ +export class NestedStackSynthesizer implements IStackSynthesizer { + constructor(private readonly parentDeployment: IStackSynthesizer) { + } + + public bind(_stack: Stack): void { + // Nothing to do + } + + public addFileAsset(asset: FileAssetSource): FileAssetLocation { + // Forward to parent deployment. By the magic of cross-stack references any parameter + // returned and used will magically be forwarded to the nested stack. + return this.parentDeployment.addFileAsset(asset); + } + + public addDockerImageAsset(asset: DockerImageAssetSource): DockerImageAssetLocation { + // Forward to parent deployment. By the magic of cross-stack references any parameter + // returned and used will magically be forwarded to the nested stack. + return this.parentDeployment.addDockerImageAsset(asset); + } + + public synthesizeStackArtifacts(_session: ISynthesisSession): void { + // Do not emit Nested Stack as a cloud assembly artifact. + // It will be registered as an S3 asset of its parent instead. + } +} diff --git a/packages/@aws-cdk/core/lib/stack-synthesizers/types.ts b/packages/@aws-cdk/core/lib/stack-synthesizers/types.ts new file mode 100644 index 0000000000000..c7f5fce1a7cbf --- /dev/null +++ b/packages/@aws-cdk/core/lib/stack-synthesizers/types.ts @@ -0,0 +1,36 @@ +import { DockerImageAssetLocation, DockerImageAssetSource, FileAssetLocation, FileAssetSource } from '../assets'; +import { ISynthesisSession } from '../construct-compat'; +import { Stack } from '../stack'; + +/** + * Encodes information how a certain Stack should be deployed + */ +export interface IStackSynthesizer { + /** + * Bind to the stack this environment is going to be used on + * + * Must be called before any of the other methods are called. + */ + bind(stack: Stack): void; + + /** + * Register a File Asset + * + * Returns the parameters that can be used to refer to the asset inside the template. + */ + addFileAsset(asset: FileAssetSource): FileAssetLocation; + + /** + * Register a Docker Image Asset + * + * Returns the parameters that can be used to refer to the asset inside the template. + */ + addDockerImageAsset(asset: DockerImageAssetSource): DockerImageAssetLocation; + + /** + * Synthesize all artifacts required for the stack into the session + * + * @experimental + */ + synthesizeStackArtifacts(session: ISynthesisSession): void; +} diff --git a/packages/@aws-cdk/core/lib/stack.ts b/packages/@aws-cdk/core/lib/stack.ts index cda56f53de5e8..7d5d41fe72feb 100644 --- a/packages/@aws-cdk/core/lib/stack.ts +++ b/packages/@aws-cdk/core/lib/stack.ts @@ -3,10 +3,9 @@ import * as cxapi from '@aws-cdk/cx-api'; import * as fs from 'fs'; import * as path from 'path'; import { DockerImageAssetLocation, DockerImageAssetSource, FileAssetLocation, FileAssetSource } from './assets'; -import { Construct, ConstructNode, IConstruct, ISynthesisSession } from './construct-compat'; +import { Construct, IConstruct, ISynthesisSession } from './construct-compat'; import { ContextProvider } from './context-provider'; import { Environment } from './environment'; -import { FileAssetParameters } from './private/asset-parameters'; import { CLOUDFORMATION_TOKEN_RESOLVER, CloudFormationLang } from './private/cloudformation-lang'; import { LogicalIDs } from './private/logical-id'; import { resolve } from './private/resolve'; @@ -17,21 +16,6 @@ const MY_STACK_CACHE = Symbol.for('@aws-cdk/core.Stack.myStack'); const VALID_STACK_NAME_REGEX = /^[A-Za-z][A-Za-z0-9-]*$/; -/** - * The well-known name for the docker image asset ECR repository. All docker - * image assets will be pushed into this repository with an image tag based on - * the source hash. - */ -const ASSETS_ECR_REPOSITORY_NAME = 'aws-cdk/assets'; - -/** - * This allows users to work around the fact that the ECR repository is - * (currently) not configurable by setting this context key to their desired - * repository name. The CLI will auto-create this ECR repository if it's not - * already created. - */ -const ASSETS_ECR_REPOSITORY_NAME_OVERRIDE_CONTEXT_KEY = 'assets-ecr-repository-name'; - export interface StackProps { /** * A description of the stack. @@ -62,6 +46,14 @@ export interface StackProps { */ readonly tags?: { [key: string]: string }; + /** + * Synthesis method to use while deploying this stack + * + * @default - `DefaultStackSynthesizer` if the `@aws-cdk/core:newStyleStackSynthesis` feature flag + * is set, `LegacyStackSynthesizer` otherwise. + */ + readonly synthesizer?: IStackSynthesizer; + /** * Whether to enable termination protection for this stack. * @@ -214,6 +206,13 @@ export class Stack extends Construct implements ITaggable { */ public readonly artifactId: string; + /** + * Synthesis method for this stack + * + * @experimental + */ + public readonly synthesizer: IStackSynthesizer; + /** * Logical ID generation strategy */ @@ -231,19 +230,8 @@ export class Stack extends Construct implements ITaggable { */ private readonly _missingContext = new Array(); - /** - * Includes all parameters synthesized for assets (lazy). - */ - private _assetParameters?: Construct; - private readonly _stackName: string; - /** - * The image ID of all the docker image assets that were already added to this - * stack (to avoid duplication). - */ - private readonly addedImageAssets = new Set(); - /** * Creates a new stack. * @@ -294,6 +282,11 @@ export class Stack extends Construct implements ITaggable { : this.stackName; this.templateFile = `${this.artifactId}.template.json`; + + this.synthesizer = props.synthesizer ?? (this.node.tryGetContext(cxapi.NEW_STYLE_STACK_SYNTHESIS_CONTEXT) + ? new DefaultStackSynthesizer() + : new LegacyStackSynthesizer()); + this.synthesizer.bind(this); } /** @@ -534,78 +527,24 @@ export class Stack extends Construct implements ITaggable { return value; } + /** + * Register a file asset on this Stack + * + * @deprecated Use `stack.synthesizer.addFileAsset()` if you are calling, + * and a different IDeploymentEnvironment class if you are implementing. + */ public addFileAsset(asset: FileAssetSource): FileAssetLocation { - - // assets are always added at the top-level stack - if (this.nestedStackParent) { - return this.nestedStackParent.addFileAsset(asset); - } - - let params = this.assetParameters.node.tryFindChild(asset.sourceHash) as FileAssetParameters; - if (!params) { - params = new FileAssetParameters(this.assetParameters, asset.sourceHash); - - const metadata: cxschema.FileAssetMetadataEntry = { - path: asset.fileName, - id: asset.sourceHash, - packaging: asset.packaging, - sourceHash: asset.sourceHash, - - s3BucketParameter: params.bucketNameParameter.logicalId, - s3KeyParameter: params.objectKeyParameter.logicalId, - artifactHashParameter: params.artifactHashParameter.logicalId, - }; - - this.node.addMetadata(cxschema.ArtifactMetadataEntryType.ASSET, metadata); - } - - const bucketName = params.bucketNameParameter.valueAsString; - - // key is prefix|postfix - const encodedKey = params.objectKeyParameter.valueAsString; - - const s3Prefix = Fn.select(0, Fn.split(cxapi.ASSET_PREFIX_SEPARATOR, encodedKey)); - const s3Filename = Fn.select(1, Fn.split(cxapi.ASSET_PREFIX_SEPARATOR, encodedKey)); - const objectKey = `${s3Prefix}${s3Filename}`; - - const s3Url = `https://s3.${this.region}.${this.urlSuffix}/${bucketName}/${objectKey}`; - - return { bucketName, objectKey, s3Url }; + return this.synthesizer.addFileAsset(asset); } + /** + * Register a docker image asset on this Stack + * + * @deprecated Use `stack.synthesizer.addDockerImageAsset()` if you are calling, + * and a different `IDeploymentEnvironment` class if you are implementing. + */ public addDockerImageAsset(asset: DockerImageAssetSource): DockerImageAssetLocation { - if (this.nestedStackParent) { - return this.nestedStackParent.addDockerImageAsset(asset); - } - - // check if we have an override from context - const repositoryNameOverride = this.node.tryGetContext(ASSETS_ECR_REPOSITORY_NAME_OVERRIDE_CONTEXT_KEY); - const repositoryName = asset.repositoryName ?? repositoryNameOverride ?? ASSETS_ECR_REPOSITORY_NAME; - const imageTag = asset.sourceHash; - const assetId = asset.sourceHash; - - // only add every image (identified by source hash) once for each stack that uses it. - if (!this.addedImageAssets.has(assetId)) { - const metadata: cxschema.ContainerImageAssetMetadataEntry = { - repositoryName, - imageTag, - id: assetId, - packaging: 'container-image', - path: asset.directoryName, - sourceHash: asset.sourceHash, - buildArgs: asset.dockerBuildArgs, - target: asset.dockerBuildTarget, - file: asset.dockerFile, - }; - - this.node.addMetadata(cxschema.ArtifactMetadataEntryType.ASSET, metadata); - this.addedImageAssets.add(assetId); - } - - return { - imageUri: `${this.account}.dkr.ecr.${this.region}.${this.urlSuffix}/${repositoryName}:${imageTag}`, - repositoryName, - }; + return this.synthesizer.addDockerImageAsset(asset); } /** @@ -758,6 +697,12 @@ export class Stack extends Construct implements ITaggable { } protected synthesize(session: ISynthesisSession): void { + // In principle, stack synthesis is delegated to the + // StackSynthesis object. + // + // However, some parts of synthesis currently use some private + // methods on Stack, and I don't really see the value in refactoring + // this right now, so some parts still happen here. const builder = session.assembly; // write the CloudFormation template as a JSON file @@ -769,46 +714,8 @@ export class Stack extends Construct implements ITaggable { builder.addMissing(ctx); } - // if this is a nested stack, do not emit it as a cloud assembly artifact (it will be registered as an s3 asset instead) - if (this.nested) { - return; - } - - // backwards compatibility since originally artifact ID was always equal to - // stack name the stackName attribute is optional and if it is not specified - // the CLI will use the artifact ID as the stack name. we *could have* - // always put the stack name here but wanted to minimize the risk around - // changes to the assembly manifest. so this means that as long as stack - // name and artifact ID are the same, the cloud assembly manifest will not - // change. - const stackNameProperty = this.stackName === this.artifactId - ? { } - : { stackName: this.stackName }; - - // nested stack tags are applied at the AWS::CloudFormation::Stack resource - // level and are not needed in the cloud assembly. - // TODO: move these to the cloud assembly artifact properties instead of metadata - if (this.tags.hasTags()) { - this.node.addMetadata(cxschema.ArtifactMetadataEntryType.STACK_TAGS, this.tags.renderTags()); - } - - const properties: cxapi.AwsCloudFormationStackProperties = { - templateFile: this.templateFile, - terminationProtection: this.terminationProtection, - ...stackNameProperty, - }; - - const deps = this.dependencies.map(s => s.artifactId); - const meta = this.collectMetadata(); - - // add an artifact that represents this stack - builder.addArtifact(this.artifactId, { - type: cxschema.ArtifactType.AWS_CLOUDFORMATION_STACK, - environment: this.environment, - properties, - dependencies: deps.length > 0 ? deps : undefined, - metadata: Object.keys(meta).length > 0 ? meta : undefined, - }); + // Delegate adding artifacts to the Synthesizer + this.synthesizer.synthesizeStackArtifacts(session); } /** @@ -910,44 +817,6 @@ export class Stack extends Construct implements ITaggable { return undefined; } - private collectMetadata() { - const output: { [id: string]: cxschema.MetadataEntry[] } = { }; - const stack = this; - - visit(this); - - return output; - - function visit(node: IConstruct) { - // break off if we reached a node that is not a child of this stack - const parent = findParentStack(node); - if (parent !== stack) { - return; - } - - if (node.node.metadata.length > 0) { - // Make the path absolute - output[ConstructNode.PATH_SEP + node.node.path] = node.node.metadata.map(md => stack.resolve(md) as cxschema.MetadataEntry); - } - - for (const child of node.node.children) { - visit(child); - } - } - - function findParentStack(node: IConstruct): Stack | undefined { - if (node instanceof Stack && node.nestedStackParent === undefined) { - return node; - } - - if (!node.node.scope) { - return undefined; - } - - return findParentStack(node.node.scope); - } - } - /** * Calculcate the stack name based on the construct path */ @@ -968,13 +837,6 @@ export class Stack extends Construct implements ITaggable { return makeUniqueId(ids); } - - private get assetParameters() { - if (!this._assetParameters) { - this._assetParameters = new Construct(this, 'AssetParameters'); - } - return this._assetParameters; - } } function merge(template: any, part: any) { @@ -1062,6 +924,7 @@ import { addDependency } from './deps'; import { prepareApp } from './private/prepare-app'; import { Reference } from './reference'; import { IResolvable } from './resolvable'; +import { DefaultStackSynthesizer, IStackSynthesizer, LegacyStackSynthesizer } from './stack-synthesizers'; import { ITaggable, TagManager } from './tag-manager'; import { Token } from './token'; diff --git a/packages/@aws-cdk/core/package.json b/packages/@aws-cdk/core/package.json index 66a24307bad97..d388107645c9b 100644 --- a/packages/@aws-cdk/core/package.json +++ b/packages/@aws-cdk/core/package.json @@ -108,7 +108,8 @@ "module-name:@aws-cdk/core", "construct-ctor:@aws-cdk/core.CustomResourceProvider", "construct-interface-extends-iconstruct:@aws-cdk/core.ICustomResourceProvider", - "props-physical-name:@aws-cdk/core.CustomResourceProps" + "props-physical-name:@aws-cdk/core.CustomResourceProps", + "integ-return-type:@aws-cdk/core.IStackSynthesizer.bind" ] }, "scripts": { @@ -164,6 +165,7 @@ "dependencies": { "minimatch": "^3.0.4", "@aws-cdk/cx-api": "0.0.0", + "@aws-cdk/cdk-assets-schema": "0.0.0", "@aws-cdk/cloud-assembly-schema": "0.0.0", "constructs": "^3.0.2" }, @@ -172,8 +174,9 @@ ], "homepage": "https://github.com/aws/aws-cdk", "peerDependencies": { - "@aws-cdk/cx-api": "0.0.0", + "@aws-cdk/cdk-assets-schema": "0.0.0", "@aws-cdk/cloud-assembly-schema": "0.0.0", + "@aws-cdk/cx-api": "0.0.0", "constructs": "^3.0.2" }, "engines": { diff --git a/packages/@aws-cdk/core/test/evaluate-cfn.ts b/packages/@aws-cdk/core/test/evaluate-cfn.ts index 8e36aea9dc0e0..1c5a07dab7b5e 100644 --- a/packages/@aws-cdk/core/test/evaluate-cfn.ts +++ b/packages/@aws-cdk/core/test/evaluate-cfn.ts @@ -25,6 +25,16 @@ export function evaluateCFN(object: any, context: {[key: string]: string} = {}): } return context[key]; }, + + 'Fn::Sub'(argument: string | [string, Record]) { + const template: string = evaluate(Array.isArray(argument) ? argument[0] : argument); + const placeholders: Record = Array.isArray(argument) ? evaluate(argument[1]) : context; + + return template.replace(/\$\{([a-zA-Z0-9.:-]*)\}/g, (_: string, key: string) => { + if (key in placeholders) { return placeholders[key]; } + throw new Error(`Unknown placeholder in Fn::Sub: ${key}`); + }); + }, }; return evaluate(object); diff --git a/packages/@aws-cdk/core/test/stack-synthesis/test.new-style-synthesis.ts b/packages/@aws-cdk/core/test/stack-synthesis/test.new-style-synthesis.ts new file mode 100644 index 0000000000000..7b47858d9a6d7 --- /dev/null +++ b/packages/@aws-cdk/core/test/stack-synthesis/test.new-style-synthesis.ts @@ -0,0 +1,104 @@ +import * as asset_schema from '@aws-cdk/cdk-assets-schema'; +import * as cxapi from '@aws-cdk/cx-api'; +import * as fs from 'fs'; +import { Test } from 'nodeunit'; +import { App, FileAssetPackaging, Stack } from '../../lib'; +import { evaluateCFN } from '../evaluate-cfn'; + +const CFN_CONTEXT = { + 'AWS::Region': 'the_region', + 'AWS::AccountId': 'the_account', + 'AWS::URLSuffix': 'domain.aws', +}; + +let app: App; +let stack: Stack; +export = { + 'setUp'(cb: () => void) { + app = new App({ + context: { + [cxapi.NEW_STYLE_STACK_SYNTHESIS_CONTEXT]: 'true', + }, + }); + stack = new Stack(app, 'Stack'); + cb(); + }, + + 'add file asset'(test: Test) { + // WHEN + const location = stack.synthesizer.addFileAsset({ + fileName: __filename, + packaging: FileAssetPackaging.FILE, + sourceHash: 'abcdef', + }); + + // THEN - we have a fixed asset location with region placeholders + test.equals(evalCFN(location.bucketName), 'cdk-bootstrap-hnb659fds-assets-the_account-the_region'); + test.equals(evalCFN(location.s3Url), 'https://s3.the_region.domain.aws/cdk-bootstrap-hnb659fds-assets-the_account-the_region/abcdef'); + + // THEN - object key contains source hash somewhere + test.ok(location.objectKey.indexOf('abcdef') > -1); + + test.done(); + }, + + 'add docker image asset'(test: Test) { + // WHEN + const location = stack.synthesizer.addDockerImageAsset({ + directoryName: '.', + sourceHash: 'abcdef', + }); + + // THEN - we have a fixed asset location with region placeholders + test.equals(evalCFN(location.repositoryName), 'cdk-bootstrap-hnb659fds-container-assets-the_account-the_region'); + test.equals(evalCFN(location.imageUri), 'the_account.dkr.ecr.the_region.domain.aws/cdk-bootstrap-hnb659fds-container-assets-the_account-the_region:abcdef'); + + test.done(); + }, + + 'synthesis'(test: Test) { + // GIVEN + stack.synthesizer.addFileAsset({ + fileName: __filename, + packaging: FileAssetPackaging.FILE, + sourceHash: 'abcdef', + }); + stack.synthesizer.addDockerImageAsset({ + directoryName: '.', + sourceHash: 'abcdef', + }); + + // WHEN + const asm = app.synth(); + + // THEN - we have an asset manifest with both assets and the stack template in there + const manifestArtifact = asm.artifacts.filter(isAssetManifest)[0]; + test.ok(manifestArtifact); + const manifest: asset_schema.ManifestFile = JSON.parse(fs.readFileSync(manifestArtifact.file, { encoding: 'utf-8' })); + + test.equals(Object.keys(manifest.files || {}).length, 2); + test.equals(Object.keys(manifest.dockerImages || {}).length, 1); + + // THEN - every artifact has an assumeRoleArn + for (const file of Object.values({...manifest.files, ...manifest.dockerImages})) { + for (const destination of Object.values(file.destinations)) { + test.ok(destination.assumeRoleArn); + } + } + + test.done(); + }, +}; + +/** + * Evaluate a possibly string-containing value the same way CFN would do + * + * (Be invariant to the specific Fn::Sub or Fn::Join we would output) + */ +function evalCFN(value: any) { + return evaluateCFN(stack.resolve(value), CFN_CONTEXT); +} + +function isAssetManifest(x: cxapi.CloudArtifact): x is cxapi.AssetManifestArtifact { + return x instanceof cxapi.AssetManifestArtifact; +} \ No newline at end of file diff --git a/packages/@aws-cdk/core/test/test.app.ts b/packages/@aws-cdk/core/test/test.app.ts index 04ea5b47492c3..af7a301f5b95b 100644 --- a/packages/@aws-cdk/core/test/test.app.ts +++ b/packages/@aws-cdk/core/test/test.app.ts @@ -293,6 +293,7 @@ export = { test.deepEqual(libs, { '@aws-cdk/core': version, '@aws-cdk/cx-api': version, + '@aws-cdk/cdk-assets-schema': version, '@aws-cdk/cloud-assembly-schema': version, 'jsii-runtime': `node.js/${process.version}`, }); diff --git a/packages/@aws-cdk/core/test/test.runtime-info.ts b/packages/@aws-cdk/core/test/test.runtime-info.ts index fe0752f90b931..9059457799c15 100644 --- a/packages/@aws-cdk/core/test/test.runtime-info.ts +++ b/packages/@aws-cdk/core/test/test.runtime-info.ts @@ -26,6 +26,7 @@ export = { '@aws-cdk/core': version, '@aws-cdk/cx-api': version, '@aws-cdk/cloud-assembly-schema': version, + '@aws-cdk/cdk-assets-schema': version, '@aws-solutions-konstruk/foo': mockVersion, 'jsii-runtime': `node.js/${process.version}`, }); diff --git a/packages/@aws-cdk/cx-api/lib/asset-manifest-artifact.ts b/packages/@aws-cdk/cx-api/lib/asset-manifest-artifact.ts new file mode 100644 index 0000000000000..8146a276d7e4f --- /dev/null +++ b/packages/@aws-cdk/cx-api/lib/asset-manifest-artifact.ts @@ -0,0 +1,30 @@ +import * as cxschema from '@aws-cdk/cloud-assembly-schema'; +import * as path from 'path'; +import { CloudArtifact } from './cloud-artifact'; +import { CloudAssembly } from './cloud-assembly'; + +/** + * Asset manifest is a description of a set of assets which need to be built and published + */ +export class AssetManifestArtifact extends CloudArtifact { + /** + * The file name of the asset manifest + */ + public readonly file: string; + + /** + * Version of bootstrap stack required to deploy this stack + */ + public readonly requiresBootstrapStackVersion: number; + + constructor(assembly: CloudAssembly, name: string, artifact: cxschema.ArtifactManifest) { + super(assembly, name, artifact); + + const properties = (this.manifest.properties || {}) as cxschema.AssetManifestProperties; + if (!properties.file) { + throw new Error('Invalid AssetManifestArtifact. Missing "file" property'); + } + this.file = path.resolve(this.assembly.directory, properties.file); + this.requiresBootstrapStackVersion = properties.requiresBootstrapStackVersion ?? 1; + } +} diff --git a/packages/@aws-cdk/cx-api/lib/cloud-artifact.ts b/packages/@aws-cdk/cx-api/lib/cloud-artifact.ts index d513743989a74..55cd7567e1612 100644 --- a/packages/@aws-cdk/cx-api/lib/cloud-artifact.ts +++ b/packages/@aws-cdk/cx-api/lib/cloud-artifact.ts @@ -1,9 +1,6 @@ import * as cxschema from '@aws-cdk/cloud-assembly-schema'; import { CloudAssembly } from './cloud-assembly'; -import { - MetadataEntryResult, - SynthesisMessage, - SynthesisMessageLevel } from './metadata'; +import { MetadataEntryResult, SynthesisMessage, SynthesisMessageLevel } from './metadata'; /** * Artifact properties for CloudFormation stacks. @@ -50,6 +47,8 @@ export class CloudArtifact { return new CloudFormationStackArtifact(assembly, id, artifact); case cxschema.ArtifactType.CDK_TREE: return new TreeCloudArtifact(assembly, id, artifact); + case cxschema.ArtifactType.ASSET_MANIFEST: + return new AssetManifestArtifact(assembly, id, artifact); default: return undefined; } @@ -144,5 +143,6 @@ export class CloudArtifact { } // needs to be defined at the end to avoid a cyclic dependency +import { AssetManifestArtifact } from './asset-manifest-artifact'; import { CloudFormationStackArtifact } from './cloudformation-artifact'; -import { TreeCloudArtifact } from './tree-cloud-artifact'; +import { TreeCloudArtifact } from './tree-cloud-artifact'; \ No newline at end of file diff --git a/packages/@aws-cdk/cx-api/lib/cloudformation-artifact.ts b/packages/@aws-cdk/cx-api/lib/cloudformation-artifact.ts index b8b9c0ead664c..2373e45e0eabc 100644 --- a/packages/@aws-cdk/cx-api/lib/cloudformation-artifact.ts +++ b/packages/@aws-cdk/cx-api/lib/cloudformation-artifact.ts @@ -1,7 +1,7 @@ import * as cxschema from '@aws-cdk/cloud-assembly-schema'; import * as fs from 'fs'; import * as path from 'path'; -import { AwsCloudFormationStackProperties, CloudArtifact } from './cloud-artifact'; +import { CloudArtifact } from './cloud-artifact'; import { CloudAssembly } from './cloud-assembly'; import { Environment, EnvironmentUtils } from './environment'; @@ -54,6 +54,34 @@ export class CloudFormationStackArtifact extends CloudArtifact { */ public readonly environment: Environment; + /** + * The role that needs to be assumed to deploy the stack + * + * @default - No role is assumed (current credentials are used) + */ + public readonly assumeRoleArn?: string; + + /** + * The role that is passed to CloudFormation to execute the change set + * + * @default - No role is passed (currently assumed role/credentials are used) + */ + public readonly cloudFormationExecutionRoleArn?: string; + + /** + * If the stack template has already been included in the asset manifest, its asset URL + * + * @default - Not uploaded yet, upload just before deploying + */ + public readonly stackTemplateAssetObjectUrl?: string; + + /** + * Version of bootstrap stack required to deploy this stack + * + * @default - No bootstrap stack required + */ + public readonly requiresBootstrapStackVersion?: number; + /** * Whether termination protection is enabled for this stack. */ @@ -62,16 +90,20 @@ export class CloudFormationStackArtifact extends CloudArtifact { constructor(assembly: CloudAssembly, artifactId: string, artifact: cxschema.ArtifactManifest) { super(assembly, artifactId, artifact); - if (!artifact.properties || !artifact.properties.templateFile) { + const properties = (this.manifest.properties || {}) as cxschema.AwsCloudFormationStackProperties; + if (!properties.templateFile) { throw new Error('Invalid CloudFormation stack artifact. Missing "templateFile" property in cloud assembly manifest'); } if (!artifact.environment) { throw new Error('Invalid CloudFormation stack artifact. Missing environment'); } this.environment = EnvironmentUtils.parse(artifact.environment); - const properties = (this.manifest.properties || {}) as AwsCloudFormationStackProperties; this.templateFile = properties.templateFile; this.parameters = properties.parameters || { }; + this.assumeRoleArn = properties.assumeRoleArn; + this.cloudFormationExecutionRoleArn = properties.cloudFormationExecutionRoleArn; + this.stackTemplateAssetObjectUrl = properties.stackTemplateAssetObjectUrl; + this.requiresBootstrapStackVersion = properties.requiresBootstrapStackVersion; this.terminationProtection = properties.terminationProtection; this.stackName = properties.stackName || artifactId; diff --git a/packages/@aws-cdk/cx-api/lib/features.ts b/packages/@aws-cdk/cx-api/lib/features.ts index ca146e6d54522..cc3f94f91e23d 100644 --- a/packages/@aws-cdk/cx-api/lib/features.ts +++ b/packages/@aws-cdk/cx-api/lib/features.ts @@ -29,6 +29,11 @@ export const ENABLE_DIFF_NO_FAIL_CONTEXT = 'aws-cdk:enableDiffNoFail'; /** @deprecated use `ENABLE_DIFF_NO_FAIL_CONTEXT` */ export const ENABLE_DIFF_NO_FAIL = ENABLE_DIFF_NO_FAIL_CONTEXT; +/** + * Switch to new stack synthesis method which enable CI/CD + */ +export const NEW_STYLE_STACK_SYNTHESIS_CONTEXT = '@aws-cdk/core:newStyleStackSynthesis'; + /** * This map includes context keys and values for feature flags that enable * capabilities "from the future", which we could not introduce as the default @@ -45,4 +50,7 @@ export const ENABLE_DIFF_NO_FAIL = ENABLE_DIFF_NO_FAIL_CONTEXT; export const FUTURE_FLAGS = { [ENABLE_STACK_NAME_DUPLICATES_CONTEXT]: 'true', [ENABLE_DIFF_NO_FAIL_CONTEXT]: 'true', + + // We will advertise this flag when the feature is complete + // [NEW_STYLE_STACK_SYNTHESIS]: 'true', }; diff --git a/packages/@aws-cdk/cx-api/lib/index.ts b/packages/@aws-cdk/cx-api/lib/index.ts index cb4079a02d6a6..507af99d8ba63 100644 --- a/packages/@aws-cdk/cx-api/lib/index.ts +++ b/packages/@aws-cdk/cx-api/lib/index.ts @@ -3,6 +3,7 @@ export * from './context/vpc'; export * from './context/ami'; export * from './context/availability-zones'; export * from './cloud-artifact'; +export * from './asset-manifest-artifact'; export * from './cloudformation-artifact'; export * from './tree-cloud-artifact'; export * from './cloud-assembly'; @@ -10,4 +11,5 @@ export * from './assets'; export * from './environment'; export * from './metadata'; export * from './features'; +export * from './placeholders'; export * from './app'; diff --git a/packages/@aws-cdk/cx-api/lib/placeholders.ts b/packages/@aws-cdk/cx-api/lib/placeholders.ts new file mode 100644 index 0000000000000..e32c02a891091 --- /dev/null +++ b/packages/@aws-cdk/cx-api/lib/placeholders.ts @@ -0,0 +1,123 @@ +/** + * Placeholders which can be used manifests + * + * These can occur both in the Asset Manifest as well as the general + * Cloud Assembly manifest. + */ +export class EnvironmentPlaceholders { + /** + * Insert this into the destination fields to be replaced with the current region + */ + public static readonly CURRENT_REGION = '${AWS::Region}'; + + /** + * Insert this into the destination fields to be replaced with the current account + */ + public static readonly CURRENT_ACCOUNT = '${AWS::AccountId}'; + + /** + * Insert this into the destination fields to be replaced with the current partition + */ + public static readonly CURRENT_PARTITION = '${AWS::Partition}'; + + /** + * Replace the environment placeholders in all strings found in a complex object. + * + * Duplicated between cdk-assets and aws-cdk CLI because we don't have a good single place to put it + * (they're nominally independent tools). + */ + public static replace(object: any, values: EnvironmentPlaceholderValues): any { + return this.recurse(object, value => { + value = replaceAll(value, EnvironmentPlaceholders.CURRENT_REGION, values.region); + value = replaceAll(value, EnvironmentPlaceholders.CURRENT_ACCOUNT, values.accountId); + value = replaceAll(value, EnvironmentPlaceholders.CURRENT_PARTITION, values.partition); + return value; + }); + } + + /** + * Like 'replace', but asynchronous + */ + public static async replaceAsync(object: any, provider: IEnvironmentPlaceholderProvider): Promise { + let needRegion = false; + let needAccountId = false; + let needPartition = false; + + this.recurse(object, value => { + if (value.indexOf(EnvironmentPlaceholders.CURRENT_REGION) > 1) { needRegion = true; } + if (value.indexOf(EnvironmentPlaceholders.CURRENT_ACCOUNT) > 1) { needAccountId = true; } + if (value.indexOf(EnvironmentPlaceholders.CURRENT_PARTITION) > 1) { needPartition = true; } + return value; + }); + + const region = needRegion ? await provider.region() : undefined; + const accountId = needAccountId ? await provider.accountId() : undefined; + const partition = needPartition ? await provider.partition() : undefined; + + return this.recurse(object, value => { + value = replaceAll(value, EnvironmentPlaceholders.CURRENT_REGION, region ?? 'WONTHAPPEN'); + value = replaceAll(value, EnvironmentPlaceholders.CURRENT_ACCOUNT, accountId ?? 'WONTHAPPEN'); + value = replaceAll(value, EnvironmentPlaceholders.CURRENT_PARTITION, partition ?? 'WONTHAPPEN'); + return value; + }); + } + + private static recurse(value: any, cb: (x: string) => string): any { + if (typeof value === 'string') { return cb(value); } + if (typeof value !== 'object' || value === null) { return value; } + if (Array.isArray(value)) { return value.map(x => this.recurse(x, cb)); } + + const ret: Record = {}; + for (const [key, inner] of Object.entries(value)) { + ret[key] = this.recurse(inner, cb); + } + return ret; + } +} + +/** + * Return the appropriate values for the environment placeholders + */ +export interface EnvironmentPlaceholderValues { + /** + * Return the region + */ + readonly region: string; + + /** + * Return the account + */ + readonly accountId: string; + + /** + * Return the partition + */ + readonly partition: string; +} + +/** + * Return the appropriate values for the environment placeholders + */ +export interface IEnvironmentPlaceholderProvider { + /** + * Return the region + */ + region(): Promise; + + /** + * Return the account + */ + accountId(): Promise; + + /** + * Return the partition + */ + partition(): Promise; +} + +/** + * A "replace-all" function that doesn't require us escaping a literal string to a regex + */ +function replaceAll(s: string, search: string, replace: string) { + return s.split(search).join(replace); +} diff --git a/packages/@aws-cdk/cx-api/lib/tree-cloud-artifact.ts b/packages/@aws-cdk/cx-api/lib/tree-cloud-artifact.ts index b31bc6d22fe1b..142671e882e23 100644 --- a/packages/@aws-cdk/cx-api/lib/tree-cloud-artifact.ts +++ b/packages/@aws-cdk/cx-api/lib/tree-cloud-artifact.ts @@ -8,7 +8,7 @@ export class TreeCloudArtifact extends CloudArtifact { constructor(assembly: CloudAssembly, name: string, artifact: cxschema.ArtifactManifest) { super(assembly, name, artifact); - const properties = (this.manifest.properties || {}); + const properties = (this.manifest.properties || {}) as cxschema.TreeArtifactProperties; if (!properties.file) { throw new Error('Invalid TreeCloudArtifact. Missing "file" property'); } diff --git a/packages/@aws-cdk/cx-api/test/cloud-assembly.test.ts b/packages/@aws-cdk/cx-api/test/cloud-assembly.test.ts index d917217ecfd74..42d7dffd94233 100644 --- a/packages/@aws-cdk/cx-api/test/cloud-assembly.test.ts +++ b/packages/@aws-cdk/cx-api/test/cloud-assembly.test.ts @@ -44,7 +44,7 @@ test('assembly with invalid tree metadata', () => { }); test('assembly with tree metadata having no file property specified', () => { - expect(() => new CloudAssembly(path.join(FIXTURES, 'tree-no-file-property'))).toThrow(/Invalid TreeCloudArtifact/); + expect(() => new CloudAssembly(path.join(FIXTURES, 'tree-no-file-property'))).toThrow(/Invalid assembly manifest/); }); test('assembly with cloudformation artifact having no environment property specified', () => { @@ -144,3 +144,9 @@ test('displayName shows both artifact ID and stack name if needed', () => { expect(art1.id).toBe('MyStackName'); expect(art1.stackName).toBe('MyStackName'); }); + +test('can read assembly with asset manifest', () => { + const assembly = new CloudAssembly(path.join(FIXTURES, 'asset-manifest')); + expect(assembly.stacks).toHaveLength(1); + expect(assembly.artifacts).toHaveLength(2); +}); diff --git a/packages/@aws-cdk/cx-api/test/fixtures/asset-manifest/asset-dir/foo.txt b/packages/@aws-cdk/cx-api/test/fixtures/asset-manifest/asset-dir/foo.txt new file mode 100644 index 0000000000000..5783cb7e31483 --- /dev/null +++ b/packages/@aws-cdk/cx-api/test/fixtures/asset-manifest/asset-dir/foo.txt @@ -0,0 +1 @@ +hello, assets! diff --git a/packages/@aws-cdk/cx-api/test/fixtures/asset-manifest/assets.json b/packages/@aws-cdk/cx-api/test/fixtures/asset-manifest/assets.json new file mode 100644 index 0000000000000..eced01a4b1814 --- /dev/null +++ b/packages/@aws-cdk/cx-api/test/fixtures/asset-manifest/assets.json @@ -0,0 +1,3 @@ +{ + "$comment": "Empty on purpose for now" +} diff --git a/packages/@aws-cdk/cx-api/test/fixtures/asset-manifest/docker-asset/Dockerfile b/packages/@aws-cdk/cx-api/test/fixtures/asset-manifest/docker-asset/Dockerfile new file mode 100644 index 0000000000000..ceaf18ac05257 --- /dev/null +++ b/packages/@aws-cdk/cx-api/test/fixtures/asset-manifest/docker-asset/Dockerfile @@ -0,0 +1 @@ +FROM ubuntu diff --git a/packages/@aws-cdk/cx-api/test/fixtures/asset-manifest/manifest.json b/packages/@aws-cdk/cx-api/test/fixtures/asset-manifest/manifest.json new file mode 100644 index 0000000000000..fa63832eea44d --- /dev/null +++ b/packages/@aws-cdk/cx-api/test/fixtures/asset-manifest/manifest.json @@ -0,0 +1,21 @@ +{ + "version": "0.0.0", + "artifacts": { + "MyStackName": { + "type": "aws:cloudformation:stack", + "environment": "aws://37736633/us-region-1", + "properties": { + "templateFile": "template.json" + }, + "dependencies": ["AssetManifest"], + "metadata": { + } + }, + "AssetManifest": { + "type": "cdk:asset-manifest", + "properties": { + "file": "asset.json" + } + } + } +} diff --git a/packages/@aws-cdk/cx-api/test/fixtures/asset-manifest/template.json b/packages/@aws-cdk/cx-api/test/fixtures/asset-manifest/template.json new file mode 100644 index 0000000000000..284fd64cffc21 --- /dev/null +++ b/packages/@aws-cdk/cx-api/test/fixtures/asset-manifest/template.json @@ -0,0 +1,7 @@ +{ + "Resources": { + "MyBucket": { + "Type": "AWS::S3::Bucket" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/cx-api/test/placeholders.test.ts b/packages/@aws-cdk/cx-api/test/placeholders.test.ts new file mode 100644 index 0000000000000..9b39478abd611 --- /dev/null +++ b/packages/@aws-cdk/cx-api/test/placeholders.test.ts @@ -0,0 +1,27 @@ +import { EnvironmentPlaceholders, IEnvironmentPlaceholderProvider } from '../lib'; + +test('complex placeholder substitution', async () => { + const replacer: IEnvironmentPlaceholderProvider = { + accountId: () => Promise.resolve('current_account'), + region: () => Promise.resolve('current_region'), + partition: () => Promise.resolve('current_partition'), + }; + + expect(await EnvironmentPlaceholders.replaceAsync({ + destinations: { + theDestination: { + assumeRoleArn: 'arn:${AWS::Partition}:role-${AWS::AccountId}', + bucketName: 'some_bucket-${AWS::AccountId}-${AWS::Region}', + objectKey: 'some_key-${AWS::AccountId}-${AWS::Region}', + }, + }, + }, replacer)).toEqual({ + destinations: { + theDestination: { + assumeRoleArn: 'arn:current_partition:role-current_account', + bucketName: 'some_bucket-current_account-current_region', + objectKey: 'some_key-current_account-current_region', + }, + }, + }); +}); diff --git a/packages/aws-cdk/lib/api/cloudformation-deployments.ts b/packages/aws-cdk/lib/api/cloudformation-deployments.ts index d41e128793f97..0ee09812629c9 100644 --- a/packages/aws-cdk/lib/api/cloudformation-deployments.ts +++ b/packages/aws-cdk/lib/api/cloudformation-deployments.ts @@ -1,6 +1,8 @@ -import { CloudFormationStackArtifact } from '@aws-cdk/cx-api'; +import * as cxapi from '@aws-cdk/cx-api'; +import { AssetManifest } from 'cdk-assets'; import { Tag } from '../cdk-toolkit'; import { debug } from '../logging'; +import { publishAssets } from '../util/asset-publishing'; import { Mode, SdkProvider } from './aws-auth'; import { deployStack, DeployStackResult, destroyStack } from './deploy-stack'; import { ToolkitInfo } from './toolkit-info'; @@ -10,7 +12,7 @@ export interface DeployStackOptions { /** * Stack to deploy */ - stack: CloudFormationStackArtifact; + stack: cxapi.CloudFormationStackArtifact; /** * Execution role for the deployment (pass through to CloudFormation) @@ -89,7 +91,7 @@ export interface DeployStackOptions { } export interface DestroyStackOptions { - stack: CloudFormationStackArtifact; + stack: cxapi.CloudFormationStackArtifact; deployName?: string; roleArn?: string; quiet?: boolean; @@ -97,7 +99,7 @@ export interface DestroyStackOptions { } export interface StackExistsOptions { - stack: CloudFormationStackArtifact; + stack: cxapi.CloudFormationStackArtifact; deployName?: string; } @@ -118,7 +120,7 @@ export class CloudFormationDeployments { this.sdkProvider = props.sdkProvider; } - public async readCurrentTemplate(stackArtifact: CloudFormationStackArtifact): Promise